99 lines
2.9 KiB
JavaScript
99 lines
2.9 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
|
|
const {
|
|
buildTrackerRow,
|
|
getCycleRange,
|
|
resolveDueDate,
|
|
} = require('../services/statusService');
|
|
|
|
function bill(overrides = {}) {
|
|
return {
|
|
id: 1,
|
|
name: 'Test bill',
|
|
due_day: 15,
|
|
expected_amount: 100,
|
|
autopay_enabled: 0,
|
|
autodraft_status: 'none',
|
|
cycle_type: 'monthly',
|
|
cycle_day: '1',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('monthly bills use due_day and calendar-month cycle range', () => {
|
|
const monthly = bill({ due_day: 31, cycle_day: '1' });
|
|
|
|
assert.equal(resolveDueDate(monthly, 2026, 2), '2026-02-28');
|
|
assert.deepEqual(getCycleRange(2026, 2, monthly), {
|
|
start: '2026-02-01',
|
|
end: '2026-02-28',
|
|
});
|
|
});
|
|
|
|
test('weekly bills use cycle_day as weekday', () => {
|
|
const weekly = bill({ cycle_type: 'weekly', cycle_day: 'wednesday' });
|
|
|
|
assert.equal(resolveDueDate(weekly, 2026, 5), '2026-05-06');
|
|
assert.deepEqual(getCycleRange(2026, 5, weekly), {
|
|
start: '2026-05-06',
|
|
end: '2026-05-12',
|
|
});
|
|
});
|
|
|
|
test('biweekly bills use cycle_day weekday on the deterministic two-week cadence', () => {
|
|
const biweekly = bill({ cycle_type: 'biweekly', cycle_day: 'monday' });
|
|
|
|
assert.equal(resolveDueDate(biweekly, 2026, 5), '2026-05-04');
|
|
assert.deepEqual(getCycleRange(2026, 5, biweekly), {
|
|
start: '2026-05-04',
|
|
end: '2026-05-17',
|
|
});
|
|
});
|
|
|
|
test('quarterly bills only occur in assigned quarter months', () => {
|
|
const quarterly = bill({ cycle_type: 'quarterly', cycle_day: '2', due_day: 30 });
|
|
|
|
assert.equal(resolveDueDate(quarterly, 2026, 2), '2026-02-28');
|
|
assert.equal(resolveDueDate(quarterly, 2026, 3), null);
|
|
assert.equal(resolveDueDate(quarterly, 2026, 5), '2026-05-30');
|
|
assert.deepEqual(getCycleRange(2026, 5, quarterly), {
|
|
start: '2026-05-01',
|
|
end: '2026-07-31',
|
|
});
|
|
});
|
|
|
|
test('annual bills only occur in their assigned month', () => {
|
|
const annual = bill({ cycle_type: 'annual', cycle_day: '11', due_day: 31 });
|
|
|
|
assert.equal(resolveDueDate(annual, 2026, 10), null);
|
|
assert.equal(resolveDueDate(annual, 2026, 11), '2026-11-30');
|
|
assert.deepEqual(getCycleRange(2026, 11, annual), {
|
|
start: '2026-01-01',
|
|
end: '2026-12-31',
|
|
});
|
|
});
|
|
|
|
test('tracker rows are skipped when a bill does not occur in the requested month', () => {
|
|
const quarterly = bill({ cycle_type: 'quarterly', cycle_day: '1' });
|
|
|
|
assert.equal(buildTrackerRow(quarterly, [], 2026, 2, '2026-02-01', { gracePeriodDays: 5 }), null);
|
|
});
|
|
|
|
test('tracker rows cap due math when a payment exceeds the amount due', () => {
|
|
const row = buildTrackerRow(
|
|
bill({ expected_amount: 10000 }),
|
|
[{ amount: 12500, paid_date: '2026-05-10' }],
|
|
2026,
|
|
5,
|
|
'2026-05-16',
|
|
{ gracePeriodDays: 5 },
|
|
);
|
|
|
|
assert.equal(row.status, 'paid');
|
|
assert.equal(row.total_paid, 125);
|
|
assert.equal(row.paid_toward_due, 100);
|
|
assert.equal(row.overpaid_amount, 25);
|
|
assert.equal(row.balance, 0);
|
|
});
|