- DailyCache: JSON file cache keyed by (source/as_of), TTL 24h, atomic tmp+rename write, persists across runs - fetch_or_stale: returns fresh cache, else refetch+cache, else falls back to stale so dashboard is never blanked - 7 tests; full suite pass
77 lines
2.3 KiB
Python
77 lines
2.3 KiB
Python
"""Tests for the daily cache layer."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import tempfile
|
|
import time
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from app import daily_cache
|
|
|
|
|
|
class DailyCacheTest(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self._tmp = tempfile.mkdtemp(prefix="cachemain_")
|
|
self.cache = daily_cache.DailyCache(cache_dir=Path(self._tmp), ttl=0.5)
|
|
|
|
def tearDown(self) -> None:
|
|
import shutil
|
|
shutil.rmtree(self._tmp, ignore_errors=True)
|
|
|
|
def test_set_get_roundtrip(self) -> None:
|
|
self.cache.set("tourism/2026-08-25", {"value": 123, "as_of": "2026-08"})
|
|
got = self.cache.get("tourism/2026-08-25")
|
|
self.assertIsNotNone(got)
|
|
self.assertEqual(got["value"], 123)
|
|
|
|
def test_missing_key_returns_none(self) -> None:
|
|
self.assertIsNone(self.cache.get("nope"))
|
|
|
|
def test_fresh_after_set(self) -> None:
|
|
self.cache.set("k", {"a": 1})
|
|
self.assertTrue(self.cache.fresh("k"))
|
|
|
|
def test_expired_not_fresh(self) -> None:
|
|
self.cache.set("k", {"a": 1})
|
|
time.sleep(0.7)
|
|
self.assertFalse(self.cache.fresh("k"))
|
|
|
|
def test_fetch_or_stale_caches_value(self) -> None:
|
|
calls = {"n": 0}
|
|
|
|
def fetcher():
|
|
calls["n"] += 1
|
|
return {"value": 42}
|
|
|
|
v1 = self.cache.fetch_or_stale("k", fetcher)
|
|
self.assertEqual(v1["value"], 42)
|
|
self.assertEqual(calls["n"], 1)
|
|
|
|
# second call within TTL -> cached, fetcher not re-invoked
|
|
v2 = self.cache.fetch_or_stale("k", fetcher)
|
|
self.assertIsNotNone(v2)
|
|
self.assertEqual(v2["value"], 42)
|
|
self.assertEqual(calls["n"], 1)
|
|
|
|
def test_fetch_or_stale_falls_back_on_error(self) -> None:
|
|
self.cache.set("k", {"value": 99})
|
|
|
|
def broken():
|
|
raise RuntimeError("network down")
|
|
|
|
# expired cache so it tries to refetch
|
|
time.sleep(0.7)
|
|
v = self.cache.fetch_or_stale("k", broken)
|
|
self.assertEqual(v["value"], 99)
|
|
self.assertTrue(v.get("_stale"))
|
|
|
|
def test_reload_persists(self) -> None:
|
|
self.cache.set("k", {"x": 1})
|
|
cache2 = daily_cache.DailyCache(cache_dir=Path(self._tmp), ttl=0.5)
|
|
self.assertEqual(cache2.get("k")["x"], 1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|