gapguide-api / apps /accounts /tests /test_auth.py
arifRB's picture
Deploy GapGuide backend (Docker)
ffd36e0 verified
Raw
History Blame Contribute Delete
5.47 kB
import pytest
from rest_framework.test import APIClient
pytestmark = pytest.mark.django_db
VALID_USER = {
'name': 'Test User',
'email': 'test@example.com',
'password': 'TestPass123!',
'password_confirm': 'TestPass123!'
}
REGISTER_URL = '/api/auth/register/'
LOGIN_URL = '/api/auth/login/'
LOGOUT_URL = '/api/auth/logout/'
ME_URL = '/api/auth/me/'
@pytest.fixture
def api_client():
return APIClient()
@pytest.fixture
def user_data():
return VALID_USER.copy()
@pytest.fixture
def registered_user(api_client, user_data):
"""Register a user and return the response data."""
response = api_client.post(REGISTER_URL, data=user_data, format='json')
return response.data
class TestRegistration:
def test_valid_registration(self, api_client, user_data):
"""TC-01: Valid registration returns 201 with tokens."""
response = api_client.post(REGISTER_URL, data=user_data, format='json')
assert response.status_code == 201
assert 'access' in response.data
assert 'refresh' in response.data
assert 'user' in response.data
assert response.data['user']['email'] == user_data['email']
def test_duplicate_email(self, api_client, user_data):
"""TC-02: Duplicate email returns 400."""
api_client.post(REGISTER_URL, data=user_data, format='json')
response = api_client.post(REGISTER_URL, data=user_data, format='json')
assert response.status_code == 400
def test_password_mismatch(self, api_client, user_data):
"""Password mismatch returns 400."""
user_data['password_confirm'] = 'DifferentPassword!'
response = api_client.post(REGISTER_URL, data=user_data, format='json')
assert response.status_code == 400
def test_missing_fields(self, api_client):
"""Missing required fields returns 400."""
response = api_client.post(REGISTER_URL, data={'email': 'x@x.com'}, format='json')
assert response.status_code == 400
class TestLogin:
def test_valid_login(self, api_client, user_data, registered_user):
"""TC-03: Valid login returns 200 with tokens."""
login_data = {'email': user_data['email'], 'password': user_data['password']}
response = api_client.post(LOGIN_URL, data=login_data, format='json')
assert response.status_code == 200
assert 'access' in response.data
assert 'refresh' in response.data
def test_invalid_credentials(self, api_client, user_data, registered_user):
"""TC-04: Wrong password returns 400."""
login_data = {'email': user_data['email'], 'password': 'WrongPassword!'}
response = api_client.post(LOGIN_URL, data=login_data, format='json')
assert response.status_code == 400
class TestMe:
def test_me_without_token(self, api_client):
"""GET /me/ without token returns 401."""
response = api_client.get(ME_URL)
assert response.status_code == 401
def test_get_me(self, api_client, user_data, registered_user):
"""Authenticated GET /me/ returns user data."""
access_token = registered_user['access']
api_client.credentials(HTTP_AUTHORIZATION='Bearer ' + access_token)
response = api_client.get(ME_URL)
assert response.status_code == 200
assert response.data['email'] == user_data['email']
class TestLoginThrottle:
def test_login_rate_limit_blocks_after_threshold(
self, api_client, user_data, registered_user,
):
"""Login endpoint is throttled at 10/minute — verify the 11th
attempt is rejected with 429, regardless of credentials."""
bad = {'email': user_data['email'], 'password': 'nope'}
# Burn through the limit with invalid credentials (400 per attempt).
for _ in range(10):
r = api_client.post(LOGIN_URL, data=bad, format='json')
assert r.status_code in (400, 401)
# 11th must be throttled.
r = api_client.post(LOGIN_URL, data=bad, format='json')
assert r.status_code == 429
class TestLogout:
def test_logout(self, api_client, user_data, registered_user):
"""Logout blacklists refresh token, returns 204."""
access_token = registered_user['access']
refresh_token = registered_user['refresh']
api_client.credentials(HTTP_AUTHORIZATION='Bearer ' + access_token)
response = api_client.post(
LOGOUT_URL,
data={'refresh_token': refresh_token},
format='json'
)
assert response.status_code == 204
class TestRegistrationRace:
def test_concurrent_email_race_returns_400(self, api_client, user_data):
"""F2: a concurrent case-variant registration can bypass the
case-insensitive validate_email check; the DB unique index then raises
IntegrityError. The view must surface a clean 400 keyed on `email`,
not an unhandled 500."""
from unittest.mock import patch
from django.db import IntegrityError
with patch(
'apps.accounts.serializers.RegisterSerializer.save',
side_effect=IntegrityError,
):
response = api_client.post(REGISTER_URL, data=user_data, format='json')
assert response.status_code == 400
assert 'email' in response.data