Files
MathApp/accounts/tests_delete_users.py
Zareian 2f1e257533 delete_users: only project models; Django admin history (LogEntry.user_id is staff) was being counted
Account deletion also keeps the status log of the payment records it keeps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 06:20:05 +03:30

78 lines
4.1 KiB
Python

"""delete_users: کاربر و همه‌ی داده‌اش در همه‌ی اپ‌ها، و هیچ چیزِ کاربرِ دیگری."""
from io import StringIO
from django.core.management import CommandError, call_command
from django.test import TestCase, override_settings
from accounts.models import OTPCode, Profile, User
from ai_tutor.models import Conversation, Message
from notifications.models import Notification
from payment.models import Order
from progress.models import UserStats
class DeleteUsersTests(TestCase):
def setUp(self):
self.doomed = User.objects.create(phone='09121110001', is_guest=False)
self.kept = User.objects.create(phone='09121110002', is_guest=False)
for user in (self.doomed, self.kept):
Profile.objects.create(user=user)
UserStats.objects.create(user_id=user.id, xp=10)
Message.objects.create(conversation=Conversation.objects.create(user_id=user.id), role='user', text='؟')
Notification.objects.create(user_id=user.id, type='xp_earned', title='ت')
OTPCode.objects.create(phone=user.phone, code='123456', expires_at='2030-01-01T00:00Z')
def run_command(self, *args):
out = StringIO()
call_command('delete_users', *args, stdout=out)
return out.getvalue()
def test_dry_run_changes_nothing(self):
out = self.run_command('--phones', self.doomed.phone)
self.assertIn('progress.UserStats.user_id: 1', out)
self.assertTrue(User.objects.filter(pk=self.doomed.pk).exists())
def test_everything_of_that_user_goes_and_nothing_else(self):
self.run_command('--phones', self.doomed.phone, '--yes')
self.assertFalse(User.objects.filter(pk=self.doomed.pk).exists())
for model in (UserStats, Conversation, Notification):
self.assertFalse(model.objects.filter(user_id=self.doomed.pk).exists(), model)
self.assertTrue(model.objects.filter(user_id=self.kept.pk).exists(), model)
self.assertFalse(OTPCode.objects.filter(phone=self.doomed.phone).exists())
self.assertTrue(OTPCode.objects.filter(phone=self.kept.phone).exists())
self.assertEqual(Message.objects.count(), 1)
def test_paid_orders_need_explicit_permission(self):
Order.objects.create(user_id=self.doomed.id, amount=1000, status='paid', idempotency_key='k1')
with self.assertRaises(CommandError):
self.run_command('--ids', str(self.doomed.pk), '--yes')
self.assertTrue(User.objects.filter(pk=self.doomed.pk).exists())
self.run_command('--ids', str(self.doomed.pk), '--yes', '--include-paid')
self.assertFalse(Order.objects.filter(user_id=self.doomed.pk).exists())
@override_settings(TEST_LOGIN_PHONES={'09121110002': '1234'})
def test_real_phones_keeps_demo_numbers_and_guests(self):
guest = User.objects.create(device_id='guest-x', is_guest=True)
self.run_command('--real-phones', '--yes')
self.assertFalse(User.objects.filter(pk=self.doomed.pk).exists())
self.assertTrue(User.objects.filter(pk=self.kept.pk).exists())
self.assertTrue(User.objects.filter(pk=guest.pk).exists())
def test_skip_paid_keeps_the_payer_and_deletes_the_rest(self):
Order.objects.create(user_id=self.doomed.id, amount=1000, status='paid', idempotency_key='k2')
self.run_command('--ids', str(self.doomed.pk), str(self.kept.pk), '--yes', '--skip-paid')
self.assertTrue(User.objects.filter(pk=self.doomed.pk).exists())
self.assertFalse(User.objects.filter(pk=self.kept.pk).exists())
def test_django_admin_history_is_never_touched(self):
from django.contrib.admin.models import LogEntry
from django.contrib.auth.models import User as StaffUser
from accounts.management.commands.delete_users import user_scoped_models
self.assertNotIn(LogEntry, [model for model, _ in user_scoped_models()])
staff = StaffUser.objects.create(id=self.doomed.pk, username='staff')
LogEntry.objects.create(user=staff, action_flag=1, object_repr='x')
self.run_command('--ids', str(self.doomed.pk), '--yes')
self.assertEqual(LogEntry.objects.count(), 1)