const { pickOffers } = require('../src/utils/waitlist'); const q = (...qs) => qs.map((quantity, i) => ({ id: `e${i + 1}`, quantity })); describe('pickOffers', () => { test('offers people in order while places last', () => { expect(pickOffers(q(1, 2, 1), 3, true)).toEqual([ { entryId: 'e1', quantity: 1 }, { entryId: 'e2', quantity: 2 }, ]); }); test('nothing free, no offers', () => { expect(pickOffers(q(1), 0, true)).toEqual([]); }); test('partial offers: the first in line gets what is free, nobody jumps ahead', () => { expect(pickOffers(q(3, 1), 2, true)).toEqual([{ entryId: 'e1', quantity: 2 }]); }); test('without partial offers, skips to the next person who fits', () => { expect(pickOffers(q(3, 1, 1), 2, false)).toEqual([ { entryId: 'e2', quantity: 1 }, { entryId: 'e3', quantity: 1 }, ]); }); test('without partial offers, nobody fits', () => { expect(pickOffers(q(3, 4), 2, false)).toEqual([]); }); }); const { lineOpen, offerExpiry, lineName } = require('../src/utils/waitlist'); const { sessionPlacesFree } = require('../src/utils/sessions'); const at = (h) => new Date(Date.UTC(2026, 10, 4, h)); const sessions = [ { id: 's1', startsAt: at(8), endsAt: at(9), capacity: 10, cancelledAt: null }, { id: 's2', startsAt: at(10), endsAt: at(11), capacity: 4, cancelledAt: null }, { id: 's3', startsAt: at(12), endsAt: at(13), capacity: null, cancelledAt: null }, { id: 's4', startsAt: at(14), endsAt: at(15), capacity: 1, cancelledAt: at(1) }, ]; describe('sessionPlacesFree', () => { const booked = new Map([['s1', 7], ['s2', 4], ['s3', 50]]); test('a picked session: its own capacity', () => { expect(sessionPlacesFree(sessions, booked, 's1')).toBe(3); expect(sessionPlacesFree(sessions, booked, 's2')).toBe(0); }); test('a session without a capacity is unlimited', () => { expect(sessionPlacesFree(sessions, booked, 's3')).toBeNull(); }); test('whole-course: the fullest limited session, cancelled ones ignored', () => { expect(sessionPlacesFree(sessions, booked, null)).toBe(0); expect(sessionPlacesFree(sessions, new Map([['s1', 2], ['s2', 1]]), null)).toBe(3); }); }); describe('lineOpen', () => { const event = { scheduleType: 'sessions', attendanceMode: 'pick', endDate: at(13), sessions }; test('a session can be offered until it starts', () => { expect(lineOpen(event, 's2', at(9))).toBe(true); expect(lineOpen(event, 's2', at(10))).toBe(false); }); test('cancelled or unknown sessions are not offered', () => { expect(lineOpen(event, 's4', at(1))).toBe(false); expect(lineOpen(event, 'nope', at(1))).toBe(false); }); test('whole-course lines close when the course does', () => { expect(lineOpen(event, null, at(7))).toBe(true); expect(lineOpen(event, null, at(8))).toBe(false); expect(lineOpen({ scheduleType: 'single' }, null, at(20))).toBe(true); }); }); describe('offerExpiry', () => { test('the offer hours, but never after the session starts', () => { expect(offerExpiry(24, null, at(0))).toEqual(new Date(at(0).getTime() + 24 * 3600 * 1000)); expect(offerExpiry(24, sessions[1], at(0))).toEqual(at(10)); expect(offerExpiry(2, sessions[1], at(0))).toEqual(at(2)); }); }); describe('lineName', () => { test('names the session for picked lines', () => { expect(lineName({ eventOption: { name: 'Drop-in' }, variant: null, session: null })).toBe('Drop-in'); expect(lineName({ eventOption: { name: 'Drop-in' }, variant: { name: 'Adult' }, session: { ...sessions[1], label: 'Class 2' } }, sessions)) .toBe('Drop-in (Adult) — Class 2, Wed 4 Nov'); expect(lineName({ eventOption: { name: 'Drop-in' }, session: sessions[1] }, sessions)).toBe('Drop-in — Session 2, Wed 4 Nov'); }); });