const { transferBlocker } = require('../src/utils/ticketTransfer'); const { judgeSyncedScan } = require('../src/utils/scanLocks'); describe('transferBlocker', () => { const now = new Date('2026-10-01T10:00:00Z'); const ticket = { userId: 'payer', holderUserId: 'holder', usages: [], registrationStatus: 'paid', event: { startDate: '2026-10-05T08:00:00Z', allowTicketTransfer: true }, }; const user = (id, role = 'user') => ({ id, role }); test('the payer and the holder can transfer', () => { expect(transferBlocker(ticket, user('payer'), now)).toBeNull(); expect(transferBlocker(ticket, user('holder'), now)).toBeNull(); }); test('anyone else needs to be a supervisor', () => { expect(transferBlocker(ticket, user('someone'), now).status).toBe(403); expect(transferBlocker(ticket, user('staffer', 'staff'), now).status).toBe(403); expect(transferBlocker(ticket, user('sup', 'supervisor'), now)).toBeNull(); }); test('not once the event has started', () => { expect(transferBlocker({ ...ticket, event: { ...ticket.event, startDate: '2026-10-01T09:00:00Z' } }, user('sup', 'supervisor'), now).message).toMatch(/before the event starts/); }); test('not once any of it has been scanned', () => { expect(transferBlocker({ ...ticket, usages: [{ id: 'u1' }] }, user('payer'), now).message).toMatch(/already been used/); }); test('not for a cancelled registration', () => { expect(transferBlocker({ ...ticket, registrationStatus: 'cancelled' }, user('payer'), now)).not.toBeNull(); }); test('turned off per event, except for supervisors', () => { const off = { ...ticket, event: { ...ticket.event, allowTicketTransfer: false } }; expect(transferBlocker(off, user('payer'), now).message).toMatch(/turned off/); expect(transferBlocker(off, user('admin', 'admin'), now)).toBeNull(); }); }); describe('offline sync of a transferred code', () => { test('is flagged and not recorded', () => { expect(judgeSyncedScan({ transferred: true }, 1)).toEqual({ redeem: 0, conflict: 'transferred' }); }); });