Files
support_backend/tests/unit/platform/business-calendars/calendar-walk.test.ts
T

98 lines
4.8 KiB
TypeScript
Raw Normal View History

2026-09-03 13:02:05 +05:30
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);
});
});