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:
@@ -15,7 +15,9 @@ use common::*;
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// SRC: `handlers/upload.rs:313-322` — the guarded quota increment, verbatim.
|
||||
/// SRC: `handlers/upload.rs::create_upload` — the guarded quota increment, verbatim.
|
||||
/// (Named, not line-numbered: the previous pointer drifted by ~130 lines and landed in unrelated
|
||||
/// code, which is how a hand-copied fixture silently stops matching its original.)
|
||||
/// Returns `rows_affected()`; the handler aborts the whole upload tx when this is 0.
|
||||
async fn quota_inc(exec: impl sqlx::PgExecutor<'_>, user_id: Uuid, size: i64, limit: i64) -> u64 {
|
||||
sqlx::query(
|
||||
@@ -146,7 +148,8 @@ async fn quota_guard_is_atomic_under_concurrent_transactions(pool: PgPool) {
|
||||
// 6. The `FOR SHARE` upload lock vs. the release
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// SRC: `handlers/upload.rs:297-303` — the in-transaction re-check under a row lock, verbatim.
|
||||
/// SRC: `handlers/upload.rs::create_upload` — the in-transaction `FOR SHARE` re-check, verbatim.
|
||||
/// (Named, not line-numbered — see the note on `quota_inc`.)
|
||||
async fn lock_and_read_event(
|
||||
tx: &mut sqlx::PgConnection,
|
||||
event_id: Uuid,
|
||||
@@ -254,12 +257,19 @@ async fn for_share_upload_lock_serializes_against_release(pool: PgPool) {
|
||||
}
|
||||
|
||||
/// The other side of the same lock: once the release has COMMITTED, the next upload's `FOR SHARE`
|
||||
/// re-read sees `export_released_at` set and the handler rejects it with `UploadsLocked`.
|
||||
/// re-read sees `export_released_at` set and the handler bails out.
|
||||
///
|
||||
/// PREVENTS: the same lost photo, on the losing side of the race — a photo committing AFTER the
|
||||
/// export snapshot would be in the live feed but missing from the keepsake. Rejecting is the correct
|
||||
/// outcome, and it is reversible: `UploadsLocked` (not Forbidden) tells the client to keep the blob
|
||||
/// and resume when the host reopens.
|
||||
/// outcome, and it is reversible: the client keeps the blob and resumes when the host reopens.
|
||||
///
|
||||
/// SCOPE, because the name overstates it: this asserts only what the LOCKED READ observes. It does
|
||||
/// not go through the handler, so it says nothing about which error the handler picks. That
|
||||
/// distinction is load-bearing — `create_upload` answers a released gallery with `GalleryReleased`
|
||||
/// and a plain lock with `UploadsLocked`, in that order, and the two drive different client
|
||||
/// behaviour (a `reopen` park vs. a retry). The ordering is covered end-to-end by
|
||||
/// `e2e/specs/10-flow-review/upload-lock-code.spec.ts` and `02-upload/retry-after-release.spec.ts`;
|
||||
/// this test's doc used to claim `UploadsLocked` outright and was simply wrong after that split.
|
||||
#[sqlx::test]
|
||||
async fn upload_after_release_commits_sees_the_lock_and_is_rejected(pool: PgPool) {
|
||||
let event_id = seed_event(&pool, "wedding").await;
|
||||
|
||||
Reference in New Issue
Block a user