const test = require('node:test'); const assert = require('node:assert/strict'); const { buildDefaultAccount, alignAccountToCurrentCycle, getAvailableCredits, consumeCredits, ensureSufficientCredits, getMonthlyAllowanceForPlan, } = require('../lib/billing'); const NOW = new Date('2026-07-06T12:00:00Z'); const freeAccount = (overrides = {}) => ({ ...buildDefaultAccount('user-1', NOW), ...overrides, }); test('free plan gets 3 monthly credits', () => { assert.equal(getMonthlyAllowanceForPlan('free'), 3); assert.equal(buildDefaultAccount('u', NOW).monthlyAllowance, 3); }); test('free account has available credits', () => { const account = freeAccount({ monthlyAllowance: 3, usedThisCycle: 1 }); assert.equal(getAvailableCredits(account), 2); }); test('free account topup balance counts as available', () => { const account = freeAccount({ monthlyAllowance: 3, usedThisCycle: 3, topupBalance: 10 }); assert.equal(getAvailableCredits(account), 10); }); test('consumeCredits charges a free account from the monthly allowance', () => { const account = freeAccount({ monthlyAllowance: 3, usedThisCycle: 0 }); const charged = consumeCredits(account, 1); assert.equal(charged, 1); assert.equal(account.usedThisCycle, 1); }); test('consumeCredits throws 402 for an exhausted free account', () => { const account = freeAccount({ monthlyAllowance: 3, usedThisCycle: 3, topupBalance: 0 }); assert.throws(() => consumeCredits(account, 1), (error) => { assert.equal(error.code, 'INSUFFICIENT_CREDITS'); assert.equal(error.status, 402); assert.deepEqual(error.metadata, { required: 1, available: 0 }); return true; }); }); test('legacy free account with allowance 0 is migrated to 3', () => { const account = freeAccount({ monthlyAllowance: 0 }); const aligned = alignAccountToCurrentCycle(account, NOW); assert.equal(aligned.monthlyAllowance, 3); }); test('pro and trial allowances are unchanged', () => { assert.equal(getMonthlyAllowanceForPlan('pro'), 100); const trial = freeAccount({ plan: 'pro', monthlyAllowance: 30, usedThisCycle: 5 }); const aligned = alignAccountToCurrentCycle(trial, NOW); assert.equal(aligned.monthlyAllowance, 30); // trial allowance stays allowed assert.equal(getAvailableCredits(aligned), 25); }); test('monthly cycle rollover resets free usage', () => { const account = freeAccount({ monthlyAllowance: 3, usedThisCycle: 3, cycleEndsAt: '2026-07-01T00:00:00.000Z', }); const aligned = alignAccountToCurrentCycle(account, NOW); assert.equal(aligned.usedThisCycle, 0); assert.equal(aligned.monthlyAllowance, 3); assert.equal(getAvailableCredits(aligned), 3); }); test('ensureSufficientCredits throws 402 when balance is below cost', () => { const account = freeAccount({ monthlyAllowance: 3, usedThisCycle: 2, topupBalance: 0 }); assert.throws(() => ensureSufficientCredits(account, 2), (error) => { assert.equal(error.code, 'INSUFFICIENT_CREDITS'); assert.equal(error.status, 402); assert.deepEqual(error.metadata, { required: 2, available: 1 }); return true; }); }); test('ensureSufficientCredits passes when balance covers cost', () => { const account = freeAccount({ monthlyAllowance: 3, usedThisCycle: 1, topupBalance: 0 }); assert.doesNotThrow(() => ensureSufficientCredits(account, 2)); });