soulsync/tests/test_video_cache.py
BoulderBadgeDad 621693ecc8 video: real TTL+LRU cache (thread-safe) + cache search — the kettui way
The inline dict cache was a latent bug: no lock (the engine singleton is hit by
concurrent Flask + worker threads) and a wholesale-clear cliff at 256 (nuked hot
entries). Extracted a thread-safe TTL+LRU TTLCache into an importable core/ module
with seam tests (expiry via injected clock, LRU-not-wholesale eviction, a
concurrency stress test). Engine now uses it; search is cached too (60s, ownership
re-stamped fresh). Deliberately NOT persisted to disk — durable data already lives
in video.db; that tier would be over-engineering for a self-hosted app.
2026-06-15 13:02:24 -07:00

61 lines
1.7 KiB
Python

"""Seam tests for the video enrichment TTL+LRU cache (importable core/ module)."""
from __future__ import annotations
import threading
from core.video.enrichment.cache import TTLCache
def test_get_miss_and_hit():
c = TTLCache(maxsize=4, ttl=100)
assert c.get("a") is None
c.put("a", 1)
assert c.get("a") == 1
def test_ttl_expiry_with_injected_clock():
t = {"now": 0.0}
c = TTLCache(maxsize=4, ttl=10, clock=lambda: t["now"])
c.put("a", 1)
t["now"] = 9.9
assert c.get("a") == 1 # still fresh
t["now"] = 10.1
assert c.get("a") is None # expired → evicted
assert len(c) == 0
def test_per_put_ttl_override():
t = {"now": 0.0}
c = TTLCache(ttl=1000, clock=lambda: t["now"])
c.put("a", 1, ttl=5)
t["now"] = 6
assert c.get("a") is None
def test_lru_eviction_not_wholesale():
c = TTLCache(maxsize=2, ttl=100)
c.put("a", 1)
c.put("b", 2)
assert c.get("a") == 1 # touch 'a' → most-recently-used
c.put("c", 3) # over capacity → evict LRU ('b'), NOT everything
assert c.get("a") == 1 # survived
assert c.get("c") == 3 # survived
assert c.get("b") is None # the only one evicted
assert len(c) == 2
def test_thread_safe_under_concurrency():
c = TTLCache(maxsize=64, ttl=100)
def worker(n):
for i in range(500):
c.put(("k", (n * 500 + i) % 80), i)
c.get(("k", i % 80))
threads = [threading.Thread(target=worker, args=(n,)) for n in range(8)]
for t in threads:
t.start()
for t in threads:
t.join()
assert len(c) <= 64 # bound held, no crash/corruption