/** * USER_JOURNEYS.md §9 — host locks/unlocks the event. We use the API for * the host action so the test isn't blocked on the host dashboard UI being * complete, but assert the SSE-driven "uploads gesperrt" banner appears * for a guest who's already viewing the feed. */ import { test, expect } from '../../fixtures/test'; test.describe('Host — event lock', () => { test('closing the event via API sets uploads_locked_at; opening clears it', async ({ host, api }) => { // The frontend doesn't (yet) render a per-guest "uploads locked" banner on // the feed — that's the journey §9 banner, currently a UX gap. We assert // the API + DB contract here and leave the banner check for once it ships. const BASE = process.env.E2E_FRONTEND_URL ?? 'http://localhost:3101'; await api.closeEvent(host.jwt); const evRes = await fetch(`${BASE}/api/v1/host/event`, { headers: { Authorization: `Bearer ${host.jwt}` }, }); expect(evRes.status).toBe(200); const body: any = await evRes.json(); expect(body.uploads_locked).toBe(true); await api.openEvent(host.jwt); const evRes2 = await fetch(`${BASE}/api/v1/host/event`, { headers: { Authorization: `Bearer ${host.jwt}` }, }); const body2: any = await evRes2.json(); expect(body2.uploads_locked).toBe(false); }); test.fixme('event-closed SSE renders a "uploads gesperrt" banner in the feed (planned UX)', async () => { // Currently no UI consumes the event-closed SSE on /feed. Add this banner // and flip fixme to test once it lands. }); // Regression for the review: likes/comments used to ignore uploads_locked_at, // so social writes still landed on a closed event. They now share the upload // handler's lock guard. test('a closed event rejects likes and comments', async ({ api, host, guest }) => { const BASE = process.env.E2E_FRONTEND_URL ?? 'http://localhost:3101'; const g = await guest('SocialLocked'); // Upload while still open so there's a target to interact with. const { uploadRaw } = await import('../../helpers/upload-client'); const { readFileSync } = await import('node:fs'); const { join } = await import('node:path'); const sample = join(process.cwd(), 'fixtures', 'media', 'sample.jpg'); const upRes = await uploadRaw(g.jwt, readFileSync(sample), { filename: 'x.jpg', contentType: 'image/jpeg', }); expect(upRes.status).toBe(201); const { id } = await upRes.json(); await api.closeEvent(host.jwt); const likeRes = await fetch(`${BASE}/api/v1/upload/${id}/like`, { method: 'POST', headers: { Authorization: `Bearer ${g.jwt}` }, }); expect(likeRes.status).toBe(403); const commentRes = await fetch(`${BASE}/api/v1/upload/${id}/comments`, { method: 'POST', headers: { Authorization: `Bearer ${g.jwt}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ body: 'sollte blockiert sein' }), }); expect(commentRes.status).toBe(403); }); });