The real-data era: token live, stubs retired, editions on demand
BGG application approved. The migration the stub markers guarded for weeks: both synthetic caches deleted; tests/fixtures/bgg_cache re-recorded from the live API (recording list extended to every scenario the suite exercises — Civilization truncation, the Sorcerer SPI tiebreak, StarForce, Flat Top's thematic year, Alice Is Missing's rpgitem fallback); resolve --force re-matched all 133 titles for real (109 auto, 6 ambiguous, 18 unmatched, 30 edition ballots); data/STUB_DATA.marker deleted with its exit condition met — the guard mechanism stays armed should stubs ever regenerate. Reality fixed one bug and taught one lesson. The bug: a multi-type search lists an expansion twice (once per matched type) and the parser kept the generic boardgame entry — parse_search now dedupes by id preferring the specific type, which is what keeps expansion tagging (the base-vs-expansion review guard) alive on real data. The lesson: hand-built ambiguity is tidier than the real thing — Wingspan has 46 versions with three plausible English Stonemaier printings, so the suite's synthetic version ids and version_auto expectations became real ballots (assertions updated to recorded reality; the cue-plumbing test keeps its crafted two-version scenario via an injected transport). New: pick edition. A cue-less matched row is version_unknown by design (never guess) — but the owner knows which printing the box is. open_version_ballot() fetches the game's complete version list, cue-scores it when cues exist, and marks the row version_ambiguous so the normal Review edition pass presents it; the Titles page grows the button (Eric's three Wiz-Wars: two cue-less copies can now each claim their edition). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
co-authored by
Claude Fable 5
parent
15d3120029
commit
59f4b8c43c
@@ -1089,3 +1089,42 @@ def test_generic_camera_names_never_overwrite(tmp_path):
|
||||
assert res3.json()["saved"] == ["IMG_9999.jpeg"]
|
||||
assert (cfg.photos_dir / "IMG_9999.jpeg").read_bytes() == b"\xff\xd8reshoot"
|
||||
assert not (cfg.extract_raw_dir / "IMG_9999.jpeg.json").exists()
|
||||
|
||||
|
||||
def test_open_versions_endpoint_routes_row_into_review(tmp_path):
|
||||
from pathlib import Path as _P
|
||||
|
||||
cfg = make_cfg(tmp_path)
|
||||
rows = read_matches(cfg.matches_path)
|
||||
rows.append(
|
||||
_row(
|
||||
title_raw="Wingspan",
|
||||
match_status="auto",
|
||||
bgg_id="266192",
|
||||
bgg_name="Wingspan",
|
||||
version_status="version_unknown",
|
||||
)
|
||||
)
|
||||
write_matches(cfg.matches_path, rows)
|
||||
fixtures = BGGClient(cache_dir=_P("tests/fixtures/bgg_cache"))
|
||||
web = TestClient(create_app(cfg, client=fixtures))
|
||||
res = web.post(
|
||||
"/api/open-versions",
|
||||
json={"title_raw": "Wingspan", "source_photos": "shelf.jpg"},
|
||||
)
|
||||
assert res.status_code == 200
|
||||
versions = res.json()["versions"]
|
||||
assert any(v["title_raw"] == "Wingspan" for v in versions) # in the pass
|
||||
# a BGG outage surfaces as a gateway error, not a fake success
|
||||
rows = read_matches(cfg.matches_path)
|
||||
for r in rows:
|
||||
if r["title_raw"] == "Dungeon!":
|
||||
r["version_status"] = "version_unknown"
|
||||
unauthorized = TestClient(
|
||||
create_app(cfg, client=unauthorized_client(tmp_path / "x"))
|
||||
)
|
||||
res = unauthorized.post(
|
||||
"/api/open-versions",
|
||||
json={"title_raw": "Dungeon!", "source_photos": "shelf.jpg"},
|
||||
)
|
||||
assert res.status_code == 502
|
||||
|
||||
Reference in New Issue
Block a user