fix: the tagger pre-selected a group, which is how an album reaches the wrong chat

The first group in the account's listing was checked by default, and the
first group in this account's listing is a test group. Somebody who typed
the names, hit save and never looked at the radios would have scoped a
private album to the wrong chat -- silently, because the commands then
simply do nothing in the chat they were meant for. Nothing is pre-selected
now, and saving without a group is refused.

Also replaces a test of mine that failed one run in sixteen: it drew a
two-way random choice five times and demanded both outcomes. The property
worth pinning is that a choice lands on a reading that *has* photos, which
is deterministic when only one of them does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017w1HZ3cZ7neL3rFdQ6VMBA
This commit is contained in:
Krzysztof Rudnicki 2026-08-30 22:41:42 +02:00
parent 1f4080e75d
commit 3b3a78820a
2 changed files with 23 additions and 9 deletions

View File

@ -136,13 +136,25 @@ async def test_a_name_nobody_has_gets_the_same_answer(
async def test_a_choice_lands_on_the_reading_that_has_a_photo(
tmp_path: Path, transport: FakeTransport, store: Store, config: Config
) -> None:
"""p2 has no photo of their own, so "either of them" must not fail."""
pack = load(
pack_fixture.build(tmp_path / "pack", tags={"gamma.png": ["p3"], "both.png": ["p1", "p2"]})
)
"""p2 has no photo at all, so "either of them" must land on p3 every time.
Deterministic on purpose. Asserting that a random choice eventually
produces both readings is a test that fails one run in sixteen, and this
repo has already shipped one bug because a random API was believed after
a single call.
"""
pack = load(pack_fixture.build(tmp_path / "pack", tags={"gamma.png": ["p3"]}))
for _ in range(5):
await _run(pack, transport, store, config, "!ktokolwiek")
assert {message.filename for message in transport.sent} == {"gamma.png", "both.png"}
assert [message.filename for message in transport.sent] == ["gamma.png"] * 5
async def test_a_choice_with_nothing_behind_either_reading_is_answered(
tmp_path: Path, transport: FakeTransport, store: Store, config: Config
) -> None:
pack = load(pack_fixture.build(tmp_path / "pack", tags={"alfa.png": ["p1"]}))
await _run(pack, transport, store, config, "!ktokolwiek")
assert transport.texts == [NO_PHOTO]
async def test_a_video_keeps_being_a_video(

View File

@ -76,8 +76,6 @@ const api = (path, body) => fetch(path, body ? {
let state = null;
let at = 0;
function key(name, index) { return name || `p${index + 1}`; }
function parseSetup(text) {
const people = [], combos = [];
const byName = new Map();
@ -110,8 +108,11 @@ function drawGroups(known, chosen) {
box.innerHTML = '<input id="rawgroup" style="width:100%" placeholder="group.…=">';
return;
}
box.innerHTML = known.map((g, i) => `<label class="group">
<input type="radio" name="group" value="${g.id}" ${chosen.includes(g.id) || (!chosen.length && i === 0) ? "checked" : ""}>
// Nothing is pre-selected. The first group in the listing is whichever
// one the account happens to return first -- checking it by default is a
// one-click way to scope a private album to the wrong chat.
box.innerHTML = known.map((g) => `<label class="group">
<input type="radio" name="group" value="${g.id}" ${chosen.includes(g.id) ? "checked" : ""}>
${g.name || "(bez nazwy)"} <span class="hint">${g.id.slice(0, 18)}…</span></label>`).join("");
}
@ -181,6 +182,7 @@ async function start() {
try {
const {people, combos} = parseSetup(document.getElementById("names").value);
if (!people.length) { throw new Error("dodaj przynajmniej jedną osobę"); }
if (!chosenGroups().filter(Boolean).length) { throw new Error("wybierz grupę"); }
await api("/api/setup", {groups: chosenGroups(), people, combos});
location.reload();
} catch (error) {