Files
Greenlens/server/test/billing.test.js
2026-07-06 15:33:53 +02:00

91 lines
3.4 KiB
JavaScript

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));
});