test: replace coverage that could not fail with coverage that can

`backend/tests/` follows a house rule of copying production SQL character-for-
character rather than calling `src/`, because the crate is a binary and nothing
in it is importable from an integration test. For pinning behaviour that already
existed that is a defensible trade. Applied to a NEW fix whose only coverage is
the copy, it proves nothing: the fix and its test become two independent
implementations, and deleting the fix leaves the test green.

`audit_names.rs` did exactly that. It never called `audit::record` — it
reimplemented `resolve_names` and the INSERT inside the test file, down to a
hardcoded `.bind("host")`, and then asserted `actor_role == "host"` against its
own literal. That assertion could not fail for any change to the code it named,
and grep confirmed there was no other coverage of the audit-name work anywhere.

Moved into `#[cfg(test)]` inside `services/audit.rs`, where the real function IS
callable. CI already runs `cargo test --all-features` with a live DATABASE_URL,
so `#[sqlx::test]` works there; verified all four run and pass. The role
assertion now compares against `UserRole::as_str()` itself rather than a literal,
so it tracks a rename instead of pretending to, plus an explicit `assert_ne!`
against the Debug spelling.

Also:

- `retry-after-release.spec.ts` filtered the feed on `u.id === original.id` to
  prove "no second row was created". A duplicate gets a fresh uuid and could
  never match, so the filter yielded exactly 1 whether the gallery held one copy
  or five. Counts by uploader now, with the original's identity asserted
  separately. (The rest of that spec is sound — its 403 control and replay-id
  check both fail if the header fast-path is reverted.)

- `upload_after_release_commits_sees_the_lock_and_is_rejected` claimed the
  handler answers `UploadsLocked`. It answers `GalleryReleased` since the check
  order was inverted on this branch, and the test asserts no variant at all.
  Documented what it actually covers (the locked READ) and where the ordering IS
  covered (two e2e specs).

- Two `// SRC:` pointers had drifted ~130 lines into unrelated code, which is how
  a hand-copied fixture silently stops matching its original. Now named, not
  numbered.

- `emptyOutDir: false` claimed a failed viewer build "leaves the last good
  artifact in place". True for the `generateBundle` error, false for the newer
  `writeBundle` assertion, which fires after Vite has already written the file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
fabi
2026-08-13 19:46:04 +02:00
parent ee70eec094
commit cfc8bd0016
5 changed files with 211 additions and 172 deletions

View File

@@ -75,11 +75,21 @@ test.describe('Upload — a retry after release replays instead of refusing', ()
);
// 4. And no second row was created — the whole point of the key.
//
// Counted by UPLOADER, not by `id`. Filtering on `u.id === original.id` looks like a duplicate
// check and is not one: a duplicate row gets a fresh uuid, so it could never match, and the
// filter yields exactly 1 whether the gallery holds one copy or five. This guest uploaded once
// successfully ('a.jpg'); 'b.jpg' was refused at step 2 and the retry must have replayed rather
// than stored, so their total must be exactly one.
const feed = await fetch(`${BASE}/api/v1/feed?limit=100`, {
headers: { Authorization: `Bearer ${g.jwt}` },
});
const items: any[] = (await feed.json()).uploads ?? [];
const mine = items.filter((u) => u.id === original.id);
expect(mine.length, 'the photo must appear exactly once').toBe(1);
const mine = items.filter((u) => u.user_id === g.userId);
expect(
mine.length,
`the retry must not have stored a second copy; got ${mine.map((u) => u.id).join(', ')}`
).toBe(1);
expect(mine[0].id, 'and the one that exists is the original').toBe(original.id);
});
});