ghost-shopper-api / python /tests /test_auth.py
azzaraqi
Deploy FastAPI backend with Supabase pooler support
b84ea83
Raw
History Blame Contribute Delete
4.17 kB
"""
Tests for JWT Authentication endpoints.
Tests:
1. POST /auth/register β†’ 501 when pyjwt/passlib not installed
2. POST /auth/login β†’ 501 when pyjwt/passlib not installed
3. POST /auth/login β†’ 422 when missing email/password
4. POST /auth/register β†’ 422 when missing fields
5. POST /auth/register β†’ 422 when password too short
6. GET /auth/me β†’ 401 when no Authorization header
7. Regression: existing endpoints unaffected by auth routes
These tests validate the graceful degradation pattern:
auth deps (pyjwt, passlib) are optional β€” endpoints return 501
with clear instructions when they're not installed.
"""
import pytest
import httpx
BASE = "http://localhost:5000/api/v1"
@pytest.fixture
def client():
with httpx.Client(base_url=BASE, timeout=10) as c:
yield c
class TestAuthRegister:
"""Tests for POST /auth/register."""
def test_register_returns_501_without_deps(self, client):
"""Should return 501 with clear message about missing auth deps."""
r = client.post("/auth/register", json={
"email": "test@example.com",
"password": "testpassword123", # pragma: allowlist secret
"full_name": "Test User",
})
# If deps ARE installed, this will be 200.
# If deps are NOT installed, this will be 501.
assert r.status_code in (200, 501, 409)
if r.status_code == 501:
body = r.json()
assert body["detail"]["code"] == "AUTH_NOT_CONFIGURED"
assert "pyjwt" in body["detail"]["message"] or "passlib" in body["detail"]["message"]
def test_register_requires_fields(self, client):
"""Should return 422 when required fields are missing."""
r = client.post("/auth/register", json={})
assert r.status_code == 422
body = r.json()
assert body["detail"]["code"] == "VALIDATION_ERROR"
def test_register_password_min_length(self, client):
"""Should return 422 when password is too short."""
r = client.post("/auth/register", json={
"email": "test@example.com",
"password": "short", # pragma: allowlist secret
"full_name": "Test User",
})
assert r.status_code == 422
body = r.json()
assert "8 characters" in body["detail"]["message"]
class TestAuthLogin:
"""Tests for POST /auth/login."""
def test_login_returns_501_without_deps(self, client):
"""Should return 501 with clear message about missing auth deps."""
r = client.post("/auth/login", json={
"email": "test@example.com",
"password": "testpassword123", # pragma: allowlist secret
})
assert r.status_code in (200, 401, 501)
if r.status_code == 501:
body = r.json()
assert body["detail"]["code"] == "AUTH_NOT_CONFIGURED"
def test_login_requires_fields(self, client):
"""Should return 422 when email or password missing."""
r = client.post("/auth/login", json={})
assert r.status_code == 422
body = r.json()
assert body["detail"]["code"] == "VALIDATION_ERROR"
class TestAuthMe:
"""Tests for GET /auth/me."""
def test_me_requires_authorization(self, client):
"""Should return 401 when no auth header provided."""
r = client.get("/auth/me")
assert r.status_code == 401
body = r.json()
assert body["detail"]["code"] == "UNAUTHORIZED"
def test_me_rejects_invalid_token(self, client):
"""Should return 401 with an invalid token."""
r = client.get("/auth/me", headers={
"Authorization": "Bearer invalid.token.here",
})
assert r.status_code in (401, 501)
class TestAuthRegression:
"""Ensure auth routes don't break existing endpoints."""
def test_existing_endpoints_still_work(self, client):
r = client.get("/health")
assert r.status_code == 200
r = client.get("/visits", params={"page": 1, "per_page": 1})
assert r.status_code == 200
r = client.get("/outlets")
assert r.status_code == 200