""" Offline tests for Phase 3 enrichment running only on the top-k slice with ONE batched related-documents lookup (perf: the old code ran a sequential Neo4j query per fused result and the final trim discarded most of it). """ from unittest.mock import AsyncMock, MagicMock import pytest from src.services.hybrid_rag_service import HybridRAGService TENANT = "llm_tester" @pytest.fixture def graph(): g = MagicMock() g.get_related_documents_batch = AsyncMock(return_value={}) return g @pytest.fixture def service(graph): settings = MagicMock() settings.ollama_llm_model = "test-model" return HybridRAGService( vector_service=MagicMock(), graph_service=graph, searxng_client=MagicMock(), ollama_client=MagicMock(), content_extractor=MagicMock(), settings=settings, ) def _fused(n): return [ { "result": {"page_id": i + 1, "title": f"page {i + 1}"}, "rrf_score": 1.0 / (i + 1), "sources": ["vector"], } for i in range(n) ] @pytest.mark.unit class TestTopKEnrichment: async def test_only_top_k_enriched_with_single_batched_lookup( self, service, graph ): results = _fused(30) enriched = await service._enrich_with_related_dossiers( results, user=TENANT, top_k=5 ) # ONE batched lookup, only for the top-k page ids graph.get_related_documents_batch.assert_awaited_once() kwargs = graph.get_related_documents_batch.await_args.kwargs assert kwargs["page_ids"] == [1, 2, 3, 4, 5] assert kwargs["user"] == TENANT # The single-page method must not be used anymore graph.get_related_documents.assert_not_called() # Every result still carries the key (tail is empty) assert all("related_dossiers" in r for r in enriched) assert all(r["related_dossiers"] == [] for r in enriched[5:]) async def test_related_docs_mapped_onto_results(self, service, graph): graph.get_related_documents_batch = AsyncMock(return_value={ 1: [{ "page_id": 9, "title": "rel", "path": "p/rel", "tags": ["docker", "infra", "misc", "extra-tag-ignored"], "shared_entities": 3, }], }) results = _fused(3) enriched = await service._enrich_with_related_dossiers( results, user=TENANT, top_k=2 ) dossiers = enriched[0]["related_dossiers"] assert len(dossiers) == 3 # max 3 tags per related doc assert dossiers[0] == { "page_id": 9, "title": "rel", "path": "p/rel", "tag": "docker", "shared_entities": 3, } assert enriched[1]["related_dossiers"] == [] assert enriched[2]["related_dossiers"] == [] async def test_results_without_page_id_are_skipped(self, service, graph): results = [ {"result": {"url": "http://x", "title": "web"}, "sources": ["web"]}, {"result": {"page_id": 7, "title": "wiki"}, "sources": ["vector"]}, ] enriched = await service._enrich_with_related_dossiers( results, user=TENANT, top_k=10 ) kwargs = graph.get_related_documents_batch.await_args.kwargs assert kwargs["page_ids"] == [7] assert enriched[0]["related_dossiers"] == [] async def test_batch_lookup_failure_degrades_gracefully(self, service, graph): graph.get_related_documents_batch = AsyncMock( side_effect=RuntimeError("neo4j down") ) results = _fused(3) enriched = await service._enrich_with_related_dossiers( results, user=TENANT, top_k=3 ) assert all(r["related_dossiers"] == [] for r in enriched)