Skip to content

Commit 22c64de

Browse files
authored
Merge pull request #2233 from infektyd/fix-user-delete-integrityerror-2232
Fix: Prevent IntegrityError on user deletion by handling missing user
2 parents 1735d1e + d124ded commit 22c64de

File tree

4 files changed

+61
-0
lines changed

4 files changed

+61
-0
lines changed

wger/core/tests/test_delete_user.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121

2222
# wger
2323
from wger.core.tests.base_testcase import WgerTestCase
24+
from django.utils import timezone
25+
from wger.trophies.services.trophy import TrophyService
26+
from wger.trophies.services.statistics import UserStatisticsService
27+
from wger.trophies.models import Trophy, UserStatistics, UserTrophy
28+
from wger.manager.models import WorkoutSession
2429

2530

2631
logger = logging.getLogger(__name__)
@@ -196,3 +201,40 @@ def test_delete_user_anonymous(self):
196201
Tests deleting the user account as an anonymous user
197202
"""
198203
self.delete_user(fail=True)
204+
class UserDeleteTrophyIntegrationTestCase(WgerTestCase):
205+
"""
206+
Tests user deletion with trophy records and service invocation
207+
"""
208+
def test_delete_user_with_trophy_records(self):
209+
"""
210+
Adds sessions/records, calls trophy system, ensures delete works without IntegrityError
211+
"""
212+
logger.info("Testing user deletion after trophy system invocation")
213+
user = User.objects.create_user(
214+
username="trophyuser",
215+
email="trophy@test.com",
216+
password="testpass"
217+
)
218+
session = WorkoutSession.objects.create(
219+
user=user,
220+
date=timezone.now().date()
221+
)
222+
logger.info("Created WorkoutSession")
223+
trophy = Trophy.objects.create(
224+
name="DeleteTestTrophy",
225+
trophy_type=0,
226+
checker_class="workout_count_based",
227+
checker_params={"count": 1},
228+
is_active=True
229+
)
230+
UserStatistics.objects.update_or_create(
231+
user=user,
232+
defaults={"total_workouts": 1}
233+
)
234+
logger.info("Created Trophy and updated UserStatistics")
235+
UserStatisticsService.update_statistics(user)
236+
TrophyService.evaluate_all_trophies(user)
237+
logger.info("Trophy services invoked")
238+
user.delete()
239+
logger.info("User deleted")
240+
self.assertEqual(User.objects.filter(username="trophyuser").count(), 0)

wger/trophies/services/statistics.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ def get_or_create_statistics(cls, user: User) -> UserStatistics:
5757
Returns:
5858
The UserStatistics instance
5959
"""
60+
if not User.objects.filter(pk=user.pk).exists():
61+
raise User.DoesNotExist('User not found')
6062
stats, created = UserStatistics.objects.get_or_create(user=user)
6163
return stats
6264

wger/trophies/signals.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
# Django
2828
from django.conf import settings
29+
from django.contrib.auth.models import User
2930
from django.db.models.signals import (
3031
post_delete,
3132
post_save,
@@ -112,6 +113,8 @@ def workout_log_saved(sender, instance: WorkoutLog, created: bool, **kwargs):
112113

113114
# Trigger trophy evaluation
114115
_trigger_trophy_evaluation(instance.user_id)
116+
except User.DoesNotExist:
117+
pass
115118
except Exception as e:
116119
logger.error(f'Error updating statistics for user {instance.user_id}: {e}', exc_info=True)
117120

@@ -128,6 +131,8 @@ def workout_log_deleted(sender, instance: WorkoutLog, **kwargs):
128131

129132
try:
130133
UserStatisticsService.handle_workout_deletion(instance.user)
134+
except User.DoesNotExist:
135+
pass
131136
except Exception as e:
132137
logger.error(
133138
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
163168

164169
# Trigger trophy evaluation
165170
_trigger_trophy_evaluation(instance.user_id)
171+
except User.DoesNotExist:
172+
pass
166173
except Exception as e:
167174
logger.error(f'Error updating statistics for session {instance.id}: {e}', exc_info=True)
168175

@@ -179,6 +186,8 @@ def workout_session_deleted(sender, instance: WorkoutSession, **kwargs):
179186

180187
try:
181188
UserStatisticsService.handle_workout_deletion(instance.user)
189+
except User.DoesNotExist:
190+
pass
182191
except Exception as e:
183192
logger.error(
184193
f'Error updating statistics after session deletion for user {instance.user_id}: {e}',

wger/trophies/tests/test_services.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ def test_handle_workout_deletion(self):
106106
self.assertEqual(stats.total_weight_lifted, Decimal('0'))
107107

108108

109+
def test_get_or_create_statistics_deleted_user(self):
110+
"""
111+
Test get_or_create raises DoesNotExist for deleted user
112+
"""
113+
with patch.object(User.objects, 'filter') as mock_filter:
114+
mock_filter.return_value.exists.return_value = False
115+
with self.assertRaises(User.DoesNotExist):
116+
UserStatisticsService.get_or_create_statistics(self.user)
109117
class TrophyServiceTestCase(WgerTestCase):
110118
"""
111119
Test the TrophyService

0 commit comments

Comments
 (0)