149 lines
5.6 KiB
TypeScript
149 lines
5.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import {
|
||
computeAvailableHours,
|
||
computeBookedHours,
|
||
computeChargeability,
|
||
} from "../allocation/chargeability.js";
|
||
|
||
// Standard Mon–Fri 8h availability
|
||
const stdAvail = { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 };
|
||
|
||
// Week of 2025-01-06 (Mon) to 2025-01-10 (Fri)
|
||
const weekStart = new Date("2025-01-06");
|
||
const weekEnd = new Date("2025-01-10");
|
||
|
||
describe("computeAvailableHours", () => {
|
||
it("Mon–Fri 8h resource: 1 calendar week = 40 available hours", () => {
|
||
expect(computeAvailableHours(stdAvail, weekStart, weekEnd)).toBe(40);
|
||
});
|
||
|
||
it("excludes Saturday and Sunday when not in availability", () => {
|
||
// 2025-01-04 (Sat) to 2025-01-05 (Sun)
|
||
expect(computeAvailableHours(stdAvail, new Date("2025-01-04"), new Date("2025-01-05"))).toBe(0);
|
||
});
|
||
|
||
it("counts Saturday when enabled in availability", () => {
|
||
const avail = { ...stdAvail, saturday: 4 };
|
||
// Mon 06 to Sat 11 Jan: 5 weekdays × 8 + 1 Saturday × 4 = 44
|
||
expect(computeAvailableHours(avail, new Date("2025-01-06"), new Date("2025-01-11"))).toBe(44);
|
||
});
|
||
|
||
it("single day period (Monday): returns daily hours", () => {
|
||
expect(computeAvailableHours(stdAvail, weekStart, weekStart)).toBe(8);
|
||
});
|
||
});
|
||
|
||
describe("computeBookedHours", () => {
|
||
it("no allocations → bookedHours = 0", () => {
|
||
expect(computeBookedHours(stdAvail, [], weekStart, weekEnd)).toBe(0);
|
||
});
|
||
|
||
it("full 8h allocation covering entire period counts all working days", () => {
|
||
const alloc = { startDate: weekStart, endDate: weekEnd, hoursPerDay: 8 };
|
||
expect(computeBookedHours(stdAvail, [alloc], weekStart, weekEnd)).toBe(40);
|
||
});
|
||
|
||
it("allocation partially overlapping: only overlap days counted", () => {
|
||
// Allocation spans Wed-Sun; period is Mon-Fri → overlap is Wed-Fri (3 days)
|
||
const alloc = {
|
||
startDate: new Date("2025-01-08"), // Wed
|
||
endDate: new Date("2025-01-12"), // Sun
|
||
hoursPerDay: 8,
|
||
};
|
||
expect(computeBookedHours(stdAvail, [alloc], weekStart, weekEnd)).toBe(24); // 3 days × 8
|
||
});
|
||
|
||
it("allocation spanning weekends: weekends excluded from booked hours", () => {
|
||
// Mon 06 to Mon 13 (spans a weekend), but weekend has 0 avail
|
||
const alloc = {
|
||
startDate: new Date("2025-01-06"),
|
||
endDate: new Date("2025-01-13"),
|
||
hoursPerDay: 8,
|
||
};
|
||
// Period is Mon-Fri week only
|
||
expect(computeBookedHours(stdAvail, [alloc], weekStart, weekEnd)).toBe(40);
|
||
});
|
||
|
||
it("Saturday with availability counted when allocation covers it", () => {
|
||
const avail = { ...stdAvail, saturday: 4 };
|
||
const alloc = {
|
||
startDate: new Date("2025-01-06"), // Mon
|
||
endDate: new Date("2025-01-11"), // Sat
|
||
hoursPerDay: 6,
|
||
};
|
||
// Mon–Fri: 5 days × 6 = 30, Sat: 1 day × 6 = 6 → total 36
|
||
expect(computeBookedHours(avail, [alloc], new Date("2025-01-06"), new Date("2025-01-11"))).toBe(36);
|
||
});
|
||
|
||
it("allocation entirely before period: no hours counted", () => {
|
||
const alloc = {
|
||
startDate: new Date("2024-12-30"),
|
||
endDate: new Date("2025-01-03"),
|
||
hoursPerDay: 8,
|
||
};
|
||
expect(computeBookedHours(stdAvail, [alloc], weekStart, weekEnd)).toBe(0);
|
||
});
|
||
|
||
it("allocation entirely after period: no hours counted", () => {
|
||
const alloc = {
|
||
startDate: new Date("2025-01-13"),
|
||
endDate: new Date("2025-01-17"),
|
||
hoursPerDay: 8,
|
||
};
|
||
expect(computeBookedHours(stdAvail, [alloc], weekStart, weekEnd)).toBe(0);
|
||
});
|
||
});
|
||
|
||
describe("computeChargeability", () => {
|
||
it("no allocations → chargeability = 0, bookedHours = 0", () => {
|
||
const result = computeChargeability(stdAvail, [], weekStart, weekEnd);
|
||
expect(result.chargeability).toBe(0);
|
||
expect(result.bookedHours).toBe(0);
|
||
expect(result.availableHours).toBe(40);
|
||
});
|
||
|
||
it("full 8h allocation covering entire period → chargeability = 100", () => {
|
||
const alloc = { startDate: weekStart, endDate: weekEnd, hoursPerDay: 8 };
|
||
const result = computeChargeability(stdAvail, [alloc], weekStart, weekEnd);
|
||
expect(result.chargeability).toBe(100);
|
||
expect(result.bookedHours).toBe(40);
|
||
});
|
||
|
||
it("50% allocation → chargeability = 50", () => {
|
||
const alloc = { startDate: weekStart, endDate: weekEnd, hoursPerDay: 4 };
|
||
const result = computeChargeability(stdAvail, [alloc], weekStart, weekEnd);
|
||
expect(result.chargeability).toBe(50);
|
||
expect(result.bookedHours).toBe(20);
|
||
});
|
||
|
||
it("over-allocated (booked > available): chargeability capped at 100", () => {
|
||
// Two allocations totalling 12h/day on a resource with 8h availability
|
||
const allocs = [
|
||
{ startDate: weekStart, endDate: weekEnd, hoursPerDay: 8 },
|
||
{ startDate: weekStart, endDate: weekEnd, hoursPerDay: 4 },
|
||
];
|
||
const result = computeChargeability(stdAvail, allocs, weekStart, weekEnd);
|
||
expect(result.chargeability).toBe(100);
|
||
expect(result.bookedHours).toBe(60); // not capped in raw value
|
||
});
|
||
|
||
it("single day period (start === end): correct result", () => {
|
||
const alloc = { startDate: weekStart, endDate: weekStart, hoursPerDay: 4 };
|
||
const result = computeChargeability(stdAvail, [alloc], weekStart, weekStart);
|
||
expect(result.availableHours).toBe(8);
|
||
expect(result.bookedHours).toBe(4);
|
||
expect(result.chargeability).toBe(50);
|
||
});
|
||
|
||
it("zero available hours (all weekend period): chargeability = 0", () => {
|
||
const alloc = {
|
||
startDate: new Date("2025-01-04"),
|
||
endDate: new Date("2025-01-05"),
|
||
hoursPerDay: 8,
|
||
};
|
||
const result = computeChargeability(stdAvail, [alloc], new Date("2025-01-04"), new Date("2025-01-05"));
|
||
expect(result.availableHours).toBe(0);
|
||
expect(result.chargeability).toBe(0);
|
||
});
|
||
});
|