Audit round 7, upload cluster: evidence over inference at every exit
Five blind reviewers swept the real-data-era surface; this lands the upload findings, all verified against the code and the documented site behavior before fixing. The two HIGHs shared a root: logging outcomes the browser never proved. add_game waited for an "Add To" button that an owned game's page does not have — so a second-copy add could never succeed, and worse, an add that LANDED but missed the log became an unretryable failure loop (every retry: 30s timeout, logged failed, nothing ever settles). add_game now polls for either button state: "In Collections" without second_copy returns the previously-dead already_present status (the landed-but-unlogged case heals itself on retry); with second_copy it refuses loudly (that flow is unverified — add by hand). A save whose dialog is slow to hide reloads the page and asks for ownership evidence instead of guessing "failed". update_entry no longer trusts the editor merely closing: the cell must settle on text matching the CHOSEN version, else the AJAX save failed server-side and "updated" would mark a job done forever that never touched the site. Per-copy bookkeeping: stale_jobs endorsed per game, so rejecting one of two queued editions let the rejected copy upload on the survivor's endorsement — it now counts endorsements per (bgg_id, version) and retires the game with "re-run diff" when a copy loses its backing. annotate_queue stamped every row sharing a job key with the same log status, so one success marked both vetoed duplicates done; completions are now claimed one row per done log line. Smaller findings: the version-drift note queued a doomed re-add after warning about it (now skips — the entry exists on BGG; re-adding only duplicates); the one-update-per-game deferral rested on a claim the collid-exact editor disproves (removed — same-game updates run together); the 3-identical-failures abort compared exception class only, so three unrelated problems aborted a healthy run (now compares whole messages). Also from the test seat: run_upload's stale filtering finally executes against a real matches.csv in tests; rejected credentials pin that no anonymous storage state is saved; update_entry's three guarded exits each have a test; _scrub's newline flattening is pinned. 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
22fa17b5ee
commit
32b6aae841
@@ -6,6 +6,7 @@ from __future__ import annotations
|
||||
import json
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from bggpipe.bgg_client import BGGClient, cache_key
|
||||
from bggpipe.config import Config
|
||||
@@ -318,3 +319,22 @@ def test_summary_counts_local_and_api_entries_separately(tmp_path, capsys):
|
||||
import re as _re
|
||||
|
||||
assert not _re.search(r"-\d", out) # no negative tallies
|
||||
|
||||
|
||||
def test_corrupt_local_games_store_fails_loud(tmp_path):
|
||||
"""local_games.json is the ONLY source for off-BGG games — a tolerant
|
||||
reader that skipped it would silently drop hand-written metadata."""
|
||||
from bggpipe.enrich import run_enrich
|
||||
|
||||
cfg = Config(data_dir=tmp_path / "data")
|
||||
rows = _matches_rows()
|
||||
for row in rows: # all local: the store must be read before any fetch
|
||||
row["match_status"] = "local"
|
||||
row["bgg_id"] = ""
|
||||
write_matches(cfg.matches_path, rows)
|
||||
cfg.local_games_path.write_text("{torn")
|
||||
bare = BGGClient(
|
||||
cache_dir=tmp_path / "empty", transport=httpx.MockTransport(_no_network)
|
||||
)
|
||||
with pytest.raises(ValueError, match="local_games.json is corrupt"):
|
||||
run_enrich(cfg, client=bare)
|
||||
|
||||
Reference in New Issue
Block a user