test: added auth contract test
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
venv
|
||||||
@@ -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