import assert from 'node:assert/strict' import { test } from 'node:test' import fc from 'fast-check' import { type BudgetState, canSpend, DEFAULT_BUDGET, emptyBudgetState, rollover, spend, } from './budget.ts' import { fixedClock } from './time.ts' const cfg = { dailyCapCalls: 10, recoveryReserveFraction: 0.2 } test('ordinary calls stop at cap minus reserve', () => { let s: BudgetState = { day: '2026-09-12', used: 0, usedRecovery: 0 } for (let i = 0; i < 8; i++) s = spend(cfg, s, 'chat') assert.equal(canSpend(cfg, s, 'chat'), false) assert.equal(canSpend(cfg, s, 'deploy_failure'), true) }) test('recovery calls draw from the reserve only', () => { let s: BudgetState = { day: '2026-09-12', used: 0, usedRecovery: 0 } for (let i = 0; i < 2; i++) s = spend(cfg, s, 'recovery') assert.equal(canSpend(cfg, s, 'recovery'), false) assert.equal(canSpend(cfg, s, 'chat'), true) }) test('zero cap disables limiting', () => { const s = { day: '2026-09-12', used: 9999, usedRecovery: 0 } assert.equal(canSpend({ dailyCapCalls: 0, recoveryReserveFraction: 0.1 }, s, 'chat'), true) }) test('state rolls over on a new day', () => { const clock = fixedClock('2026-09-13T00:00:30Z') const s = rollover({ day: '2026-09-12', used: 5, usedRecovery: 1 }, clock) assert.deepEqual(s, emptyBudgetState(clock)) }) test('property: total allowed recovery calls per day equals reserve', () => { fc.assert( fc.property(fc.nat({ max: 1000 }), fc.nat({ max: 900 }), (cap, fractionPermille) => { const config = { dailyCapCalls: cap, recoveryReserveFraction: fractionPermille / 1000, } let s = emptyBudgetState(fixedClock('2026-09-12T10:00:00Z')) let recoveryCalls = 0 while (recoveryCalls < 100_000 && canSpend(config, s, 'recovery')) { s = spend(config, s, 'recovery') recoveryCalls++ } const reserve = Math.floor(config.dailyCapCalls * config.recoveryReserveFraction) assert.equal(recoveryCalls, config.dailyCapCalls === 0 ? 100_000 : reserve) }), ) }) test('default budget exists and is sane', () => { assert.ok(DEFAULT_BUDGET.dailyCapCalls > 0) assert.ok( DEFAULT_BUDGET.recoveryReserveFraction > 0 && DEFAULT_BUDGET.recoveryReserveFraction < 1, ) })