Files
set50-system/backend/tests/test_daily_cache.py
Kunthawat Greethong 63058ecfc9 [verified] Add daily cache layer (fetch-once/day, stale fallback, atomic write)
- 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
2026-08-25 15:29:15 +07:00

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()