import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("../sse/event-bus.js", () => ({ emitAllocationCreated: vi.fn(), emitAllocationDeleted: vi.fn(), emitAllocationUpdated: vi.fn(), })); vi.mock("../router/timeline-allocation-assignment-procedure-support.js", () => ({ createTimelineBatchQuickAssignments: vi.fn(), createTimelineQuickAssignment: vi.fn(), })); vi.mock("../router/timeline-allocation-inline-support.js", () => ({ applyTimelineInlineAllocationUpdate: vi.fn(), })); vi.mock("../router/timeline-allocation-fragment-support.js", () => ({ carveTimelineAllocationRange: vi.fn(), })); vi.mock("../router/timeline-allocation-procedure-support.js", () => ({ shiftTimelineAllocations: vi.fn(), })); import { emitAllocationCreated, emitAllocationDeleted, emitAllocationUpdated, } from "../sse/event-bus.js"; import { carveTimelineAllocationRange } from "../router/timeline-allocation-fragment-support.js"; import { createTimelineBatchQuickAssignments, createTimelineQuickAssignment, } from "../router/timeline-allocation-assignment-procedure-support.js"; import { applyTimelineInlineAllocationUpdate } from "../router/timeline-allocation-inline-support.js"; import { shiftTimelineAllocations } from "../router/timeline-allocation-procedure-support.js"; import { applyTimelineAllocationBatchShiftMutation, carveTimelineAllocationRangeMutation, createTimelineBatchQuickAssignMutation, createTimelineQuickAssignMutation, updateTimelineAllocationInlineMutation, } from "../router/timeline-allocation-router-support.js"; const emitAllocationCreatedMock = vi.mocked(emitAllocationCreated); const emitAllocationDeletedMock = vi.mocked(emitAllocationDeleted); const emitAllocationUpdatedMock = vi.mocked(emitAllocationUpdated); const carveTimelineAllocationRangeMock = vi.mocked(carveTimelineAllocationRange); const createTimelineBatchQuickAssignmentsMock = vi.mocked(createTimelineBatchQuickAssignments); const createTimelineQuickAssignmentMock = vi.mocked(createTimelineQuickAssignment); const applyTimelineInlineAllocationUpdateMock = vi.mocked(applyTimelineInlineAllocationUpdate); const shiftTimelineAllocationsMock = vi.mocked(shiftTimelineAllocations); describe("timeline allocation router support", () => { beforeEach(() => { vi.clearAllMocks(); }); it("updates an inline allocation and emits an update event", async () => { const db = {} as never; applyTimelineInlineAllocationUpdateMock.mockResolvedValueOnce({ id: "allocation_1", projectId: "project_1", resourceId: "resource_1", } as never); await expect( updateTimelineAllocationInlineMutation({ db, allocationId: "allocation_1", hoursPerDay: 8, startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-05T00:00:00.000Z"), includeSaturday: true, role: "Lead", }), ).resolves.toEqual({ id: "allocation_1", projectId: "project_1", resourceId: "resource_1", }); expect(applyTimelineInlineAllocationUpdateMock).toHaveBeenCalledWith({ db, allocationId: "allocation_1", hoursPerDay: 8, startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-05T00:00:00.000Z"), includeSaturday: true, role: "Lead", }); expect(emitAllocationUpdatedMock).toHaveBeenCalledWith({ id: "allocation_1", projectId: "project_1", resourceId: "resource_1", }); }); it("creates a quick assignment with the router source metadata", async () => { const db = {} as never; createTimelineQuickAssignmentMock.mockResolvedValueOnce({ id: "allocation_1" } as never); await expect( createTimelineQuickAssignMutation(db, { resourceId: "resource_1", projectId: "project_1", startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-05T00:00:00.000Z"), hoursPerDay: 8, role: "Lead", status: "PROPOSED", }), ).resolves.toEqual({ id: "allocation_1" }); expect(createTimelineQuickAssignmentMock).toHaveBeenCalledWith(db, { resourceId: "resource_1", projectId: "project_1", startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-05T00:00:00.000Z"), hoursPerDay: 8, role: "Lead", status: "PROPOSED", source: "quickAssign", }); }); it("maps batch quick assignments to the batch router source metadata", async () => { const db = {} as never; createTimelineBatchQuickAssignmentsMock.mockResolvedValueOnce({ count: 2 } as never); await expect( createTimelineBatchQuickAssignMutation(db, { assignments: [ { resourceId: "resource_1", projectId: "project_1", startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-05T00:00:00.000Z"), hoursPerDay: 8, role: "Lead", status: "PROPOSED", }, { resourceId: "resource_2", projectId: "project_2", startDate: new Date("2026-04-06T00:00:00.000Z"), endDate: new Date("2026-04-10T00:00:00.000Z"), hoursPerDay: 6, role: "Artist", status: "PROPOSED", }, ], }), ).resolves.toEqual({ count: 2 }); expect(createTimelineBatchQuickAssignmentsMock).toHaveBeenCalledWith(db, { assignments: [ { resourceId: "resource_1", projectId: "project_1", startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-05T00:00:00.000Z"), hoursPerDay: 8, role: "Lead", status: "PROPOSED", source: "batchQuickAssign", }, { resourceId: "resource_2", projectId: "project_2", startDate: new Date("2026-04-06T00:00:00.000Z"), endDate: new Date("2026-04-10T00:00:00.000Z"), hoursPerDay: 6, role: "Artist", status: "PROPOSED", source: "batchQuickAssign", }, ], }); }); it("delegates allocation batch shifts unchanged", async () => { const db = {} as never; shiftTimelineAllocationsMock.mockResolvedValueOnce({ count: 3 } as never); await expect( applyTimelineAllocationBatchShiftMutation(db, { allocationIds: ["allocation_1", "allocation_2"], daysDelta: 5, mode: "preserve-duration", }), ).resolves.toEqual({ count: 3 }); expect(shiftTimelineAllocationsMock).toHaveBeenCalledWith(db, { allocationIds: ["allocation_1", "allocation_2"], daysDelta: 5, mode: "preserve-duration", }); }); it("carves an allocation range and emits update/create/delete events for every affected fragment", async () => { const db = {} as never; const startDate = new Date("2026-04-09T00:00:00.000Z"); const endDate = new Date("2026-04-10T00:00:00.000Z"); carveTimelineAllocationRangeMock.mockResolvedValueOnce({ action: "split", allocationGroupId: "group_1", updatedAllocationIds: ["allocation_left"], createdAllocationIds: ["allocation_right"], deletedAllocationIds: ["allocation_removed"], projectId: "project_1", resourceId: "resource_1", }); await expect( carveTimelineAllocationRangeMutation({ db, allocationId: "allocation_1", startDate, endDate, }), ).resolves.toEqual({ action: "split", allocationGroupId: "group_1", updatedAllocationIds: ["allocation_left"], createdAllocationIds: ["allocation_right"], deletedAllocationIds: ["allocation_removed"], projectId: "project_1", resourceId: "resource_1", }); expect(carveTimelineAllocationRangeMock).toHaveBeenCalledWith({ db, allocationId: "allocation_1", startDate, endDate, }); expect(emitAllocationUpdatedMock).toHaveBeenCalledWith({ id: "allocation_left", projectId: "project_1", resourceId: "resource_1", }); expect(emitAllocationCreatedMock).toHaveBeenCalledWith({ id: "allocation_right", projectId: "project_1", resourceId: "resource_1", }); expect(emitAllocationDeletedMock).toHaveBeenCalledWith( "allocation_removed", "project_1", "resource_1", ); }); });