/** * USER_JOURNEYS.md §12 — release the export, see status, download. * * We don't drive a real export job here (the compression takes too long * for E2E timing). Instead we forge the export-job rows via the db helper * and assert the API behavior + UI banner state. */ import { test, expect } from '../../fixtures/test'; import { ExportPage } from '../../page-objects'; const SLUG = 'e2e-test-event'; test.describe('Export — release and download', () => { test('/export shows the "not yet available" state before release', async ({ page, guest, signIn }) => { const g = await guest('PreRelease'); await signIn(page, g); const exportPage = new ExportPage(page); await exportPage.goto(); // The page shouldn't show download buttons before release. await expect(page.getByRole('button', { name: /^herunterladen$/i })).not.toBeVisible(); }); test('export status API reflects released flag', async ({ guest, db }) => { const g = await guest('ReleaseQuery'); let res = await fetch((process.env.E2E_FRONTEND_URL ?? 'http://localhost:3101') + '/api/v1/export/status', { headers: { Authorization: `Bearer ${g.jwt}` }, }); let body: any = await res.json(); expect(body.released).toBe(false); await db.setExportReleased(SLUG, true); await db.fakeExportJob(SLUG, 'zip', 'done'); await db.fakeExportJob(SLUG, 'html', 'done'); res = await fetch((process.env.E2E_FRONTEND_URL ?? 'http://localhost:3101') + '/api/v1/export/status', { headers: { Authorization: `Bearer ${g.jwt}` }, }); body = await res.json(); expect(body.released).toBe(true); expect(body.zip.status).toBe('done'); expect(body.html.status).toBe('done'); }); const base = process.env.E2E_FRONTEND_URL ?? 'http://localhost:3101'; // Browser downloads stream to disk via a top-level navigation, so the download // endpoint authenticates with a single-use ticket (no Bearer header). async function mintTicket(jwt: string): Promise { const res = await fetch(base + '/api/v1/export/ticket', { method: 'POST', headers: { Authorization: `Bearer ${jwt}` }, }); return (await res.json()).ticket; } test('ZIP download 404s when the export is not yet marked ready', async ({ guest, db }) => { const g = await guest('NotReady'); // Released flag set, but export_zip_ready is still false → must refuse, never serve. await db.setExportReleased(SLUG, true); await db.fakeExportJob(SLUG, 'zip', 'done'); const ticket = await mintTicket(g.jwt); const res = await fetch(base + '/api/v1/export/zip?ticket=' + encodeURIComponent(ticket)); // Pinned to 404 (not [404,200]): a 200 here would mean serving an export that was // never released for download — a data-exposure regression. This hits the // `!export_zip_ready` guard. expect(res.status).toBe(404); }); test('ZIP download 404s when marked ready but the file is missing on disk', async ({ guest, db }) => { const g = await guest('ReadyNoFile'); // Released AND ready, but no Gallery.zip on disk (we never ran a real export) → // the handler must 404 on the missing-file check, not 200/500 or serve a stale file. await db.setExportReleased(SLUG, true); await db.setExportZipReady(SLUG, true); await db.fakeExportJob(SLUG, 'zip', 'done'); const ticket = await mintTicket(g.jwt); const res = await fetch(base + '/api/v1/export/zip?ticket=' + encodeURIComponent(ticket)); expect(res.status).toBe(404); }); });