diff --git a/wger/core/tests/test_delete_user.py b/wger/core/tests/test_delete_user.py index 17c232743..86fa78e79 100644 --- a/wger/core/tests/test_delete_user.py +++ b/wger/core/tests/test_delete_user.py @@ -21,6 +21,11 @@ # wger from wger.core.tests.base_testcase import WgerTestCase +from django.utils import timezone +from wger.trophies.services.trophy import TrophyService +from wger.trophies.services.statistics import UserStatisticsService +from wger.trophies.models import Trophy, UserStatistics, UserTrophy +from wger.manager.models import WorkoutSession logger = logging.getLogger(__name__) @@ -196,3 +201,40 @@ def test_delete_user_anonymous(self): Tests deleting the user account as an anonymous user """ self.delete_user(fail=True) +class UserDeleteTrophyIntegrationTestCase(WgerTestCase): + """ + Tests user deletion with trophy records and service invocation + """ + def test_delete_user_with_trophy_records(self): + """ + Adds sessions/records, calls trophy system, ensures delete works without IntegrityError + """ + logger.info("Testing user deletion after trophy system invocation") + user = User.objects.create_user( + username="trophyuser", + email="trophy@test.com", + password="testpass" + ) + session = WorkoutSession.objects.create( + user=user, + date=timezone.now().date() + ) + logger.info("Created WorkoutSession") + trophy = Trophy.objects.create( + name="DeleteTestTrophy", + trophy_type=0, + checker_class="workout_count_based", + checker_params={"count": 1}, + is_active=True + ) + UserStatistics.objects.update_or_create( + user=user, + defaults={"total_workouts": 1} + ) + logger.info("Created Trophy and updated UserStatistics") + UserStatisticsService.update_statistics(user) + TrophyService.evaluate_all_trophies(user) + logger.info("Trophy services invoked") + user.delete() + logger.info("User deleted") + self.assertEqual(User.objects.filter(username="trophyuser").count(), 0) diff --git a/wger/trophies/services/statistics.py b/wger/trophies/services/statistics.py index 2128953c7..b9238987b 100644 --- a/wger/trophies/services/statistics.py +++ b/wger/trophies/services/statistics.py @@ -57,6 +57,8 @@ def get_or_create_statistics(cls, user: User) -> UserStatistics: Returns: The UserStatistics instance """ + if not User.objects.filter(pk=user.pk).exists(): + raise User.DoesNotExist('User not found') stats, created = UserStatistics.objects.get_or_create(user=user) return stats diff --git a/wger/trophies/signals.py b/wger/trophies/signals.py index 55aafe330..ec0ea52e4 100644 --- a/wger/trophies/signals.py +++ b/wger/trophies/signals.py @@ -26,6 +26,7 @@ # Django from django.conf import settings +from django.contrib.auth.models import User from django.db.models.signals import ( post_delete, post_save, @@ -112,6 +113,8 @@ def workout_log_saved(sender, instance: WorkoutLog, created: bool, **kwargs): # Trigger trophy evaluation _trigger_trophy_evaluation(instance.user_id) + except User.DoesNotExist: + pass except Exception as e: logger.error(f'Error updating statistics for user {instance.user_id}: {e}', exc_info=True) @@ -128,6 +131,8 @@ def workout_log_deleted(sender, instance: WorkoutLog, **kwargs): try: UserStatisticsService.handle_workout_deletion(instance.user) + except User.DoesNotExist: + pass except Exception as e: logger.error( f'Error updating statistics after deletion for user {instance.user_id}: {e}', @@ -163,6 +168,8 @@ def workout_session_saved(sender, instance: WorkoutSession, created: bool, **kwa # Trigger trophy evaluation _trigger_trophy_evaluation(instance.user_id) + except User.DoesNotExist: + pass except Exception as e: logger.error(f'Error updating statistics for session {instance.id}: {e}', exc_info=True) @@ -179,6 +186,8 @@ def workout_session_deleted(sender, instance: WorkoutSession, **kwargs): try: UserStatisticsService.handle_workout_deletion(instance.user) + except User.DoesNotExist: + pass except Exception as e: logger.error( f'Error updating statistics after session deletion for user {instance.user_id}: {e}', diff --git a/wger/trophies/tests/test_services.py b/wger/trophies/tests/test_services.py index 038ef06a5..e6679d347 100644 --- a/wger/trophies/tests/test_services.py +++ b/wger/trophies/tests/test_services.py @@ -106,6 +106,14 @@ def test_handle_workout_deletion(self): self.assertEqual(stats.total_weight_lifted, Decimal('0')) + def test_get_or_create_statistics_deleted_user(self): + """ + Test get_or_create raises DoesNotExist for deleted user + """ + with patch.object(User.objects, 'filter') as mock_filter: + mock_filter.return_value.exists.return_value = False + with self.assertRaises(User.DoesNotExist): + UserStatisticsService.get_or_create_statistics(self.user) class TrophyServiceTestCase(WgerTestCase): """ Test the TrophyService