semantic-search-api / tests /unit /test_cache.py
TarunikaHF's picture
test: fix assertion exception type in test_cache.py
0b91e41
Raw
History Blame Contribute Delete
3.63 kB
import pytest
import numpy as np
import time
from app.cache import SemanticCache
@pytest.fixture
def cache():
return SemanticCache(threshold=0.85, max_entries=5)
def test_empty_cache_returns_none(cache):
query_emb = np.array([1.0, 0.0], dtype=np.float32)
assert cache.lookup(query_emb, dominant_cluster=0) is None
def test_store_and_lookup_exact_match(cache):
query_emb = np.array([1.0, 0.0], dtype=np.float32)
cache.store("test query", query_emb, "test answer", 0, generated_answer="test answer", citations=[{"id": 1}])
# Simulate lookup with candidate index 0
result = cache.lookup(query_emb, dominant_cluster=0)
assert result is not None
assert result.query == "test query"
assert result.generated_answer == "test answer"
def test_lookup_returns_none_below_threshold(cache):
emb1 = np.array([1.0, 0.0], dtype=np.float32)
emb2 = np.array([0.0, 1.0], dtype=np.float32) # Orthogonal, dot product = 0.0
cache.store("query 1", emb1, "ans 1", 0)
# lookup with emb2 should fail threshold (0.0 < 0.85)
assert cache.lookup(emb2, dominant_cluster=0) is None
def test_lru_eviction_removes_oldest():
cache = SemanticCache(threshold=0.85, max_entries=2)
emb1 = np.array([1.0, 0.0], dtype=np.float32)
emb2 = np.array([0.0, 1.0], dtype=np.float32)
emb3 = np.array([0.707, 0.707], dtype=np.float32)
cache.store("q1", emb1, "a1", 0)
cache.store("q2", emb2, "a2", 1)
cache.store("q3", emb3, "a3", 2) # This should evict q1 (index 0)
# q1 should be evicted
queries = [entry.query for entry in cache._store]
assert "q1" not in queries
assert len(cache._store) == 2
assert "q2" in queries
assert "q3" in queries
def test_ttl_expiry_skips_stale_entries():
cache = SemanticCache(threshold=0.85, max_entries=5, max_age=0.1)
emb = np.array([1.0, 0.0], dtype=np.float32)
cache.store("q1", emb, "a1", 0)
time.sleep(0.2) # Wait for expiry
# lookup should return None
assert cache.lookup(emb, dominant_cluster=0) is None
def test_flush_clears_all_entries_and_stats(cache):
emb = np.array([1.0, 0.0], dtype=np.float32)
cache.store("q1", emb, "a1", 0)
# Force some stats
cache.lookup(emb, dominant_cluster=0) # Hit
cache.lookup(np.array([0.0, 1.0], dtype=np.float32), dominant_cluster=0) # Miss
cache.flush()
assert len(cache._store) == 0
assert len(cache._cluster_index) == 0
stats = cache.get_stats()
assert stats["total_entries"] == 0
assert stats["hit_count"] == 0
assert stats["miss_count"] == 0
def test_set_threshold_validates_range(cache):
cache.set_threshold(0.9)
assert cache.threshold == 0.9
with pytest.raises(AssertionError):
cache.set_threshold(1.5)
with pytest.raises(AssertionError):
cache.set_threshold(-0.1)
def test_get_stats_returns_correct_counts(cache):
emb = np.array([1.0, 0.0], dtype=np.float32)
cache.store("q1", emb, "a1", 0)
cache.lookup(emb, dominant_cluster=0) # Hit
cache.lookup(np.array([0.0, 1.0], dtype=np.float32), dominant_cluster=0) # Miss
stats = cache.get_stats()
assert stats["total_entries"] == 1
assert stats["hit_count"] == 1
assert stats["miss_count"] == 1
assert stats["hit_rate"] == 0.5
assert stats["threshold"] == 0.85
def test_cluster_index_updated_on_store(cache):
emb = np.array([1.0, 0.0], dtype=np.float32)
cache.store("q1", emb, "a1", 5, np.array([0.1, 0.9]))
assert 5 in cache._cluster_index
assert 0 in cache._cluster_index[5] # Entry ID 0