Files
CapaKraken/packages/engine/src/__tests__/recurrence.test.ts
T

138 lines
4.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { isRecurringDay, getRecurringHoursForDay } from "../allocation/recurrence.js";
import { RecurrenceFrequency } from "@planarchy/shared";
import type { RecurrencePattern } from "@planarchy/shared";
const monday = new Date("2026-03-09"); // Monday
const tuesday = new Date("2026-03-10"); // Tuesday
const wednesday = new Date("2026-03-11"); // Wednesday
const friday = new Date("2026-03-13"); // Friday
const nextMonday = new Date("2026-03-16"); // Monday (next week)
const twoWeeksMonday = new Date("2026-03-23"); // Monday (two weeks after)
const allocationStart = new Date("2026-03-09"); // Monday
describe("isRecurringDay", () => {
describe("WEEKLY", () => {
const pattern: RecurrencePattern = {
frequency: RecurrenceFrequency.WEEKLY,
weekdays: [1, 3, 5], // Mon, Wed, Fri
};
it("returns true for days matching weekday list", () => {
expect(isRecurringDay(monday, pattern, allocationStart)).toBe(true);
expect(isRecurringDay(wednesday, pattern, allocationStart)).toBe(true);
expect(isRecurringDay(friday, pattern, allocationStart)).toBe(true);
});
it("returns false for days not in weekday list", () => {
expect(isRecurringDay(tuesday, pattern, allocationStart)).toBe(false);
});
});
describe("BIWEEKLY", () => {
const pattern: RecurrencePattern = {
frequency: RecurrenceFrequency.BIWEEKLY,
weekdays: [1], // Monday only
interval: 2,
};
it("returns true on the allocation start week (week 0)", () => {
expect(isRecurringDay(monday, pattern, allocationStart)).toBe(true);
});
it("returns false on the next week (week 1, odd)", () => {
expect(isRecurringDay(nextMonday, pattern, allocationStart)).toBe(false);
});
it("returns true two weeks after start (week 2, even)", () => {
expect(isRecurringDay(twoWeeksMonday, pattern, allocationStart)).toBe(true);
});
it("returns false for non-matching weekdays even in active weeks", () => {
expect(isRecurringDay(tuesday, pattern, allocationStart)).toBe(false);
});
});
describe("MONTHLY", () => {
const pattern: RecurrencePattern = {
frequency: RecurrenceFrequency.MONTHLY,
monthDay: 15,
};
it("returns true on the specified day of month", () => {
expect(isRecurringDay(new Date("2026-03-15"), pattern, allocationStart)).toBe(true);
});
it("returns false on other days", () => {
expect(isRecurringDay(new Date("2026-03-14"), pattern, allocationStart)).toBe(false);
});
it("returns false when monthDay is undefined", () => {
const noDay: RecurrencePattern = {
frequency: RecurrenceFrequency.MONTHLY,
};
expect(isRecurringDay(new Date("2026-03-15"), noDay, allocationStart)).toBe(false);
});
});
describe("CUSTOM", () => {
const pattern: RecurrencePattern = {
frequency: RecurrenceFrequency.CUSTOM,
};
it("always returns true", () => {
expect(isRecurringDay(monday, pattern, allocationStart)).toBe(true);
expect(isRecurringDay(tuesday, pattern, allocationStart)).toBe(true);
});
});
});
describe("getRecurringHoursForDay", () => {
const weeklyPattern: RecurrencePattern = {
frequency: RecurrenceFrequency.WEEKLY,
weekdays: [1, 3, 5], // Mon, Wed, Fri
};
it("returns default hours on recurring days", () => {
expect(getRecurringHoursForDay(monday, weeklyPattern, 8, allocationStart)).toBe(8);
});
it("returns 0 on non-recurring days", () => {
expect(getRecurringHoursForDay(tuesday, weeklyPattern, 8, allocationStart)).toBe(0);
});
it("uses pattern.hoursPerDay when set", () => {
const patternWithHours: RecurrencePattern = {
...weeklyPattern,
hoursPerDay: 4,
};
expect(getRecurringHoursForDay(monday, patternWithHours, 8, allocationStart)).toBe(4);
});
it("returns 0 before pattern startDate", () => {
const patternWithStart: RecurrencePattern = {
...weeklyPattern,
startDate: new Date("2026-03-16"),
};
expect(getRecurringHoursForDay(monday, patternWithStart, 8, allocationStart)).toBe(0);
});
it("returns 0 after pattern endDate", () => {
const patternWithEnd: RecurrencePattern = {
...weeklyPattern,
endDate: new Date("2026-03-08"),
};
expect(getRecurringHoursForDay(monday, patternWithEnd, 8, allocationStart)).toBe(0);
});
it("returns hours within pattern date bounds", () => {
const bounded: RecurrencePattern = {
...weeklyPattern,
startDate: new Date("2026-03-01"),
endDate: new Date("2026-03-31"),
};
expect(getRecurringHoursForDay(monday, bounded, 8, allocationStart)).toBe(8);
});
});