File size: 4,173 Bytes
b84ea83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
"""
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