""" Unit tests for RAGService — embedding cache, Qdrant client. Uses mocked dependencies to test in isolation. """ import pytest from unittest.mock import MagicMock, patch class TestEmbeddingCache: @pytest.fixture def rag_service(self): from app.services.rag_service import RAGService with patch.object(RAGService, '_load_model'), \ patch.object(RAGService, '_load_reranker'), \ patch.object(RAGService, '_init_turbovec'): service = RAGService() service.tv_index = MagicMock() return service def test_cache_initialized(self, rag_service): assert rag_service._embed_cache is not None assert rag_service._embed_cache_max == 256 def test_cache_stores_and_returns(self, rag_service): """Cache should store and return the same value for identical input.""" mock_response = MagicMock() mock_response.json.return_value = {"embeddings": [[0.1, 0.2, 0.3]]} mock_response.raise_for_status.return_value = None with patch("app.services.rag_service.httpx.post", return_value=mock_response): emb1 = rag_service._get_embedding("test query") # Second call should use cache (no httpx call) emb2 = rag_service._get_embedding("test query") assert emb1 == emb2 def test_cache_max_respected(self, rag_service): """When cache exceeds max, oldest entries should be evicted.""" old_entry = "a" * 10 for i in range(rag_service._embed_cache_max + 5): rag_service._embed_cache[f"key_{i}"] = [0.1] * 1024 if len(rag_service._embed_cache) > rag_service._embed_cache_max: rag_service._embed_cache.popitem(last=False) assert len(rag_service._embed_cache) <= rag_service._embed_cache_max class TestRAGServiceInit: def test_init_graceful_on_turbovec_failure(self): """RAGService constructor should not crash when turbovec is unavailable.""" from app.services.rag_service import RAGService with patch.object(RAGService, '_load_model'), \ patch.object(RAGService, '_load_reranker'), \ patch.object(RAGService, '_init_turbovec') as mock_init: service = RAGService() mock_init.assert_called_once() assert service.tv_index is None # _init_turbovec didn't set it