Coverage for modules\authentication\tests\test_password_reset.py: 100%
26 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-10-17 16:50 +0800
« prev ^ index » next coverage.py v7.16.0, created at 2026-10-17 16:50 +0800
1import re
3from django.core import mail
4from django.test import TestCase, override_settings
6from modules.authentication.models import AuthAuditEvent, PasswordResetToken, User
7from modules.authentication.services import AuthService
10@override_settings(
11 EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend',
12 FRONTEND_URL='http://testserver',
13 PASSWORD_RESET_REQUEST_LIMIT=2,
14)
15class PasswordResetServiceTests(TestCase):
16 def setUp(self):
17 self.user = User.objects.create_user(email='member@example.com', password='Before123!')
19 def test_reset_token_is_emailed_hashed_and_single_use(self):
20 AuthService.request_password_reset(self.user.email)
22 self.assertEqual(len(mail.outbox), 1)
23 self.assertNotIn('Before123!', mail.outbox[0].body)
24 token = re.search(r'token=([^\s]+)', mail.outbox[0].body).group(1)
25 stored = PasswordResetToken.objects.get(user=self.user)
26 self.assertNotEqual(stored.token_hash, token)
28 AuthService.confirm_password_reset(token, 'After123!')
29 self.user.refresh_from_db()
30 self.assertTrue(self.user.check_password('After123!'))
31 self.assertIsNotNone(stored.__class__.objects.get(id=stored.id).used_at)
33 with self.assertRaisesMessage(Exception, 'invalid or has expired'):
34 AuthService.confirm_password_reset(token, 'Another123!')
36 def test_unknown_email_does_not_send_mail_or_reveal_account_status(self):
37 AuthService.request_password_reset('nobody@example.com')
38 self.assertEqual(len(mail.outbox), 0)
39 self.assertTrue(AuthAuditEvent.objects.filter(event_type='PASSWORD_RESET_REQUESTED', email='nobody@example.com').exists())