import { describe, it, expect } from "vitest"; import { checkDuplicateAssignment, type ExistingAssignment } from "../allocation/duplicate-check.js"; const base: ExistingAssignment = { id: "a1", resourceId: "r1", projectId: "p1", startDate: new Date("2026-03-01"), endDate: new Date("2026-06-30"), status: "CONFIRMED", }; describe("checkDuplicateAssignment", () => { it("returns no duplicate when no existing assignments", () => { const result = checkDuplicateAssignment("r1", "p1", "2026-04-01", "2026-05-01", []); expect(result.isDuplicate).toBe(false); }); it("returns no duplicate for different project", () => { const result = checkDuplicateAssignment("r1", "p2", "2026-04-01", "2026-05-01", [base]); expect(result.isDuplicate).toBe(false); }); it("returns no duplicate for different resource", () => { const result = checkDuplicateAssignment("r2", "p1", "2026-04-01", "2026-05-01", [base]); expect(result.isDuplicate).toBe(false); }); it("returns no duplicate when dates don't overlap (before)", () => { const result = checkDuplicateAssignment("r1", "p1", "2026-01-01", "2026-02-28", [base]); expect(result.isDuplicate).toBe(false); }); it("returns no duplicate when dates don't overlap (after)", () => { const result = checkDuplicateAssignment("r1", "p1", "2026-07-01", "2026-08-01", [base]); expect(result.isDuplicate).toBe(false); }); it("detects duplicate with full overlap", () => { const result = checkDuplicateAssignment("r1", "p1", "2026-03-15", "2026-05-15", [base]); expect(result.isDuplicate).toBe(true); expect(result.conflictingAssignment?.id).toBe("a1"); expect(result.message).toContain("already assigned"); }); it("detects duplicate with partial overlap (start before, end during)", () => { const result = checkDuplicateAssignment("r1", "p1", "2026-02-15", "2026-04-01", [base]); expect(result.isDuplicate).toBe(true); }); it("detects duplicate with partial overlap (start during, end after)", () => { const result = checkDuplicateAssignment("r1", "p1", "2026-05-01", "2026-07-15", [base]); expect(result.isDuplicate).toBe(true); }); it("detects duplicate on exact same dates", () => { const result = checkDuplicateAssignment("r1", "p1", "2026-03-01", "2026-06-30", [base]); expect(result.isDuplicate).toBe(true); }); it("ignores CANCELLED assignments", () => { const cancelled = { ...base, status: "CANCELLED" }; const result = checkDuplicateAssignment("r1", "p1", "2026-04-01", "2026-05-01", [cancelled]); expect(result.isDuplicate).toBe(false); }); it("ignores DRAFT assignments", () => { const draft = { ...base, status: "DRAFT" }; const result = checkDuplicateAssignment("r1", "p1", "2026-04-01", "2026-05-01", [draft]); expect(result.isDuplicate).toBe(false); }); it("excludes self by ID (for updates)", () => { const result = checkDuplicateAssignment("r1", "p1", "2026-04-01", "2026-05-01", [base], "a1"); expect(result.isDuplicate).toBe(false); }); it("does not exclude other assignments when excludeId is set", () => { const other: ExistingAssignment = { ...base, id: "a2" }; const result = checkDuplicateAssignment("r1", "p1", "2026-04-01", "2026-05-01", [other], "a1"); expect(result.isDuplicate).toBe(true); }); it("works with string dates", () => { const strAssignment: ExistingAssignment = { ...base, startDate: "2026-03-01", endDate: "2026-06-30", }; const result = checkDuplicateAssignment("r1", "p1", "2026-04-01", "2026-05-01", [strAssignment]); expect(result.isDuplicate).toBe(true); }); it("checks PROPOSED status as active", () => { const proposed = { ...base, status: "PROPOSED" }; const result = checkDuplicateAssignment("r1", "p1", "2026-04-01", "2026-05-01", [proposed]); expect(result.isDuplicate).toBe(true); }); it("formats conflict dates from local-midnight Date objects without shifting the calendar day", () => { const localDateAssignment: ExistingAssignment = { ...base, startDate: new Date(2026, 3, 6), endDate: new Date(2026, 3, 17), }; const result = checkDuplicateAssignment( "r1", "p1", new Date(2026, 3, 9), new Date(2026, 3, 10), [localDateAssignment], ); expect(result.isDuplicate).toBe(true); expect(result.message).toContain("2026-04-06"); expect(result.message).toContain("2026-04-17"); }); });