import test from 'node:test'; import assert from 'node:assert/strict'; import { IncidentScheduler } from './IncidentScheduler.js'; function createMemorySave(state) { return { state: structuredClone(state), get() { return this.state; }, set(partial) { const nextState = { ...this.state }; for (const [key, value] of Object.entries(partial)) { if (value && typeof value === 'object' && !Array.isArray(value) && nextState[key] && typeof nextState[key] === 'object' && !Array.isArray(nextState[key])) { nextState[key] = { ...nextState[key], ...structuredClone(value) }; } else { nextState[key] = structuredClone(value); } } this.state = nextState; return this.state; } }; } function createScheduler({ phase = 'unease', quests = [] } = {}) { const sentMail = []; const save = createMemorySave({ shift_started_at: '2026-04-25T11:00:00Z', world_flags: [], pressure: {}, incidents: {} }); const scheduler = new IncidentScheduler({ loader: { pressureProfiles: new Map([ ['kowalski_phase_2', { id: 'kowalski_phase_2', trigger_phase: 'unease', label: 'Dave Kowalski — Phase 2', escalation_steps: [ { trigger_after_seconds: 0, notification: 'Phase pressure test message.', notification_severity: 'info', sender: 'Dave Kowalski ', subject: 'Phase pressure test' } ] }] ]), incidents: new Map(), get(type, id) { if (type === 'quests') { return { id, ticket_id: 'T099' }; } return null; } }, email: { send(payload) { sentMail.push(payload); return payload; } }, quests: { getAllEntries() { return quests; } }, save, phaseTracker: { getPhase() { return phase; } }, tickets: { setPriority() {} }, validator: { async evaluateRule() { return { passed: false, failures: ['not-applicable'] }; } }, now: () => new Date('2026-04-25T12:05:00Z').getTime(), tickSeconds: 30 }); return { scheduler, save, sentMail }; } test('IncidentScheduler fires trigger_phase pressure profile when narrative phase matches', async () => { const { scheduler, save, sentMail } = createScheduler(); await scheduler.tick(); assert.equal(sentMail.length, 1); assert.equal(sentMail[0].from, 'Dave Kowalski '); assert.equal(sentMail[0].subject, 'Phase pressure test'); assert.equal(sentMail[0].body, 'Phase pressure test message.'); assert.deepEqual(save.get().pressure.kowalski_phase_2.fired_step_indexes, [0]); }); test('IncidentScheduler does not fire trigger_phase pressure profile when narrative phase differs', async () => { const { scheduler, save, sentMail } = createScheduler({ phase: 'stability' }); await scheduler.tick(); assert.equal(sentMail.length, 0); assert.deepEqual(save.get().pressure, {}); }); test('IncidentScheduler trigger_phase pressure profile is independent of active quest pressure_profile fields', async () => { const { scheduler, save, sentMail } = createScheduler({ quests: [[ 'Q099', { state: 'active', started_at: '2026-04-25T11:30:00Z' } ]] }); await scheduler.tick(); assert.equal(sentMail.length, 1); assert.equal(sentMail[0].subject, 'Phase pressure test'); assert.deepEqual(save.get().pressure.kowalski_phase_2.fired_step_indexes, [0]); assert.equal(save.get().pressure.Q099, undefined); }); test('IncidentScheduler handles missing pressureProfiles map without firing global pressure', async () => { const sentMail = []; const scheduler = new IncidentScheduler({ loader: { incidents: new Map(), get() { return null; } }, email: { send(payload) { sentMail.push(payload); return payload; } }, quests: { getAllEntries() { return []; } }, save: createMemorySave({ shift_started_at: '2026-04-25T11:00:00Z', world_flags: [], pressure: {}, incidents: {} }), phaseTracker: { getPhase() { return 'unease'; } }, tickets: { setPriority() {} }, validator: { async evaluateRule() { return { passed: false, failures: ['not-applicable'] }; } }, now: () => new Date('2026-04-25T12:05:00Z').getTime(), tickSeconds: 30 }); await assert.doesNotReject(async () => scheduler.tick()); assert.equal(sentMail.length, 0); }); test('IncidentScheduler only fires a trigger_phase pressure step once', async () => { const { scheduler, save, sentMail } = createScheduler(); await scheduler.tick(); await scheduler.tick(); assert.equal(sentMail.length, 1); assert.deepEqual(save.get().pressure.kowalski_phase_2.fired_step_indexes, [0]); });