fix(db): guest_emails/lobby ALTERs ran before scheduled_meetings existed + e2e drift

Migration prep. Two real fixes surfaced while building a regression harness:

1. db.js: the `guest_emails` and `lobby` ALTER TABLEs sat at lines 241/244, BEFORE
   scheduled_meetings is CREATEd (line 299). On a FRESH database the ALTER fails
   (no table yet), is swallowed by the try/catch, and the columns are never added —
   so a brand-new deploy is missing them and scheduling with guests crashes. Prod
   escaped it only by incremental deploy history. Moved both ALTERs to after the
   CREATE. (The upcoming Postgres schema defines every column up front, so this
   whole class of ordering bug goes away there.)

2. test/e2e.js: /api/meetings returns paginated `{list, pastTotal, page, pageSize}`
   now, not a bare array — updated three `.data.find` → `.data.list.find`.

No prod behaviour change (prod already has the columns; ALTERs are idempotent).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-24 19:43:46 +05:30
parent ee95f594c6
commit b2c4b10e41
2 changed files with 10 additions and 9 deletions
+3 -3
View File
@@ -289,21 +289,21 @@ function nextMsg(ws, type, timeout = 3000) {
const schP = await call('/api/v1/meetings/schedule', { title: 'Synced', scheduledAt: Date.now() + 3600000, participants: [bobId] }, cookie);
check('meeting scheduled with participants', schP.status === 200 && Array.isArray(schP.data.participants) && schP.data.participants.includes(bobId));
const bobMeetings = await get('/api/v1/meetings', bobCookie);
const bm = bobMeetings.data.find((m) => m.id === schP.data.id);
const bm = bobMeetings.data.list.find((m) => m.id === schP.data.id);
check('invited participant sees the meeting', !!bm && bm.isHost === false);
const schPush = await nextMsg(bobWs, 'chat-message');
check('schedule announced in the group chat', schPush.message && /Scheduled a call/.test(schPush.message.body));
const pastM = await call('/api/v1/meetings/schedule', { group: gid, title: 'Old Standup', scheduledAt: Date.now() - 2 * 60 * 60 * 1000 }, cookie);
check('past meeting scheduling is rejected', pastM.status === 400); // can't schedule in the past (#1)
const mlist = await get('/api/v1/meetings', cookie);
const schUp = mlist.data.find((m) => m.id === sched.data.id);
const schUp = mlist.data.list.find((m) => m.id === sched.data.id);
check('meetings list buckets upcoming', mlist.status === 200 && schUp && schUp.status === 'upcoming');
const sm = wsClient(); await new Promise((r) => sm.on('open', r));
sm.send(JSON.stringify({ type: 'meeting-join', room: sched.data.roomCode, name: 'Admin' }));
const smJoined = await nextMsg(sm, 'meeting-joined');
check('scheduled meeting joinable by code (room created lazily)', !!smJoined.peerId && smJoined.room === sched.data.roomCode);
const mlist2 = await get('/api/v1/meetings', cookie);
const upRun = mlist2.data.find((m) => m.id === sched.data.id);
const upRun = mlist2.data.list.find((m) => m.id === sched.data.id);
check('scheduled meeting shows running while a peer is connected', !!upRun && upRun.status === 'running' && upRun.inCall >= 1);
sm.close();
const cancelBob = await call('/api/v1/meetings/cancel', { id: sched.data.id }, bobCookie);