70 lines
2.5 KiB
Python
70 lines
2.5 KiB
Python
|
|
"""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()
|