Files
EventSnap/e2e/loadtest/confirm-diashow-fix.mjs
MechaCat02 9b8698f86b test(loadtest): 100-guest / 1000-image stress harness
HTTP-level load driver simulating ~100 guests uploading ~1000 images in bursts
over a window, plus SSE viewers and one real browser on /diashow. Correlates
upload→upload-processed (pipeline latency), waits for the compression backlog
to drain against DB ground truth, and emits per-status/latency metrics with
pass/fail flags. Includes a realistic-JPEG generator and a diashow-SSE
regression check (confirm-diashow-fix.mjs). Run artifacts are gitignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 17:29:20 +02:00

79 lines
3.9 KiB
JavaScript

// End-to-end confirmation of the diashow SSE fix, reproducing the ORIGINAL failure:
// kiosk opens /diashow directly BEFORE any photos exist, then a guest uploads.
// Pre-fix: stream never opens, display stuck on "Noch keine Beiträge" forever.
// Post-fix: stream opens on mount, the uploaded photo appears live.
import { chromium } from '@playwright/test';
import { readFileSync } from 'node:fs';
const BASE = 'http://localhost:3101';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const j = (r) => r.json();
const adminLogin = () =>
fetch(`${BASE}/api/v1/admin/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: 'admin-test-pw' }) }).then(j).then((b) => b.jwt);
const join = (name) =>
fetch(`${BASE}/api/v1/join`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ display_name: name }) }).then(j);
async function truncate(admin) {
await fetch(`${BASE}/api/v1/admin/__truncate`, { method: 'POST', headers: { Authorization: `Bearer ${admin}` } });
}
async function upload(jwt) {
const form = new FormData();
form.append('file', new Blob([readFileSync('/tmp/eventsnap-loadtest/photos/photo_000.jpg')], { type: 'image/jpeg' }), 'live.jpg');
form.append('caption', 'LIVE-PROBE');
const r = await fetch(`${BASE}/api/v1/upload`, { method: 'POST', headers: { Authorization: `Bearer ${jwt}` }, body: form });
return (await r.json()).id;
}
// Reproduce the failing scenario: empty event, then open /diashow directly.
let admin = await adminLogin();
await truncate(admin);
admin = await adminLogin();
const showcase = await join('Showcase Display');
const guest = await join('Uploading Guest');
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 } });
const page = await ctx.newPage();
const streamReqs = [];
page.on('request', (req) => { if (req.url().includes('/stream')) streamReqs.push(req.url().replace(BASE, '')); });
await page.goto(`${BASE}/join`, { waitUntil: 'domcontentloaded' });
await page.evaluate((g) => {
localStorage.setItem('eventsnap_jwt', g.jwt);
localStorage.setItem('eventsnap_pin', g.pin);
localStorage.setItem('eventsnap_user_id', g.user_id);
localStorage.setItem('eventsnap_display_name', 'Showcase');
localStorage.setItem('eventsnap_guide_seen', '1');
}, showcase);
// Open /diashow DIRECTLY (never via /feed) on an EMPTY event.
await page.goto(`${BASE}/diashow`, { waitUntil: 'domcontentloaded' });
await sleep(2500);
const emptyBefore = (await page.locator('text=Noch keine Beiträge').count()) > 0;
const streamOpened = streamReqs.length > 0;
console.log(`\n1) direct /diashow on empty event:`);
console.log(` "Noch keine Beiträge" shown: ${emptyBefore} (expected: true)`);
console.log(` SSE stream opened: ${streamOpened} ${streamOpened ? '('+streamReqs.join(', ')+')' : ''} (expected: true — this is the fix)`);
// Now a guest uploads a photo while the display is open.
console.log(`\n2) guest uploads a photo (display already open)…`);
await upload(guest.jwt);
// Wait for compression → upload-processed SSE → debounced refresh → slide appears.
let appeared = false;
for (let i = 0; i < 15; i++) {
await sleep(1000);
const stillEmpty = (await page.locator('text=Noch keine Beiträge').count()) > 0;
const imgs = await page.locator('img').count();
if (!stillEmpty && imgs > 0) { appeared = true; console.log(` photo appeared live after ~${i + 1}s (img rendered, placeholder gone)`); break; }
}
if (!appeared) console.log(` ✗ photo did NOT appear within 15s`);
await page.screenshot({ path: 'results/diashow-fix-confirm.png' });
await browser.close();
console.log(`\n════ VERDICT ════`);
console.log(`SSE opens on direct /diashow: ${streamOpened ? 'YES ✓' : 'NO ✗'}`);
console.log(`Live photo appears on showcase: ${appeared ? 'YES ✓' : 'NO ✗'}`);
console.log(streamOpened && appeared ? 'FIX CONFIRMED' : 'FIX NOT confirmed');