Spaces:
Sleeping
Sleeping
| """ | |
| 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" | |
| 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 | |