Files
contexta_be/tests/test_cache.py
2026-04-26 18:52:00 +00:00

88 lines
3.1 KiB
Python

"""Tests for the in-memory response cache."""
import time
import pytest
from app.services import cache
@pytest.fixture(autouse=True)
def clear_cache():
"""Wipe cache state before each test."""
cache._store.clear()
cache._index.clear()
yield
cache._store.clear()
cache._index.clear()
class TestCacheGetSet:
def test_miss_on_empty_cache(self):
assert cache.get("col-1", "hello") is None
def test_set_then_get_returns_value(self):
payload = {"response": "Hi", "sources": []}
cache.set("col-1", "hello", payload)
assert cache.get("col-1", "hello") == payload
def test_different_collections_are_independent(self):
cache.set("col-a", "query", {"response": "A"})
cache.set("col-b", "query", {"response": "B"})
assert cache.get("col-a", "query")["response"] == "A"
assert cache.get("col-b", "query")["response"] == "B"
def test_query_normalisation_ignores_case_and_whitespace(self):
cache.set("col-1", " Hello World ", {"response": "hi"})
assert cache.get("col-1", "hello world") is not None
assert cache.get("col-1", "HELLO WORLD") is not None
def test_overwrite_updates_value(self):
cache.set("col-1", "q", {"response": "old"})
cache.set("col-1", "q", {"response": "new"})
assert cache.get("col-1", "q")["response"] == "new"
class TestCacheExpiry:
def test_expired_entry_returns_none(self, monkeypatch):
cache.set("col-1", "q", {"response": "old"})
# Manually expire the entry
k = list(cache._store.keys())[0]
cache._store[k] = (cache._store[k][0], time.monotonic() - 1)
assert cache.get("col-1", "q") is None
def test_expired_entry_is_evicted_from_store(self, monkeypatch):
cache.set("col-1", "q", {"response": "x"})
k = list(cache._store.keys())[0]
cache._store[k] = (cache._store[k][0], time.monotonic() - 1)
cache.get("col-1", "q")
assert k not in cache._store
class TestCacheInvalidation:
def test_invalidate_removes_all_entries_for_collection(self):
cache.set("col-1", "query a", {"response": "a"})
cache.set("col-1", "query b", {"response": "b"})
cache.set("col-2", "query a", {"response": "c"})
removed = cache.invalidate("col-1")
assert removed == 2
assert cache.get("col-1", "query a") is None
assert cache.get("col-1", "query b") is None
# Other collection unaffected
assert cache.get("col-2", "query a") is not None
def test_invalidate_unknown_collection_returns_zero(self):
assert cache.invalidate("nonexistent") == 0
def test_index_cleaned_up_after_invalidation(self):
cache.set("col-1", "q", {"response": "x"})
cache.invalidate("col-1")
assert "col-1" not in cache._index
class TestCacheEviction:
def test_does_not_exceed_max_entries(self, monkeypatch):
monkeypatch.setattr(cache, "_MAX_ENTRIES", 5)
for i in range(10):
cache.set("col-1", f"query {i}", {"response": str(i)})
assert len(cache._store) <= 5