diff --git a/src/backend/core/management/commands/merge_duplicate_users.py b/src/backend/core/management/commands/merge_duplicate_users.py index cc7750d4..a61a13f1 100644 --- a/src/backend/core/management/commands/merge_duplicate_users.py +++ b/src/backend/core/management/commands/merge_duplicate_users.py @@ -36,17 +36,28 @@ class Command(BaseCommand): action="store_true", help="Simulate the merge without writing any changes to the database.", ) + parser.add_argument( + "--email-filter", + type=str, + default=None, + help="Only merge users whose email contains this string (e.g. '@example.com').", + ) def handle(self, *args, **options): """Execute the management command.""" dry_run = options["dry_run"] + email_filter = options["email_filter"] if dry_run: self.stdout.write("[DRY-RUN] No changes will be written.\n") + users_qs = User.objects.all() + if email_filter: + users_qs = users_qs.filter(email__icontains=email_filter) + self.stdout.write(f"[INFO] Filtering emails containing '{email_filter}'.\n") + duplicate_emails = ( - User.objects.all() - .exclude(email__isnull=True) + users_qs.exclude(email__isnull=True) .exclude(email="") .values("email") .annotate(cnt=Count("id")) diff --git a/src/backend/core/tests/management/test_management_merge_duplicate_users.py b/src/backend/core/tests/management/test_management_merge_duplicate_users.py index fe1d6a6d..d29767d7 100644 --- a/src/backend/core/tests/management/test_management_merge_duplicate_users.py +++ b/src/backend/core/tests/management/test_management_merge_duplicate_users.py @@ -427,3 +427,34 @@ def test_merge_is_atomic_rolls_back_all_on_any_failure(mock_reassign_files): for f in files: f.refresh_from_db() assert f.creator == user1 + + +# ── Email filter ─────────────────────────────────────────────────────────────── + + +def test_merge_email_filter_only_merges_matching_emails(): + """Command should only merge users whose email matches the filter.""" + UserFactory(email="user1@example.com") + UserFactory(email="user1@example.com") + other1 = UserFactory(email="user1@other.com") + other2 = UserFactory(email="user1@other.com") + call_command("merge_duplicate_users", email_filter="@example.com") + assert User.objects.filter(email="user1@example.com").count() == 1 + assert User.objects.filter(id=other1.id).exists() + assert User.objects.filter(id=other2.id).exists() + + +def test_merge_email_filter_no_match_does_nothing(): + """Command should do nothing when the email filter matches no users.""" + UserFactory(email="user1@example.com") + UserFactory(email="user1@example.com") + call_command("merge_duplicate_users", email_filter="@nomatch.com") + assert User.objects.filter(email="user1@example.com").count() == 2 + + +def test_merge_email_filter_is_case_insensitive(): + """Command should match emails case-insensitively when filtering.""" + UserFactory(email="user1@Example.com") + UserFactory(email="user1@Example.com") + call_command("merge_duplicate_users", email_filter="@example.com") + assert User.objects.filter(email="user1@Example.com").count() == 1