'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const { createState, reconcile, summarize } = require('../src/scheduler'); const options = { maxChannels: 3, startGraceMs: 24 * 60 * 60 * 1000 }; function makeEvent(id, startIso, endIso) { return { key: id, title: id, league: 'Jupiler Pro League', language: 'nl', startAt: new Date(startIso), endAt: new Date(endIso), sources: [{ streamId: id, name: id, candidates: [`http://upstream/${id}.ts`] }], missingCount: 0, }; } function current(...events) { return new Map(events.map((event) => [event.key, event])); } function slotKeys(state) { return state.slots.map((slot) => slot.eventKey); } test('fills the first three slots by start time and queues the rest', () => { const state = createState(3); const events = current( makeEvent('A', '2026-11-06T16:00:00Z', '2026-11-06T20:00:00Z'), makeEvent('B', '2026-11-06T16:30:00Z', '2026-11-06T20:30:00Z'), makeEvent('C', '2026-11-06T17:00:00Z', '2026-11-06T21:00:00Z'), makeEvent('D', '2026-11-06T17:30:00Z', '2026-11-06T21:30:00Z'), makeEvent('E', '2026-11-06T18:00:00Z', '2026-11-06T22:00:00Z') ); reconcile(state, events, Date.parse('2026-11-06T18:00:00Z'), options); assert.deepEqual(slotKeys(state), ['A', 'B', 'C']); const summary = summarize(state); assert.deepEqual(summary.queue.map((event) => event.key), ['D', 'E']); }); test('a freed slot is filled by the oldest waiting event', () => { const state = createState(3); const events = current( makeEvent('A', '2026-11-06T16:00:00Z', '2026-11-06T20:00:00Z'), makeEvent('B', '2026-11-06T16:30:00Z', '2026-11-06T20:30:00Z'), makeEvent('C', '2026-11-06T17:00:00Z', '2026-11-06T21:00:00Z'), makeEvent('D', '2026-11-06T17:30:00Z', '2026-11-06T21:30:00Z'), makeEvent('E', '2026-11-06T18:00:00Z', '2026-11-06T22:00:00Z') ); reconcile(state, events, Date.parse('2026-11-06T18:00:00Z'), options); assert.deepEqual(slotKeys(state), ['A', 'B', 'C']); reconcile(state, events, Date.parse('2026-11-06T20:01:00Z'), options); assert.deepEqual(slotKeys(state), ['D', 'B', 'C']); }); test('an existing assignment is not preempted by an earlier event', () => { const state = createState(3); const events = current( makeEvent('A', '2026-11-06T16:00:00Z', '2026-11-06T20:00:00Z'), makeEvent('B', '2026-11-06T16:30:00Z', '2026-11-06T20:30:00Z'), makeEvent('C', '2026-11-06T17:00:00Z', '2026-11-06T21:00:00Z') ); reconcile(state, events, Date.parse('2026-11-06T18:00:00Z'), options); const withEarlier = current( ...events.values(), makeEvent('X', '2026-11-06T17:45:00Z', '2026-11-06T21:45:00Z') ); reconcile(state, withEarlier, Date.parse('2026-11-06T18:10:00Z'), options); assert.deepEqual(slotKeys(state), ['A', 'B', 'C']); }); test('an assigned event keeps its slot until its end even when it disappears', () => { const state = createState(3); const a = makeEvent('A', '2026-11-06T16:00:00Z', '2026-11-06T20:00:00Z'); const b = makeEvent('B', '2026-11-06T16:30:00Z', '2026-11-06T20:30:00Z'); reconcile(state, current(a, b), Date.parse('2026-11-06T18:00:00Z'), options); assert.deepEqual(slotKeys(state), ['A', 'B', null]); reconcile(state, current(), Date.parse('2026-11-06T19:00:00Z'), options); reconcile(state, current(), Date.parse('2026-11-06T19:30:00Z'), options); assert.deepEqual(slotKeys(state), ['A', 'B', null]); reconcile(state, current(), Date.parse('2026-11-06T20:31:00Z'), options); assert.deepEqual(slotKeys(state), [null, null, null]); }); test('an unassigned event that disappears twice is dropped from the queue', () => { const state = createState(3); const events = current( makeEvent('A', '2026-11-06T16:00:00Z', '2026-11-06T20:00:00Z'), makeEvent('B', '2026-11-06T16:30:00Z', '2026-11-06T20:30:00Z'), makeEvent('C', '2026-11-06T17:00:00Z', '2026-11-06T21:00:00Z'), makeEvent('D', '2026-11-06T17:30:00Z', '2026-11-06T21:30:00Z'), makeEvent('E', '2026-11-06T18:00:00Z', '2026-11-06T22:00:00Z') ); reconcile(state, events, Date.parse('2026-11-06T18:00:00Z'), options); const withoutD = current( events.get('A'), events.get('B'), events.get('C'), events.get('E') ); reconcile(state, withoutD, Date.parse('2026-11-06T18:20:00Z'), options); reconcile(state, withoutD, Date.parse('2026-11-06T18:40:00Z'), options); reconcile(state, withoutD, Date.parse('2026-11-06T20:01:00Z'), options); assert.deepEqual(slotKeys(state), ['E', 'B', 'C']); });