BillTracker/tests/analyticsService.test.js

69 lines
3.0 KiB
JavaScript

'use strict';
// X2 — analyticsService's month-window math (which drives the expected-vs-actual
// reconciliation, the QA-B5-03 area) and query validation had no unit tests.
// These pin the pure helpers with hand-calculated examples, including the
// year-boundary and leap-year edges that off-by-one bugs hide in.
const test = require('node:test');
const assert = require('node:assert/strict');
const {
monthKey, monthLabel, addMonths, monthEndDate, buildMonths, validateSummaryQuery,
} = require('../services/analyticsService');
test('monthKey zero-pads the month', () => {
assert.equal(monthKey(2026, 6), '2026-06');
assert.equal(monthKey(2026, 12), '2026-12');
});
test('monthLabel is a UTC short-month + 2-digit year (no TZ drift)', () => {
assert.equal(monthLabel(2026, 1), 'Jan 26');
assert.equal(monthLabel(2026, 6), 'Jun 26');
assert.equal(monthLabel(2025, 12), 'Dec 25');
});
test('addMonths crosses year boundaries both ways', () => {
assert.deepEqual(addMonths(2026, 1, -1), { year: 2025, month: 12 });
assert.deepEqual(addMonths(2026, 12, 1), { year: 2027, month: 1 });
assert.deepEqual(addMonths(2026, 6, 0), { year: 2026, month: 6 });
assert.deepEqual(addMonths(2026, 3, -6), { year: 2025, month: 9 });
});
test('monthEndDate handles 30/31-day months and leap Februaries', () => {
assert.equal(monthEndDate(2026, 6), '2026-06-30');
assert.equal(monthEndDate(2026, 7), '2026-07-31');
assert.equal(monthEndDate(2026, 2), '2026-02-28'); // not a leap year
assert.equal(monthEndDate(2024, 2), '2024-02-29'); // leap year
});
test('buildMonths yields `count` contiguous months ending at (endYear, endMonth)', () => {
const months = buildMonths(2026, 3, 3); // Jan, Feb, Mar 2026
assert.equal(months.length, 3);
assert.deepEqual(months.map(m => m.key), ['2026-01', '2026-02', '2026-03']);
assert.equal(months[0].start, '2026-01-01');
assert.equal(months[2].end, '2026-03-31');
assert.equal(months[2].label, 'Mar 26');
});
test('buildMonths spanning a year boundary', () => {
const months = buildMonths(2026, 1, 3); // Nov 25, Dec 25, Jan 26
assert.deepEqual(months.map(m => m.key), ['2025-11', '2025-12', '2026-01']);
});
test('validateSummaryQuery: defaults, parsing, and range errors', () => {
const ok = validateSummaryQuery({ year: '2026', month: '6', months: '12' });
assert.equal(ok.error, undefined);
assert.equal(ok.year, 2026);
assert.equal(ok.month, 6);
assert.equal(ok.months, 12);
assert.equal(ok.includeSkipped, true, 'skipped included unless explicitly false');
assert.match(validateSummaryQuery({ month: '13' }, new Date('2026-06-15')).error, /month/);
assert.match(validateSummaryQuery({ months: '37' }, new Date('2026-06-15')).error, /months/);
assert.match(validateSummaryQuery({ year: '1999' }, new Date('2026-06-15')).error, /year/);
assert.match(validateSummaryQuery({ category_id: '-1' }, new Date('2026-06-15')).error, /category_id/);
// include_skipped=false flips the flag
assert.equal(validateSummaryQuery({ include_skipped: 'false' }, new Date('2026-06-15')).includeSkipped, false);
});