test: added auth contract test
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from django.test import override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
from apps.accounts.models import OtpPurpose, PhoneOTP, User
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(OTP_PROVIDER="console")
|
||||
def test_phone_auth_request_creates_customer_for_new_phone(client):
|
||||
with patch("apps.accounts.services.otp.generate_code", return_value="123456"):
|
||||
response = client.post(
|
||||
reverse("phone_auth_request"),
|
||||
{
|
||||
"phone_number": "0512345678",
|
||||
"channel": "sms",
|
||||
"first_name": "Sara",
|
||||
"last_name": "Ali",
|
||||
"email": "sara@example.com",
|
||||
},
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
data = response.json()
|
||||
assert "request_id" in data
|
||||
assert "expires_at" in data
|
||||
|
||||
user = User.objects.get(phone_number="+966512345678")
|
||||
assert user.role == "customer"
|
||||
assert user.is_phone_verified is False
|
||||
|
||||
otp = PhoneOTP.objects.get(id=data["request_id"])
|
||||
assert otp.phone_number == "+966512345678"
|
||||
assert otp.purpose == OtpPurpose.AUTH
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(OTP_PROVIDER="console")
|
||||
def test_phone_auth_request_existing_phone_no_duplicate_user(client):
|
||||
User.objects.create_user(
|
||||
phone_number="+966512345678",
|
||||
email="existing@example.com",
|
||||
first_name="Existing",
|
||||
)
|
||||
|
||||
before_count = User.objects.filter(phone_number="+966512345678").count()
|
||||
|
||||
with patch("apps.accounts.services.otp.generate_code", return_value="123456"):
|
||||
response = client.post(
|
||||
reverse("phone_auth_request"),
|
||||
{"phone_number": "0512345678", "channel": "sms"},
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
assert User.objects.filter(phone_number="+966512345678").count() == before_count
|
||||
assert PhoneOTP.objects.filter(phone_number="+966512345678").count() == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(OTP_PROVIDER="console")
|
||||
def test_phone_auth_request_rejects_email_already_used(client):
|
||||
User.objects.create_user(
|
||||
phone_number="+966500000001",
|
||||
email="taken@example.com",
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
reverse("phone_auth_request"),
|
||||
{
|
||||
"phone_number": "0512345678",
|
||||
"channel": "sms",
|
||||
"email": "taken@example.com",
|
||||
},
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "detail" in response.json()
|
||||
assert User.objects.filter(phone_number="+966512345678").count() == 0
|
||||
assert PhoneOTP.objects.filter(phone_number="+966512345678").count() == 0
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_phone_auth_request_invalid_phone_localized_en(client):
|
||||
response = client.post(
|
||||
reverse("phone_auth_request"),
|
||||
{"phone_number": "123", "channel": "sms"},
|
||||
content_type="application/json",
|
||||
HTTP_ACCEPT_LANGUAGE="en",
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert response.json()["phone_number"][0] == "Phone number must be in E.164 format or a valid Saudi mobile"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_phone_auth_request_invalid_phone_localized_ar(client):
|
||||
response = client.post(
|
||||
reverse("phone_auth_request"),
|
||||
{"phone_number": "123", "channel": "sms"},
|
||||
content_type="application/json",
|
||||
HTTP_ACCEPT_LANGUAGE="ar-sa",
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert response.json()["phone_number"][0] == "يجب أن يكون رقم الهاتف بصيغة E.164 أو رقم جوال سعودي صالح"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(
|
||||
OTP_PROVIDER="console",
|
||||
OTP_MAX_PER_WINDOW=5,
|
||||
OTP_WINDOW_MINUTES=15,
|
||||
OTP_RESEND_COOLDOWN_SECONDS=60,
|
||||
)
|
||||
def test_phone_auth_request_cooldown_returns_retry_after(client):
|
||||
with patch("apps.accounts.services.otp.generate_code", return_value="123456"):
|
||||
first = client.post(
|
||||
reverse("phone_auth_request"),
|
||||
{"phone_number": "0512345678", "channel": "sms"},
|
||||
content_type="application/json",
|
||||
)
|
||||
assert first.status_code == 201
|
||||
|
||||
second = client.post(
|
||||
reverse("phone_auth_request"),
|
||||
{"phone_number": "0512345678", "channel": "sms"},
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert second.status_code == 429
|
||||
data = second.json()
|
||||
assert "detail" in data
|
||||
assert data["retry_after_seconds"] > 0
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(
|
||||
OTP_PROVIDER="console",
|
||||
OTP_MAX_PER_WINDOW=1,
|
||||
OTP_WINDOW_MINUTES=15,
|
||||
OTP_RESEND_COOLDOWN_SECONDS=0,
|
||||
)
|
||||
def test_phone_auth_request_rate_limit_returns_retry_after(client):
|
||||
with patch("apps.accounts.services.otp.generate_code", return_value="123456"):
|
||||
first = client.post(
|
||||
reverse("phone_auth_request"),
|
||||
{"phone_number": "0512345678", "channel": "sms"},
|
||||
content_type="application/json",
|
||||
)
|
||||
assert first.status_code == 201
|
||||
|
||||
second = client.post(
|
||||
reverse("phone_auth_request"),
|
||||
{"phone_number": "0512345678", "channel": "sms"},
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert second.status_code == 429
|
||||
data = second.json()
|
||||
assert "detail" in data
|
||||
assert data["retry_after_seconds"] > 0
|
||||
Reference in New Issue
Block a user