thermograph/tests/test_store.py
Emi Griffith 576a239723 Compare/calendar mobile polish: overflow fix, sheet load button, 6-yr default, country in names (#80)
- Fix the mobile width blow-out: the distribution grid item (.cmp-dist-row) had no
  min-width:0, so each row expanded to the 9–10-column connected bar chart's
  min-content and widened the whole page (zoom-out, clipped filter sheet). Add
  grid-template-columns: minmax(0,1fr) + min-width:0 so the strip scrolls inside its
  own .ct-strip, and tighten .ct-col to 34px on phones.
- Add a Load/Refresh button inside the compare filter sheet's date-range block, wired
  to the same refresh()/isDirty(); editing the range on mobile no longer needs the
  sheet closed. The existing button stays in the controls (loads added places).
- Default date range on both pages is now January six years back → the present. On
  the calendar this is an explicit chunked range (the months=24 path is server-capped
  at ~2yr).
- Names now include country: reverse_geocode appends it to the place label; revgeo
  cache key gets a v2 prefix and PAYLOAD_VER bumps to p2 so labels/payloads
  repopulate.
- Location names read as a hierarchy: compact chips show just the lead segment (full
  name in the title/aria), and each ranked card shows the lead segment bold over a
  muted "city · region · country" line.

Frontend + a small backend name/cache change; no schema migration.
2026-07-12 06:32:10 +00:00

65 lines
2.8 KiB
Python

import json
import sqlite3
import time
def test_payload_round_trip(tmp_store):
payload = {"cell": "1_2", "days": [1, 2, 3]}
body = tmp_store.put_payload("grade", "1_2", "k", "tok", payload)
assert json.loads(body) == payload
assert tmp_store.get_payload("grade", "1_2", "k", "tok") == body
assert tmp_store.get_json("grade", "1_2", "k", "tok") == payload
def test_token_mismatch_is_a_miss(tmp_store):
tmp_store.put_payload("grade", "1_2", "k", "tok-a", {"v": 1})
assert tmp_store.get_payload("grade", "1_2", "k", "tok-b") is None
# A rewrite under the new token replaces the row (same primary key).
tmp_store.put_payload("grade", "1_2", "k", "tok-b", {"v": 2})
assert tmp_store.get_json("grade", "1_2", "k", "tok-b") == {"v": 2}
assert tmp_store.get_payload("grade", "1_2", "k", "tok-a") is None
def test_cache_false_serves_without_persisting(tmp_store):
body = tmp_store.put_payload("grade", "1_2", "k", "tok", {"v": 1}, cache=False)
assert json.loads(body) == {"v": 1}
assert tmp_store.get_payload("grade", "1_2", "k", "tok") is None
def test_put_payload_rejects_nan(tmp_store):
import pytest
with pytest.raises(ValueError):
tmp_store.put_payload("grade", "1_2", "k", "tok", {"v": float("nan")})
def test_revgeo_round_trip(tmp_store):
key = tmp_store.revgeo_key(47.60623, -122.33305)
assert key == "v2:47.606,-122.333" # v2: label format now carries country
assert tmp_store.get_revgeo(key) == (False, None)
tmp_store.put_revgeo(key, "Belltown, Seattle, Washington, United States")
assert tmp_store.get_revgeo(key) == (True, "Belltown, Seattle, Washington, United States")
def test_revgeo_cached_miss_retries_after_ttl(tmp_store):
key = tmp_store.revgeo_key(1.0, 2.0)
tmp_store.put_revgeo(key, None)
assert tmp_store.get_revgeo(key) == (True, None) # fresh miss: don't re-ask yet
# Age the row past the TTL directly in SQLite.
conn = sqlite3.connect(tmp_store.DB_PATH)
conn.execute("UPDATE revgeo SET updated_at=? WHERE key=?",
(time.time() - tmp_store.REVGEO_MISS_TTL - 1, key))
conn.commit()
conn.close()
assert tmp_store.get_revgeo(key) == (False, None) # stale miss: retryable
def test_store_is_a_pure_accelerator_when_db_unavailable(tmp_store, monkeypatch):
# Point the store somewhere unwritable: every helper degrades, none raises.
monkeypatch.setattr(tmp_store, "DB_PATH", "/proc/nope/store.sqlite")
import threading
monkeypatch.setattr(tmp_store, "_local", threading.local())
assert tmp_store.get_payload("grade", "c", "k", "t") is None
body = tmp_store.put_payload("grade", "c", "k", "t", {"v": 1})
assert json.loads(body) == {"v": 1} # still serves the encoded payload
assert tmp_store.get_revgeo("x") == (False, None)
tmp_store.put_revgeo("x", "label") # no-op, no exception