youtube-summarizer/backend/tests/integration/test_cache_api.py

259 lines
10 KiB
Python

"""Integration tests for cache API endpoints."""
import pytest
from fastapi.testclient import TestClient
from unittest.mock import patch, AsyncMock, MagicMock
from backend.main import app
from backend.services.enhanced_cache_manager import EnhancedCacheManager, CacheConfig, CacheMetrics
@pytest.fixture
def client():
"""Create test client."""
return TestClient(app)
@pytest.fixture
def mock_cache_manager():
"""Create mock enhanced cache manager."""
manager = MagicMock(spec=EnhancedCacheManager)
manager.config = CacheConfig()
manager.metrics = CacheMetrics(hits=50, misses=10, write_operations=20)
manager._initialized = True
manager.redis_client = None
# Setup async methods
manager.get_cache_analytics = AsyncMock(return_value={
"performance_metrics": {
"hit_rate": 0.833,
"total_hits": 50,
"total_misses": 10,
"total_writes": 20,
"average_response_time_ms": 25.5
},
"redis_usage": None,
"memory_cache_usage": {
"entries": 15,
"estimated_size_mb": 0.5
},
"configuration": {
"transcript_ttl_hours": 168,
"summary_ttl_hours": 72,
"using_redis": False
}
})
manager.invalidate_cache = AsyncMock(return_value=15)
manager.initialize = AsyncMock()
return manager
class TestCacheAnalyticsEndpoint:
"""Test cache analytics endpoint."""
def test_get_cache_analytics_success(self, client, mock_cache_manager):
"""Test successfully getting cache analytics."""
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.get("/api/cache/analytics")
assert response.status_code == 200
data = response.json()
assert data["status"] == "success"
assert "data" in data
assert data["data"]["performance_metrics"]["hit_rate"] == 0.833
assert data["data"]["memory_cache_usage"]["entries"] == 15
def test_get_cache_analytics_error(self, client, mock_cache_manager):
"""Test error handling in cache analytics."""
mock_cache_manager.get_cache_analytics = AsyncMock(side_effect=Exception("Analytics error"))
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.get("/api/cache/analytics")
assert response.status_code == 500
assert "Analytics error" in response.json()["detail"]
class TestCacheInvalidationEndpoint:
"""Test cache invalidation endpoint."""
def test_invalidate_all_cache(self, client, mock_cache_manager):
"""Test invalidating all cache entries."""
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.post("/api/cache/invalidate")
assert response.status_code == 200
data = response.json()
assert data["status"] == "success"
assert data["count"] == 15
assert "Invalidated 15 cache entries" in data["message"]
def test_invalidate_cache_with_pattern(self, client, mock_cache_manager):
"""Test invalidating cache with pattern."""
mock_cache_manager.invalidate_cache = AsyncMock(return_value=5)
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.post("/api/cache/invalidate?pattern=transcript")
assert response.status_code == 200
data = response.json()
assert data["status"] == "success"
assert data["count"] == 5
mock_cache_manager.invalidate_cache.assert_called_once_with("transcript")
def test_invalidate_cache_error(self, client, mock_cache_manager):
"""Test error handling in cache invalidation."""
mock_cache_manager.invalidate_cache = AsyncMock(side_effect=Exception("Invalidation error"))
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.post("/api/cache/invalidate")
assert response.status_code == 500
assert "Invalidation error" in response.json()["detail"]
class TestCacheStatsEndpoint:
"""Test cache statistics endpoint."""
def test_get_cache_stats_success(self, client, mock_cache_manager):
"""Test successfully getting cache stats."""
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.get("/api/cache/stats")
assert response.status_code == 200
data = response.json()
assert data["status"] == "success"
assert data["data"]["hit_rate"] == 0.8333333333333334
assert data["data"]["total_hits"] == 50
assert data["data"]["total_misses"] == 10
assert data["data"]["total_operations"] == 80
assert data["data"]["errors"] == 0
def test_get_cache_stats_error(self, client, mock_cache_manager):
"""Test error handling in cache stats."""
mock_cache_manager.metrics.to_dict = MagicMock(side_effect=Exception("Stats error"))
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.get("/api/cache/stats")
assert response.status_code == 500
assert "Stats error" in response.json()["detail"]
class TestCacheWarmingEndpoint:
"""Test cache warming endpoint."""
def test_warm_cache_success(self, client, mock_cache_manager):
"""Test cache warming initiation."""
video_ids = ["video1", "video2", "video3"]
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.post("/api/cache/warm", json=video_ids)
assert response.status_code == 200
data = response.json()
assert data["status"] == "success"
assert data["video_count"] == 3
assert "initiated for 3 videos" in data["message"]
def test_warm_cache_empty_list(self, client, mock_cache_manager):
"""Test cache warming with empty list."""
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.post("/api/cache/warm", json=[])
assert response.status_code == 200
data = response.json()
assert data["status"] == "success"
assert data["video_count"] == 0
class TestCacheHealthEndpoint:
"""Test cache health check endpoint."""
def test_cache_health_check_healthy(self, client, mock_cache_manager):
"""Test healthy cache system."""
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.get("/api/cache/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "healthy"
assert data["components"]["memory_cache"] is True
assert data["components"]["redis"] is False
assert data["components"]["background_tasks"] is True
def test_cache_health_with_redis(self, client, mock_cache_manager):
"""Test cache health with Redis connection."""
mock_redis = AsyncMock()
mock_redis.ping = AsyncMock(return_value=True)
mock_cache_manager.redis_client = mock_redis
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.get("/api/cache/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "healthy"
assert data["components"]["redis"] is True
def test_cache_health_low_hit_rate_warning(self, client, mock_cache_manager):
"""Test cache health with low hit rate warning."""
mock_cache_manager.metrics.hit_rate = 0.5 # Below threshold
with patch('backend.api.cache.get_enhanced_cache_manager', return_value=mock_cache_manager):
response = client.get("/api/cache/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "healthy"
assert "warnings" in data
assert "Hit rate" in data["warnings"][0]
assert "below threshold" in data["warnings"][0]
def test_cache_health_check_error(self, client, mock_cache_manager):
"""Test cache health check with error."""
with patch('backend.api.cache.get_enhanced_cache_manager', side_effect=Exception("Health check error")):
response = client.get("/api/cache/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "unhealthy"
assert "Health check error" in data["error"]
class TestCacheIntegrationWithPipeline:
"""Test cache integration with pipeline endpoints."""
def test_pipeline_uses_cache(self, client):
"""Test that pipeline endpoints utilize cache."""
# This would be a more comprehensive integration test
# Testing the actual cache usage in the pipeline
pass # TODO: Implement when pipeline is fully integrated
class TestCacheManagerInitialization:
"""Test cache manager initialization."""
@pytest.mark.asyncio
async def test_cache_manager_singleton(self):
"""Test that cache manager is a singleton."""
from backend.api.cache import get_enhanced_cache_manager, _cache_manager_instance
# Reset instance
import backend.api.cache
backend.api.cache._cache_manager_instance = None
# First call creates instance
manager1 = await get_enhanced_cache_manager()
assert manager1 is not None
assert backend.api.cache._cache_manager_instance is not None
# Second call returns same instance
manager2 = await get_enhanced_cache_manager()
assert manager1 is manager2
# Cleanup
await manager1.close()
backend.api.cache._cache_manager_instance = None