"""Unit tests for LLMCacheStore.""" from __future__ import annotations from unittest.mock import AsyncMock, MagicMock import pytest from libs.llm.cache import LLMCacheStore, build_cache_key @pytest.mark.unit class TestBuildCacheKey: def test_deterministic(self) -> None: key1 = build_cache_key("text", "prompt_v1", "v1", "llama3.2", "1.0.0") key2 = build_cache_key("text", "prompt_v1", "v1", "llama3.2", "1.0.0") assert key1 == key2 def test_different_inputs_different_keys(self) -> None: key1 = build_cache_key("text_a", "p", "v1", "llama3.2") key2 = build_cache_key("text_b", "p", "v1", "llama3.2") assert key1 != key2 @pytest.mark.unit class TestLLMCacheStore: @pytest.mark.asyncio async def test_get_returns_none_on_miss(self) -> None: """Cache miss returns None.""" cache = LLMCacheStore() mock_session = AsyncMock() mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = None mock_session.execute = AsyncMock(return_value=mock_result) result = await cache.get(mock_session, "deadbeef" * 8) assert result is None @pytest.mark.asyncio async def test_put_stores_entry(self) -> None: """Cache put creates a new row when key is absent.""" cache = LLMCacheStore() mock_session = AsyncMock() # Simulate no existing entry mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = None mock_session.execute = AsyncMock(return_value=mock_result) mock_session.add = MagicMock() mock_session.flush = AsyncMock() await cache.put( session=mock_session, cache_key="deadbeef" * 8, document_id="DOC::test", model_name="llama3.2", prompt_version="v1", schema_version="1.0.0", raw_prompt='[{"role":"user","content":"test"}]', raw_response='{"event_type":"unknown"}', normalized={"event_type": "unknown"}, token_usage={"prompt_tokens": 10, "completion_tokens": 5}, elapsed_ms=250, ) mock_session.add.assert_called_once() mock_session.flush.assert_called_once()