import test from 'node:test'; import assert from 'node:assert/strict'; import { DEFAULTS, STRATEGIES, evaluate, landscape, recommendation, normalizeState } from '../model.mjs'; const near = (actual, expected, tolerance = 1e-7) => assert.ok(Math.abs(actual - expected) <= tolerance, `${actual} != ${expected}`); const find = (results, id) => results.find(result => result.id === id); test('defaults define four distinct strategies and a twelve-month illustrative baseline', () => { assert.equal(DEFAULTS.months, 12); assert.deepEqual(STRATEGIES.map(option => option.id), ['current', 'theatre', 'referral', 'combined']); const results = evaluate(); assert.equal(results.length, 4); near(find(results, 'current').completed, 1080); near(find(results, 'current').queue, 960); near(find(results, 'combined').incremental, 150); assert.equal(recommendation(results).id, 'combined'); }); test('every month conserves people and respects each throughput constraint', () => { const params = normalizeState({ budget: 2912345, capacity: 71.3, referralGain: 0.283, initialQueue: 53.7 }); for (const result of evaluate(params)) { let queue = params.initialQueue; let total = 0; for (const month of result.monthly) { near(month.openingQueue, queue); near(month.openingQueue + month.arrivals, month.completed + month.closingQueue); assert.ok(month.completed >= 0 && month.closingQueue >= 0); for (const bound of [month.available, month.referralThroughput, month.capacity, month.fundedSlots]) { assert.ok(month.completed <= bound + 1e-8); } queue = month.closingQueue; total += month.completed; } near(total, result.completed); near(queue, result.queue); near(params.initialQueue + params.arrivals * params.months, result.completed + result.queue); assert.ok(result.cost >= 0 && result.cost <= params.budget); } }); test('zero budget makes investment strategies infeasible and selects current service', () => { const results = evaluate({ budget: 0 }); for (const result of results) { near(result.cost, 0); near(result.incremental, 0); assert.equal(result.feasible, result.id === 'current'); } assert.equal(recommendation(results).id, 'current'); }); test('referral programme is infeasible below fixed cost and spends its fixed cost when enabled', () => { const below = evaluate({ budget: 599999 }); for (const id of ['referral', 'combined']) { assert.equal(find(below, id).feasible, false); near(find(below, id).cost, 0); assert.match(find(below, id).reason, /fixed/i); } const exact = evaluate({ budget: 600000, capacity: 140 }); for (const id of ['referral', 'combined']) { assert.equal(find(exact, id).feasible, true); near(find(exact, id).cost, 600000); near(find(exact, id).programmeCost, 600000); near(find(exact, id).additionalSlots, 0); } }); test('investment costs remain committed even when another bottleneck prevents benefit', () => { const results = evaluate({ capacity: 140, referralGain: 0, budget: 2400000 }); for (const id of ['theatre', 'referral', 'combined']) { near(find(results, id).incremental, 0); near(find(results, id).cost, 2400000); } assert.equal(recommendation(results).id, 'current'); }); test('baseline does not change with intervention budget or referral gain', () => { const first = find(evaluate({ budget: 0, referralGain: 0 }), 'current'); const last = find(evaluate({ budget: 6000000, referralGain: 0.4 }), 'current'); assert.deepEqual(first, last); }); test('theatre wins under a physical constraint and referral wins with existing spare capacity', () => { assert.equal(recommendation(evaluate({ capacity: 40, budget: 2400000 })).id, 'theatre'); const winner = recommendation(evaluate({ capacity: 150, budget: 2400000 })); assert.equal(winner.id, 'referral'); assert.deepEqual(winner.tiedIds, ['referral', 'combined']); assert.equal(recommendation(evaluate({ capacity: 90, budget: 2400000 })).id, 'combined'); }); test('referral strategy cannot expand physical capacity and theatre cannot change referrals', () => { const results = evaluate({ capacity: 75, budget: 6000000, referralGain: 0.4 }); const referral = find(results, 'referral'); const theatre = find(results, 'theatre'); assert.ok(referral.monthly.every(month => month.capacity === 75)); assert.ok(theatre.monthly.every(month => month.referralThroughput === 90)); near(theatre.additionalSlots, 6000000 / 12000 / 12); near(referral.additionalSlots, (6000000 - 600000) / 12000 / 12); }); test('more referral effect and more budget never reduce completions for a feasible strategy', () => { for (const id of ['theatre', 'referral', 'combined']) { let previous = -Infinity; for (const budget of [600000, 1200000, 2400000, 6000000]) { const result = find(evaluate({ budget, capacity: 80 }), id); assert.ok(result.completed >= previous - 1e-7); previous = result.completed; } previous = -Infinity; for (const referralGain of [0, 0.1, 0.2, 0.4]) { const result = find(evaluate({ referralGain, capacity: 120 }), id); assert.ok(result.completed >= previous - 1e-7); previous = result.completed; } } }); test('zero demand does not create patients or outcomes', () => { for (const result of evaluate({ arrivals: 0, initialQueue: 0 })) { near(result.completed, 0); near(result.queue, 0); assert.equal(result.bottleneck, 'Demand'); } }); test('tie tolerance uses lower cost then stable strategy order without implying strict superiority', () => { const results = STRATEGIES.map((option, index) => ({ ...option, feasible: true, incremental: 10 + (index === 3 ? 1e-9 : 0), cost: index === 0 ? 0 : 100 })); const chosen = recommendation(results); assert.equal(chosen.id, 'current'); assert.deepEqual(chosen.tiedIds, ['current']); assert.deepEqual(chosen.effectTiedIds, ['current', 'theatre', 'referral', 'combined']); assert.throws(() => recommendation([]), /feasible/i); }); test('normalization clamps finite numbers, preserves zero, and rejects invalid numeric inputs', () => { assert.deepEqual(normalizeState(), { ...DEFAULTS }); const result = normalizeState({ budget: -20, capacity: 300, referralGain: 9, baseReferral: -1, months: 2.8, initialQueue: 0 }); assert.equal(result.budget, 0); assert.equal(result.capacity, 180); assert.equal(result.referralGain, 0.4); assert.equal(result.baseReferral, 0); assert.equal(result.months, 2); assert.equal(result.initialQueue, 0); for (const value of [NaN, Infinity, -Infinity, '90', null, undefined]) { assert.throws(() => normalizeState({ capacity: value }), /capacity/); } }); test('landscape covers requested bounds and reproduces direct decisions at every cell', () => { const cells = landscape({ budget: 2400000 }, 4, 3); assert.equal(cells.length, 12); assert.deepEqual([cells[0].x, cells[0].y], [40, 0]); assert.deepEqual([cells.at(-1).x, cells.at(-1).y], [180, 0.4]); for (const [index, cell] of cells.entries()) { assert.equal(cell.index, index); const winner = recommendation(evaluate({ budget: 2400000, capacity: cell.x, referralGain: cell.y })); assert.equal(cell.winnerId, winner.id); near(cell.incremental, winner.incremental); } assert.throws(() => landscape({}, 0, 2), /dimension/i); assert.throws(() => landscape({}, 2, NaN), /dimension/i); }); test('default landscape reveals all three active strategy regions without artificial coloring', () => { const winners = new Set(landscape().map(cell => cell.winnerId)); for (const strategy of ['theatre', 'referral', 'combined']) assert.ok(winners.has(strategy)); }); test('all supported extremes remain finite, nonnegative, and budget-feasible', () => { for (const budget of [0, 599999, 600000, 6000000]) { for (const capacity of [40, 180]) { for (const referralGain of [0, 0.4]) { for (const result of evaluate({ budget, capacity, referralGain })) { for (const field of ['completed', 'incremental', 'cost', 'queue']) { assert.ok(Number.isFinite(result[field]), `${result.id}.${field} must be finite`); assert.ok(result[field] >= 0, `${result.id}.${field} must not be negative`); } assert.ok(result.cost <= budget); } } } } }); test('landscape preserves direct simulation totals and ties across extreme and fractional states', () => { const scenarios = [ { budget: 0 }, { budget: 599999 }, { budget: 600000 }, { budget: 6000000 }, { arrivals: 0, initialQueue: 0 }, { arrivals: 0, initialQueue: 900 }, { arrivals: 10000, initialQueue: 1000000, unitCost: 1, months: 120 }, { baseReferral: 0, baselineFundedSlots: 0, months: 1 }, { baseReferral: 1, initialQueue: 0, baselineFundedSlots: 10000, months: 120 }, { budget: 1234567.89, arrivals: 157.73, unitCost: 15678.32, initialQueue: 25.87, months: 79 }, { referralFixedCost: 0, budget: 1 }, { referralFixedCost: 6000000, budget: 5999999 }, ]; for (const state of scenarios) { for (const cell of landscape(state, 7, 5)) { const winner = recommendation(evaluate({ ...state, capacity: cell.x, referralGain: cell.y })); assert.equal(cell.winnerId, winner.id); assert.deepEqual(cell.tiedIds, winner.tiedIds); assert.deepEqual(cell.effectTiedIds, winner.effectTiedIds); for (const field of ['incremental', 'completed', 'cost', 'queue']) near(cell[field], winner[field]); } } }); test('large 120-month landscape remains interactive without constructing detailed monthly histories', () => { const started = performance.now(); const cells = landscape({ months: 120 }, 100, 100); const elapsed = performance.now() - started; assert.equal(cells.length, 10000); assert.ok(elapsed < 750, `10000-cell landscape took ${elapsed.toFixed(1)}ms; target <750ms`); });