"""Tests for qmd search-result cache (TTL + mtime invalidation).""" import asyncio import tempfile import time import unittest from pathlib import Path from unittest.mock import patch from src.rag.qmd.cache import SearchCache class SearchCacheTestCase(unittest.IsolatedAsyncioTestCase): async def test_get_set_roundtrip(self): cache = SearchCache(ttl_seconds=60) args = ("merakom", "2026", "bm25", True) self.assertIsNone(cache.get("hello", args)) cache.set("hello", args, "ctx-blob") self.assertEqual(cache.get("hello", args), "ctx-blob") async def test_question_normalization(self): cache = SearchCache(ttl_seconds=60) args = ("merakom", "2026", "bm25", True) cache.set("Hello World", args, "ctx") self.assertEqual(cache.get(" hello world ", args), "ctx") async def test_different_args_yield_different_keys(self): cache = SearchCache(ttl_seconds=60) cache.set("q", ("o", "p1", "bm25", True), "ctx1") cache.set("q", ("o", "p2", "bm25", True), "ctx2") self.assertEqual(cache.get("q", ("o", "p1", "bm25", True)), "ctx1") self.assertEqual(cache.get("q", ("o", "p2", "bm25", True)), "ctx2") async def test_set_skips_empty_value(self): cache = SearchCache(ttl_seconds=60) cache.set("q", ("o", "p", "bm25", True), "") self.assertIsNone(cache.get("q", ("o", "p", "bm25", True))) async def test_ttl_expiry(self): cache = SearchCache(ttl_seconds=0) args = ("o", "p", "bm25", True) with patch("src.rag.qmd.cache._index_mtime", return_value=0.0): cache.set("q", args, "ctx") await asyncio.sleep(0.05) self.assertIsNone(cache.get("q", args)) async def test_mtime_invalidation(self): cache = SearchCache(ttl_seconds=60) args = ("o", "p", "bm25", True) with patch("src.rag.qmd.cache._index_mtime", return_value=10.0): cache.set("q", args, "ctx") with patch("src.rag.qmd.cache._index_mtime", return_value=20.0): self.assertIsNone(cache.get("q", args)) async def test_clear(self): cache = SearchCache(ttl_seconds=60) cache.set("q", ("o", "p", "bm25", True), "ctx") cache.clear() self.assertIsNone(cache.get("q", ("o", "p", "bm25", True))) async def test_stats(self): cache = SearchCache(ttl_seconds=42) stats = cache.stats() self.assertEqual(stats["entries"], 0) self.assertEqual(stats["ttl_seconds"], 42) if __name__ == "__main__": unittest.main()