"""Replay test: verify same document → LLM cache hit on second call.""" from __future__ import annotations from unittest.mock import AsyncMock, MagicMock import pytest pytestmark = pytest.mark.replay @pytest.mark.asyncio async def test_same_document_hits_cache_on_replay() -> None: """Processing the same document twice should hit cache on the second call.""" from libs.llm.cache import LLMCacheStore from libs.llm.client import OllamaClient from libs.llm.parser import LLMParser # Simulate cache miss on first call, hit on second cached_output = { "schema_version": "1.0.0", "document_id": "DOC::replay_test", "parser_kind": "llm", "event_type": "earnings_release", "event_direction": "bullish", "event_date": "2026-01-15", "filing_time_bucket": "post_market", "headline": "Q1 results beat", "summary": "Earnings beat estimates.", "guidance": {"status": "raised", "scope": "annual", "notes": ""}, "signals": { "demand_strength": "strong", "pricing_power": "present", "backlog_or_bookings": "present", "customer_expansion": "present", "margin_quality": "improving", }, "risk_flags": { "oneoff_item": False, "tax_benefit": False, "valuation_gain": False, "non_gaap_heavy": False, "financing_related": False, "legal_or_regulatory_overhang": False, }, "evidence": [], "confidence": { "overall": 0.85, "event_type": 0.90, "event_direction": 0.80, "guidance": 0.75, "risk_flags": 0.90, }, "warnings": [], } call_count = {"n": 0} async def mock_get(session: object, cache_key: str) -> dict | None: if call_count["n"] == 0: return None # cache miss on first call return cached_output # cache hit on subsequent calls async def mock_put(*args: object, **kwargs: object) -> None: call_count["n"] += 1 # increment after first call stores to cache mock_cache = MagicMock(spec=LLMCacheStore) mock_cache.get = AsyncMock(side_effect=mock_get) mock_cache.put = AsyncMock(side_effect=mock_put) # Mock Ollama client (only called once) mock_ollama = MagicMock(spec=OllamaClient) mock_ollama.model = "llama3.2" mock_ollama.chat = AsyncMock( return_value=( {k: v for k, v in cached_output.items() if k != "schema_version"}, {"prompt_tokens": 100, "completion_tokens": 50}, 300, ) ) llm_parser = LLMParser(client=mock_ollama, cache_store=mock_cache) doc_text = "Apple reports Q1 earnings: revenue $123B, EPS $2.50, beats estimates." doc_meta = { "form_type": "8-K", "filing_date": "2026-01-15", "filing_time_bucket": "post_market", } # First call — cache miss, Ollama called mock_session1 = AsyncMock() result1 = await llm_parser.parse( document_id="DOC::replay_test", doc_text=doc_text, doc_meta=doc_meta, rule_hints={}, session=mock_session1, ) assert result1 is not None assert mock_ollama.chat.call_count == 1 # Second call — cache hit, Ollama NOT called again mock_session2 = AsyncMock() result2 = await llm_parser.parse( document_id="DOC::replay_test", doc_text=doc_text, doc_meta=doc_meta, rule_hints={}, session=mock_session2, ) assert result2 == cached_output # Ollama was still only called once (not twice) assert mock_ollama.chat.call_count == 1