Files
guilan-ace-backend/apps/users/tests/integration/test_auth_service_edges.py
Amirhossein Khalili ff99130938 test: migrate backend suites to pytest with module coverage gate
Split backend tests into unit and integration suites, add isolated fixtures and coverage enforcement, and update CI and test documentation. Cover current authentication, content, notification, payment, media, and admin flows; fix behaviors revealed by the new tests. Validate both suites and the per-module gate against local PostgreSQL.
2026-09-26 16:50:29 +03:30

90 lines
4.7 KiB
Python

import pytest
from apps.users.models import Major, University, User
from apps.users.services import auth
pytestmark = [pytest.mark.integration, pytest.mark.django_db]
def test_otp_send_modes_and_login_otp(user_factory, fake_redis, monkeypatch, mocker):
monkeypatch.setattr(auth, "_redis", lambda: fake_redis)
sms = mocker.patch("apps.users.services.auth.send_critical_sms.delay")
user = user_factory(is_mobile_verified=False)
with pytest.raises(auth.AuthServiceError):
auth.generate_and_send_otp("bad", "register")
with pytest.raises(auth.AuthServiceError):
auth.generate_and_send_otp(user.mobile, "register")
with pytest.raises(auth.AuthServiceError):
auth.generate_and_send_otp("09197770061", "login")
response = auth.generate_and_send_otp(user.mobile, "login")
assert response["expires_in_seconds"] == auth.OTP_EXPIRY_SECONDS
code = fake_redis.get(auth._otp_key("login", user.mobile)).decode()
sms.assert_called_with(user.mobile, auth.SMS_KIND_LOGIN, code)
tokens = auth.login_with_otp(user.mobile, code)
assert tokens["access_token"] and tokens["refresh_token"]
user.refresh_from_db()
assert user.is_mobile_verified
with pytest.raises(auth.AuthServiceError):
auth.login_with_otp(user.mobile, code)
def test_mobile_claim_conflict_and_verification(user_factory, fake_redis, monkeypatch, mocker):
monkeypatch.setattr(auth, "_redis", lambda: fake_redis)
sms = mocker.patch("apps.users.services.auth.send_critical_sms.delay")
user = user_factory()
another = user_factory()
with pytest.raises(auth.AuthServiceError):
auth.send_authenticated_mobile_otp(user, another.mobile)
new_mobile = "09197770062"
auth.send_authenticated_mobile_otp(user, new_mobile)
code = fake_redis.get(auth._otp_key("verify_mobile", new_mobile)).decode()
sms.assert_called_with(new_mobile, auth.SMS_KIND_VERIFY_MOBILE, code)
assert auth.verify_authenticated_mobile_otp(user, new_mobile, code).mobile == new_mobile
user.refresh_from_db()
assert user.mobile == new_mobile and user.is_mobile_verified
assert auth.lookup_mobile_registration_state(new_mobile) == {"exists": True, "has_password": True}
assert auth.lookup_mobile_registration_state("09197770063") == {"exists": False, "has_password": False}
def test_registration_validates_profile_fields_and_duplicate_email(user_factory, fake_redis, monkeypatch):
monkeypatch.setattr(auth, "_redis", lambda: fake_redis)
existing = user_factory(email="used@example.com")
payload = auth.RegistrationPayload(username="new_profile", mobile="09197770064", password="StrongPass123!", code="12345")
fake_redis.set(auth._verified_otp_key("register", payload.mobile), "1")
for changes, field in [({"student_id": "123"}, "student_id"),
({"email": existing.email}, "email"),
({"major": "invalid"}, "major"),
({"university": "invalid"}, "university")]:
fake_redis.set(auth._verified_otp_key("register", payload.mobile), "1")
current = auth.RegistrationPayload(**{**payload.__dict__, **changes})
with pytest.raises(auth.AuthServiceError) as error:
auth.register_user(current)
assert error.value.field == field
major = Major.objects.create(code="TEST_AUTH_MAJOR", name="Auth Major")
university = University.objects.create(code="TEST_AUTH_UNI", name="Auth University")
current = auth.RegistrationPayload(**{**payload.__dict__, "major": major.code, "university": university.code,
"student_id": "1234567890"})
fake_redis.set(auth._verified_otp_key("register", payload.mobile), "1")
created = auth.register_user(current)
assert created.major == major and created.university == university
assert created.student_id == "1234567890"
assert User.objects.filter(username=payload.username).count() == 1
def test_password_reset_rejects_reuse_and_inactive_login(user_factory, fake_redis, monkeypatch):
monkeypatch.setattr(auth, "_redis", lambda: fake_redis)
user = user_factory(is_active=False)
with pytest.raises(auth.AuthServiceError) as error:
auth.login_with_password(user.mobile, "TestPass123!")
assert error.value.status_code == 401
user.is_active = True
user.save(update_fields=["is_active"])
with pytest.raises(auth.AuthServiceError):
auth.reset_password_with_otp("09197770065", "12345", "NewStrongPass123!")
fake_redis.set(auth._otp_key("reset_password", user.mobile), "12345")
with pytest.raises(auth.AuthServiceError) as error:
auth.reset_password_with_otp(user.mobile, "12345", "TestPass123!")
assert error.value.field == "password"
assert user.check_password("TestPass123!")