""" LoginEvent: سابقه‌ی ماندگارِ ورود/خروج. آزموده: ورودِ موفق، کدِ غلط، مسدودشدن، خروج، ماندنِ ردیف بعد از حذفِ حساب. آزموده نشده: حجمِ بالا و نوشتن هم‌زمان. """ from django.core.cache import cache from django.test import TestCase from django.utils import timezone from rest_framework.test import APIClient from accounts.authentication import issue_access_token from accounts.models import LoginEvent, OTPCode, User class LoginEventTests(TestCase): def setUp(self): cache.clear() self.phone = '09121116001' self.api = APIClient() def otp(self, code='482913'): OTPCode.objects.create(phone=self.phone, code=code, expires_at=timezone.now() + timezone.timedelta(minutes=5)) def login(self, code): return self.api.post('/api/auth/login', {'phone': self.phone, 'otp': code}, format='json', HTTP_USER_AGENT='test-agent', REMOTE_ADDR='203.0.113.7') def test_success_and_failure_are_recorded(self): self.assertEqual(self.login('000000').status_code, 400) self.otp() self.assertEqual(self.login('482913').status_code, 200) events = list(LoginEvent.objects.order_by('id').values_list('event', 'phone', 'ip', 'user_agent')) self.assertEqual(events, [ ('login_failed', self.phone, '203.0.113.7', 'test-agent'), ('login_ok', self.phone, '203.0.113.7', 'test-agent'), ]) self.assertEqual(LoginEvent.objects.get(event='login_ok').user_id, User.objects.get(phone=self.phone).id) def test_logout_is_recorded(self): user = User.objects.create(phone=self.phone) self.api.credentials(HTTP_AUTHORIZATION=f'Bearer {issue_access_token(user.id)}') self.assertEqual(self.api.post('/api/auth/logout', {}, format='json').status_code, 200) self.assertTrue(LoginEvent.objects.filter(event='logout', user_id=user.id).exists()) def test_events_survive_account_deletion(self): user = User.objects.create(phone=self.phone) LoginEvent.objects.create(event='login_ok', user_id=user.id, phone=self.phone) self.api.credentials(HTTP_AUTHORIZATION=f'Bearer {issue_access_token(user.id)}') self.otp() r = self.api.post('/api/user/delete', {'otp': '482913'}, format='json') self.assertEqual(r.status_code, 200, r.content) self.assertFalse(User.objects.filter(pk=user.pk).exists()) self.assertEqual(set(LoginEvent.objects.values_list('event', flat=True)), {'login_ok', 'account_deleted'})