Two blind reviewers swept the 33 commits since 10f65d8 for signs of
machine generation. Verdict: production code and copy largely clean;
the tells clustered in duplication and tests.
JS: the six-times-pasted change-detection loop (three pages honoring a
LAST-after-render invariant, three violating it) becomes one
changeGate() factory in app.js; the reshoot ticket renderer and
dismiss wiring, duplicated across photos/photo pages, become
ticketCard()/wireDismiss(); review.html's hand-rolled fetch/post
collapse onto fetchJSON/apiPost keeping only its unique
saved-but-render-failed path; dead lastGood deleted; page-state naming
unified to CAPS (ACTIVE, RUNNING); a dead defensive rowix branch gone.
CSS: header no longer claims "two pages"; --focus derives from
--accent; five state tints become tokens (the header's tokens-for-roles
promise, kept); component button rules drop declarations the global
rule supplies; duplicate color declarations trimmed.
Python: dead seen_per_title vestige removed from resolve; redundant
ternary arm in the catalog builder collapsed; csv import hoisted; twin
VetoBody/SplitBody merged into RowRef; warn-once idiom deduplicated
into a closure; a stray "a bare arrays" typo.
Tests: the one assertion that could never fail (aria-current check
with an always-true fallback) replaced by a strict per-page check
across all seven pages; the traversal test asserts escape
unconditionally; stale "both pages" names updated; nine redundant
function-local imports hoisted to their module tops.
Docs: aria role="status" set once in the shell instead of per call;
joblog gets role="log"; README's --lan paragraph becomes a proper
"From your phone" quickstart subsection with the command visible, and
the seven-page list stops restating the screenshot captions; Help's
re-extract claim matches actual behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
55 lines
1.9 KiB
Python
55 lines
1.9 KiB
Python
"""Config loading: toml knobs, env-only username, unknown-key warning."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from bggpipe.config import Config, load_config
|
|
|
|
|
|
def test_defaults_when_no_file(tmp_path, monkeypatch):
|
|
# no config.toml in cwd and no explicit path -> defaults (an EXPLICIT
|
|
# missing path errors instead; see the dedicated test)
|
|
monkeypatch.delenv("BGG_USERNAME", raising=False)
|
|
monkeypatch.chdir(tmp_path)
|
|
cfg = load_config()
|
|
assert cfg == Config()
|
|
assert cfg.cache_dir == Path("data/bgg_cache")
|
|
|
|
|
|
def test_reads_toml(tmp_path, monkeypatch):
|
|
monkeypatch.delenv("BGG_USERNAME", raising=False)
|
|
p = tmp_path / "config.toml"
|
|
p.write_text('data_dir = "elsewhere"\nrate_limit_seconds = 3\n')
|
|
cfg = load_config(p)
|
|
assert cfg.data_dir == Path("elsewhere")
|
|
assert cfg.rate_limit_seconds == 3.0
|
|
assert cfg.matches_path == Path("elsewhere/matches.csv")
|
|
|
|
|
|
def test_username_comes_from_env_only(tmp_path, monkeypatch):
|
|
# the account has exactly one home: BGG_USERNAME in the environment —
|
|
# a bgg_username key in config.toml is deliberately ignored
|
|
p = tmp_path / "config.toml"
|
|
p.write_text('bgg_username = "from_toml"\n')
|
|
monkeypatch.setenv("BGG_USERNAME", "from_env")
|
|
assert load_config(p).bgg_username == "from_env"
|
|
monkeypatch.delenv("BGG_USERNAME")
|
|
assert load_config(p).bgg_username == ""
|
|
|
|
|
|
def test_unknown_toml_keys_warn(tmp_path, monkeypatch):
|
|
# a typo'd knob must not silently fall back to defaults
|
|
monkeypatch.delenv("BGG_USERNAME", raising=False)
|
|
p = tmp_path / "config.toml"
|
|
p.write_text('photo_dir = "oops"\n')
|
|
with pytest.warns(UserWarning, match="photo_dir"):
|
|
load_config(p)
|
|
|
|
|
|
def test_explicit_missing_config_errors_instead_of_silent_defaults(tmp_path):
|
|
with pytest.raises(FileNotFoundError, match="does not exist"):
|
|
load_config(tmp_path / "nope.toml")
|