test(e2e): restore the download-side 404 coverage the mint pre-check displaced
Both tests in export.spec.ts are still named "ZIP download 404s…" but now assert only that `/export/ticket` refuses. That move was right — the mint pre-validates, and refusing there spends none of the guest's three daily downloads — but it left `resolve_export_file` unasserted on the download path itself, so deleting that check would not have turned anything red. It cannot be covered by minting against an already-dead archive, because the mint refuses first. The order has to be: real release → mint while healthy → retire the generation → download. Which is also precisely what happens to a ticket already in flight when the host takes a photo down mid-download. Carries its own positive control: the ticket is asserted to serve a 200 before the epoch moves, so the 404 afterwards cannot be green for the wrong reason — an expired or never-valid ticket would 404 too.
This commit is contained in:
73
e2e/specs/06-export/download-retired-epoch.spec.ts
Normal file
73
e2e/specs/06-export/download-retired-epoch.spec.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* A ticket minted against a LIVE archive must stop working the moment that archive is retired.
|
||||
*
|
||||
* This is the download-side half of the stale-keepsake guarantee, and it was left unasserted. Both
|
||||
* tests in `export.spec.ts` are named "ZIP download 404s…" but now assert only that the *mint*
|
||||
* refuses — correctly, since `/export/ticket` pre-validates and refusing there spends none of the
|
||||
* guest's three daily downloads. The consequence is that nothing exercised `resolve_export_file`
|
||||
* on the download path itself, so removing that check would not have turned anything red.
|
||||
*
|
||||
* It cannot be tested by minting against an already-dead archive (the mint refuses first), so the
|
||||
* order has to be: real release → mint while healthy → retire → download. Which is also exactly
|
||||
* what a host taking a photo down mid-download does to a ticket already in flight.
|
||||
*/
|
||||
import { test, expect } from '../../fixtures/test';
|
||||
import { seedUpload } from '../../helpers/seed';
|
||||
import { BASE } from '../../helpers/env';
|
||||
|
||||
const SLUG = 'e2e-test-event';
|
||||
|
||||
test.describe('Export — a retired generation cannot be downloaded', () => {
|
||||
test('a ticket minted before the epoch moved is refused at the download, not served stale', async ({
|
||||
host,
|
||||
db,
|
||||
}) => {
|
||||
test.setTimeout(60_000);
|
||||
const bearer = { Authorization: `Bearer ${host.jwt}` };
|
||||
|
||||
await seedUpload(host.jwt, { caption: 'about to go stale' });
|
||||
|
||||
const rel = await fetch(`${BASE}/api/v1/host/gallery/release`, {
|
||||
method: 'POST',
|
||||
headers: bearer,
|
||||
});
|
||||
expect(rel.status).toBe(204);
|
||||
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
const res = await fetch(`${BASE}/api/v1/export/status`, { headers: bearer });
|
||||
return (await res.json()).zip?.status;
|
||||
},
|
||||
{ timeout: 45_000, intervals: [500] }
|
||||
)
|
||||
.toBe('done');
|
||||
|
||||
// Mint while everything is healthy. This must succeed, or the assertion below proves nothing —
|
||||
// a 404 on a ticket that was never valid would be green for the wrong reason.
|
||||
const mint = await fetch(`${BASE}/api/v1/export/ticket?kind=zip`, {
|
||||
method: 'POST',
|
||||
headers: bearer,
|
||||
});
|
||||
expect(mint.status, 'the ticket must be mintable while the archive is live').toBe(200);
|
||||
const { ticket } = await mint.json();
|
||||
expect(ticket).toBeTruthy();
|
||||
|
||||
// And it genuinely works right now — the positive control for the negative below.
|
||||
const before = await fetch(`${BASE}/api/v1/export/zip?ticket=${encodeURIComponent(ticket)}`);
|
||||
expect(before.status, 'the ticket must serve the archive while it is current').toBe(200);
|
||||
|
||||
// Now retire the generation, which is what a reopen or a post-release takedown does.
|
||||
await db.setExportZipReady(SLUG, false);
|
||||
|
||||
// The SAME ticket — still unexpired, still under its redemption cap, session still valid —
|
||||
// must now be refused. Readiness is derived at READ time from `job.epoch = event.export_epoch`,
|
||||
// so this is the check that stops a superseded archive being served to a guest who happened to
|
||||
// hold a ticket when the host moderated.
|
||||
const after = await fetch(`${BASE}/api/v1/export/zip?ticket=${encodeURIComponent(ticket)}`);
|
||||
expect(
|
||||
after.status,
|
||||
'a retired archive must 404 on the download path, not be served from a still-valid ticket'
|
||||
).toBe(404);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user