File size: 4,455 Bytes
c62301e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"""Tests for cache management API endpoints."""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from fastapi.testclient import TestClient


@pytest.fixture
def mock_user():
    """Create a mock user for auth dependency override."""
    from app.core import models

    user = MagicMock(spec=models.User)
    user.id = 1
    return user


@pytest.fixture
def client(mock_user):
    """Test client with auth dependency overridden."""
    from app.main import app
    from app.routers.auth import get_current_user

    # Override auth to return mock user
    def _override_user():
        return mock_user

    app.dependency_overrides[get_current_user] = _override_user
    yield TestClient(app)
    app.dependency_overrides.pop(get_current_user, None)


@pytest.fixture
def mock_cache_health_ok():
    """Patch get_cache().get_cache_health to return healthy status."""
    with patch("app.routers.cache.get_cache") as mock_get_cache:
        mock_cache = MagicMock()
        # Methods called with await need to be AsyncMock
        mock_cache.get_cache_health = AsyncMock()
        mock_cache.get_cache_health.return_value = {
            "status": "ok",
            "keys_tracked": 5,
            "ttl_seconds": 300,
        }
        mock_cache.invalidate_user_cache = AsyncMock()
        mock_cache.invalidate_user_cache.return_value = 3
        mock_get_cache.return_value = mock_cache
        yield mock_cache


@pytest.fixture
def mock_cache_degraded():
    """Patch get_cache().get_cache_health to return degraded status."""
    with patch("app.routers.cache.get_cache") as mock_get_cache:
        mock_cache = MagicMock()
        mock_cache.get_cache_health = AsyncMock()
        mock_cache.get_cache_health.return_value = {
            "status": "degraded",
            "keys_tracked": 0,
            "ttl_seconds": 300,
        }
        mock_cache.invalidate_user_cache = AsyncMock()
        mock_cache.invalidate_user_cache.return_value = 0
        mock_get_cache.return_value = mock_cache
        yield mock_cache


class TestCacheInvalidateEndpoint:
    """Tests for POST /api/v1/cache/invalidate."""

    def test_invalidate_returns_ok(self, client, mock_cache_health_ok):
        """Invalidate should return 200 with keys_cleared count."""
        response = client.post("/api/v1/cache/invalidate")

        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "ok"
        assert data["keys_cleared"] == 3

    def test_invalidate_degraded(self, client, mock_cache_degraded):
        """Invalidate should return degraded status when Redis unavailable."""
        response = client.post("/api/v1/cache/invalidate")

        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "degraded"
        assert data["keys_cleared"] == 0

    def test_invalidate_requires_auth(self):
        """Invalidate should return 401 without auth token."""
        from app.main import app

        client = TestClient(app)
        response = client.post(
            "/api/v1/cache/invalidate",
            headers={"Authorization": "Bearer invalid-token"},
        )

        # Should be 401 or 403 when unauthenticated
        assert response.status_code in (401, 403)


class TestCacheHealthEndpoint:
    """Tests for GET /api/v1/cache/health."""

    def test_health_ok(self, client, mock_cache_health_ok):
        """Health endpoint should return cache status."""
        response = client.get("/api/v1/cache/health")

        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "ok"
        assert data["keys_tracked"] == 5
        assert data["ttl_seconds"] == 300

    def test_health_degraded(self, client, mock_cache_degraded):
        """Health endpoint should return degraded when Redis unavailable."""
        response = client.get("/api/v1/cache/health")

        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "degraded"
        assert data["keys_tracked"] == 0

    def test_health_requires_auth(self):
        """Health endpoint should return 401 without auth token."""
        from app.main import app

        client = TestClient(app)
        response = client.get(
            "/api/v1/cache/health",
            headers={"Authorization": "Bearer invalid-token"},
        )

        assert response.status_code in (401, 403)