Files
support_backend/tests/unit/platform/business-calendars/calendar-walk.test.ts
T
saqib mirandClaude Sonnet 5 40687f68fa feat(010-identity-auth): real staff login, session verification, and role gating
Replaces the no-op fastify.authenticate stub and the never-implemented
identity/auth login with real bcrypt password verification, JWT session
issuance/verification (reusing the existing JWT_SECRET), and a Redis-backed
revocation denylist for logout. Adds requireRole('ADMIN') to admin-only
configuration writes across 002-009 that previously relied on a decorator
that never actually checked anything. Adds self-identity (GET /auth/me,
re-validated against live account state) and admin-provisioned accounts
(POST /admin/users).

Making the auth check genuinely reject invalid/missing tokens exposed that
~18 pre-existing integration test files called already-gated routes with no
Authorization header (safe against the old no-op stub, broken against a real
one) — fixed via a shared tests/helpers/auth.ts (loginAs/authHeader) and a
file-by-file pass, plus two related SLA-run cleanup races exposed once admin
setup calls in those files' own beforeAll blocks started actually succeeding.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 12:45:37 +05:30

103 lines
4.8 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
addBusinessMinutes,
isWithinWorkingHours,
} from '@/modules/platform/business-calendars/calculators/business-hours.calculator';
const MON_FRI_9_TO_5 = {
timezone: 'America/New_York',
workingHours: {
mon: { start: '09:00', end: '17:00' },
tue: { start: '09:00', end: '17:00' },
wed: { start: '09:00', end: '17:00' },
thu: { start: '09:00', end: '17:00' },
fri: { start: '09:00', end: '17:00' },
},
};
// America/New_York is UTC-5 (EST) outside DST, UTC-4 (EDT) during DST — every Date.UTC(...)
// below is written as the equivalent UTC instant for the Eastern wall-clock time noted in the
// comment, since luxon's own DST handling is exactly what's under test here.
describe('addBusinessMinutes', () => {
it('stays within the same working day when enough time remains', () => {
// Mon 2026-01-05 10:00 EST (UTC-5) -> 15:00 UTC
const start = new Date(Date.UTC(2026, 0, 5, 15, 0));
const due = addBusinessMinutes(start, 120, MON_FRI_9_TO_5, []);
// +120 min -> 12:00 EST -> 17:00 UTC
expect(due.toISOString()).toBe(new Date(Date.UTC(2026, 0, 5, 17, 0)).toISOString());
});
it('rolls over a weekend, never counting Saturday/Sunday as available time', () => {
// Fri 2026-01-02 16:00 EST (1 hour left in the window) -> 21:00 UTC
const start = new Date(Date.UTC(2026, 0, 2, 21, 0));
// 1 hour left Friday + need 3 more hours -> Monday 12:00 EST
const due = addBusinessMinutes(start, 240, MON_FRI_9_TO_5, []);
expect(due.toISOString()).toBe(new Date(Date.UTC(2026, 0, 5, 17, 0)).toISOString()); // Mon 12:00 EST
});
it('excludes a holiday entirely, rolling to the next working day', () => {
// Fri 2026-01-02 16:00 EST, Monday Jan 5 is a holiday
const start = new Date(Date.UTC(2026, 0, 2, 21, 0));
const holiday = new Date(Date.UTC(2026, 0, 5, 17, 0)); // Mon 12:00 EST — safely mid-Monday
const due = addBusinessMinutes(start, 240, MON_FRI_9_TO_5, [holiday]);
// 1h Friday + Monday excluded + 3h into Tuesday -> Tue 12:00 EST
expect(due.toISOString()).toBe(new Date(Date.UTC(2026, 0, 6, 17, 0)).toISOString());
});
it('a weekday with no configured window contributes zero available time', () => {
const noWednesday = {
timezone: 'America/New_York',
workingHours: {
tue: { start: '09:00', end: '17:00' },
thu: { start: '09:00', end: '17:00' },
},
};
// Tue 2026-01-06 16:00 EST (1h left)
const start = new Date(Date.UTC(2026, 0, 6, 21, 0));
const due = addBusinessMinutes(start, 300, noWednesday, []);
// 1h Tue + (Wed skipped, zero hours) + 4h into Thu -> Thu 13:00 EST
expect(due.toISOString()).toBe(new Date(Date.UTC(2026, 0, 8, 18, 0)).toISOString());
});
it('clips to the start time on the first day, never counting time before it', () => {
// Mon 10:00 EST: 7h remain in the window (to 17:00) + 1 more hour -> Tue 10:00 EST
const start = new Date(Date.UTC(2026, 0, 5, 15, 0));
const due = addBusinessMinutes(start, 420 + 60, MON_FRI_9_TO_5, []);
expect(due.toISOString()).toBe(new Date(Date.UTC(2026, 0, 6, 15, 0)).toISOString()); // Tue 10:00 EST
});
it('returns the start time unchanged when minutes is zero or negative', () => {
const start = new Date(Date.UTC(2026, 0, 5, 15, 0));
expect(addBusinessMinutes(start, 0, MON_FRI_9_TO_5, []).toISOString()).toBe(
start.toISOString(),
);
});
it('is correct across a DST transition (US spring-forward, March 2026)', () => {
// 2026-03-08 is the US DST transition (02:00 -> 03:00). Fri 2026-03-06 16:00 EST (1h left).
const start = new Date(Date.UTC(2026, 2, 6, 21, 0));
// 1h Friday + weekend skipped + 3h into Monday 2026-03-09 (now EDT, UTC-4) -> 12:00 EDT -> 16:00 UTC
const due = addBusinessMinutes(start, 240, MON_FRI_9_TO_5, []);
expect(due.toISOString()).toBe(new Date(Date.UTC(2026, 2, 9, 16, 0)).toISOString());
});
});
describe('isWithinWorkingHours', () => {
it('is true inside a configured window and false outside it', () => {
const insideWindow = new Date(Date.UTC(2026, 0, 5, 16, 0)); // Mon 11:00 EST
const beforeWindow = new Date(Date.UTC(2026, 0, 5, 13, 0)); // Mon 08:00 EST
expect(isWithinWorkingHours(insideWindow, MON_FRI_9_TO_5, [])).toBe(true);
expect(isWithinWorkingHours(beforeWindow, MON_FRI_9_TO_5, [])).toBe(false);
});
it('is false on a weekend and on a holiday', () => {
const saturday = new Date(Date.UTC(2026, 0, 3, 16, 0));
expect(isWithinWorkingHours(saturday, MON_FRI_9_TO_5, [])).toBe(false);
const mondayNoon = new Date(Date.UTC(2026, 0, 5, 16, 0)); // Mon 11:00 EST
const holiday = new Date(Date.UTC(2026, 0, 5, 17, 0)); // Mon 12:00 EST — safely mid-Monday
expect(isWithinWorkingHours(mondayNoon, MON_FRI_9_TO_5, [holiday])).toBe(false);
});
});