"""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)