diff --git a/artifacts/api-server/tests/executive-meetings.test.mjs b/artifacts/api-server/tests/executive-meetings.test.mjs index d9778f7e..da750b95 100644 --- a/artifacts/api-server/tests/executive-meetings.test.mjs +++ b/artifacts/api-server/tests/executive-meetings.test.mjs @@ -1217,6 +1217,61 @@ test("Reorder: rejects orderedIds containing a cancelled meeting with code cance "rejection must be reported with the cancelled_in_reorder code"); }); +test("Reorder: handles a day with a null-startTime meeting deterministically", async () => { + // Per the slot-swap sort, rows with non-null startTime come first + // (chronological), and null-startTime rows come last. After a reorder + // of [null-row, A, B], the null row should land in the LAST slot + // (whatever its startTime/dailyNumber were originally), and A/B should + // get the first two chronological slots. This proves the slot-swap + // does not crash on nulls and produces a stable order. + const reorderDate = "2050-06-10"; + const a = await api(adminCookie, "POST", "/api/executive-meetings", { + titleAr: "أ", titleEn: "A", meetingDate: reorderDate, + startTime: "09:00", endTime: "09:30", + }); + const b = await api(adminCookie, "POST", "/api/executive-meetings", { + titleAr: "ب", titleEn: "B", meetingDate: reorderDate, + startTime: "10:00", endTime: "10:30", + }); + const n = await api(adminCookie, "POST", "/api/executive-meetings", { + titleAr: "ن", titleEn: "Null-time", meetingDate: reorderDate, + // startTime/endTime intentionally omitted -> null in DB + }); + const A = await a.json(); created.meetingIds.push(A.id); + const B = await b.json(); created.meetingIds.push(B.id); + const N = await n.json(); created.meetingIds.push(N.id); + + // Ask: put null-row first, then A, then B + const r = await api(adminCookie, "POST", + "/api/executive-meetings/reorder", + { meetingDate: reorderDate, orderedIds: [N.id, A.id, B.id] }); + assert.equal(r.status, 200, "reorder with a null-time row must succeed"); + + const after = await pool.query( + `SELECT id, daily_number, start_time, end_time + FROM executive_meetings + WHERE meeting_date = $1 + ORDER BY daily_number`, + [reorderDate], + ); + // Slots sorted by startTime (nulls last) were: + // slot[0] = (09:00, 09:30, dn=A) for A originally + // slot[1] = (10:00, 10:30, dn=B) for B originally + // slot[2] = (null, null, dn=N) for N originally + // Assignment N->slot[0], A->slot[1], B->slot[2]: + const byId = Object.fromEntries(after.rows.map((r) => [r.id, r])); + assert.equal(byId[N.id].start_time, "09:00:00", + "null-time row should inherit the first chronological slot's startTime"); + assert.equal(byId[A.id].start_time, "10:00:00", + "A should inherit the second chronological slot's startTime"); + assert.equal(byId[B.id].start_time, null, + "B should inherit the originally-null third slot's startTime"); + // dailyNumbers must be unique 1..3 across the day + const dns = after.rows.map((r) => r.daily_number).sort(); + assert.deepEqual(dns, [1, 2, 3].sort(), + "dailyNumbers across the day must remain a unique 1..N permutation"); +}); + test("Reorder: rejects an incomplete-day request (orderedIds missing some)", async () => { const reorderDate = "2050-02-20"; const a = await api(adminCookie, "POST", "/api/executive-meetings", { diff --git a/artifacts/tx-os/public/opengraph.jpg b/artifacts/tx-os/public/opengraph.jpg index 756fe47f..d08d6341 100644 Binary files a/artifacts/tx-os/public/opengraph.jpg and b/artifacts/tx-os/public/opengraph.jpg differ