From 751b8a765e1f195bd43cf9908b090f224922f8bd Mon Sep 17 00:00:00 2001 From: Marius Mather Date: Wed, 4 Nov 2026 09:52:34 +1100 Subject: [PATCH] fix: delete linked migration requests when hard deleting users --- db/models.py | 2 +- tests/db/test_models.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/db/models.py b/db/models.py index c242bb34..2c8daec9 100644 --- a/db/models.py +++ b/db/models.py @@ -297,7 +297,7 @@ def hard_delete(self, session: Session, auth0_client: Auth0Client) -> None: except HTTPStatusError as e: logger.warning(f"Error deleting user {user_id} from Auth0: {e}") - for model in (EmailChangeOtp, GroupMembershipHistory, PlatformMembershipHistory, GroupMembership, PlatformMembership, BiocommonsUserHistory): + for model in (AccountMigrationRequest, EmailChangeOtp, GroupMembershipHistory, PlatformMembershipHistory, GroupMembership, PlatformMembership, BiocommonsUserHistory): session.exec(delete(model).where(model.user_id == user_id)) session.exec(delete(BiocommonsUser).where(BiocommonsUser.id == user_id)) session.commit() diff --git a/tests/db/test_models.py b/tests/db/test_models.py index 8e994df7..af44d449 100644 --- a/tests/db/test_models.py +++ b/tests/db/test_models.py @@ -11,6 +11,7 @@ from sqlmodel import select from db.models import ( + AccountMigrationRequest, ApprovalStatusEnum, Auth0Role, BiocommonsGroup, @@ -24,6 +25,7 @@ PlatformMembership, PlatformMembershipHistory, ) +from schemas.aaf_migration import MigrationDirection, MigrationStatus from schemas.biocommons import BiocommonsUserAccountType from tests.biocommons.datagen import RoleDataFactory from tests.datagen import ( @@ -1711,6 +1713,41 @@ def test_hard_delete_removes_user_and_related_rows(test_db_session, mock_auth0_c assert test_db_session.get(EmailChangeOtp, otp_id) is None +@pytest.mark.parametrize("is_deleted", [False, True]) +@pytest.mark.parametrize("direction", list(MigrationDirection)) +def test_hard_delete_removes_account_migration_requests( + test_db_session, mock_auth0_client, persistent_factories, is_deleted, direction +): + test_db_session.connection().exec_driver_sql("PRAGMA foreign_keys=ON") + user = BiocommonsUserFactory.create_sync(is_deleted=is_deleted, deleted_by_id=None) + other_user = BiocommonsUserFactory.create_sync(deleted_by_id=None) + test_db_session.commit() + user_id = user.id + requests = [ + AccountMigrationRequest( + user_id=owner.id, + new_email="new@example.edu.au", + direction=direction, + status=status, + expires_at=datetime.now(timezone.utc) + timedelta(minutes=10), + ) + for owner in (user, other_user) + for status in MigrationStatus + ] + test_db_session.add_all(requests) + test_db_session.commit() + other_request_ids = {request.id for request in requests if request.user_id == other_user.id} + + user.hard_delete(test_db_session, auth0_client=mock_auth0_client) + + test_db_session.expire_all() + assert BiocommonsUser.get_by_id(user_id, test_db_session, include_deleted=True) is None + remaining_requests = test_db_session.exec(select(AccountMigrationRequest)).all() + assert {request.id for request in remaining_requests} == other_request_ids + assert test_db_session.get(BiocommonsUser, other_user.id) is not None + mock_auth0_client.delete_user.assert_called_once_with(user_id=user_id) + + def test_hard_delete_refuses_if_user_acted_as_admin_for_another_user(test_db_session, mock_auth0_client, persistent_factories): admin = BiocommonsUserFactory.create_sync() other_user = BiocommonsUserFactory.create_sync()