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