import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("../router/timeline-project-load-support.js", () => ({ previewTimelineProjectShift: vi.fn(), })); vi.mock("@capakraken/application", () => ({ listAssignmentBookings: vi.fn(), })); vi.mock("@capakraken/engine", () => ({ computeBudgetStatus: vi.fn(), })); vi.mock("../router/timeline-project-query-support.js", () => ({ findTimelineProjectOrThrow: vi.fn(), timelineBudgetStatusProjectSelect: { id: true }, timelineShiftPreviewProjectSelect: { id: true }, })); vi.mock("../router/timeline-project-read-support.js", () => ({ buildTimelineBudgetStatusAllocations: vi.fn(), buildTimelineBudgetStatusResponse: vi.fn(), buildTimelineShiftPreviewDetailResponse: vi.fn(), })); import { listAssignmentBookings } from "@capakraken/application"; import { computeBudgetStatus } from "@capakraken/engine"; import { previewTimelineProjectShift, } from "../router/timeline-project-load-support.js"; import { buildTimelineBudgetStatusAllocations, buildTimelineBudgetStatusResponse, buildTimelineShiftPreviewDetailResponse, } from "../router/timeline-project-read-support.js"; import { findTimelineProjectOrThrow, timelineBudgetStatusProjectSelect, timelineShiftPreviewProjectSelect, } from "../router/timeline-project-query-support.js"; import { readTimelineProjectBudgetStatusResponse, readTimelineProjectShiftPreview, readTimelineProjectShiftPreviewDetail, } from "../router/timeline-project-procedure-support.js"; const previewTimelineProjectShiftMock = vi.mocked(previewTimelineProjectShift); const listAssignmentBookingsMock = vi.mocked(listAssignmentBookings); const computeBudgetStatusMock = vi.mocked(computeBudgetStatus); const findTimelineProjectOrThrowMock = vi.mocked(findTimelineProjectOrThrow); const buildTimelineBudgetStatusAllocationsMock = vi.mocked(buildTimelineBudgetStatusAllocations); const buildTimelineBudgetStatusResponseMock = vi.mocked(buildTimelineBudgetStatusResponse); const buildTimelineShiftPreviewDetailResponseMock = vi.mocked(buildTimelineShiftPreviewDetailResponse); describe("timeline project procedure support", () => { beforeEach(() => { vi.clearAllMocks(); }); it("builds shift preview detail responses", async () => { findTimelineProjectOrThrowMock.mockResolvedValueOnce({ id: "project_1" } as never); previewTimelineProjectShiftMock.mockResolvedValueOnce({ valid: true } as never); buildTimelineShiftPreviewDetailResponseMock.mockReturnValueOnce({ preview: true } as never); await expect( readTimelineProjectShiftPreviewDetail({} as never, { projectId: "project_1", newStartDate: new Date("2026-04-03T00:00:00.000Z"), newEndDate: new Date("2026-04-12T00:00:00.000Z"), }), ).resolves.toEqual({ preview: true }); expect(findTimelineProjectOrThrowMock).toHaveBeenCalledWith({}, { projectId: "project_1", select: timelineShiftPreviewProjectSelect, }); expect(previewTimelineProjectShiftMock).toHaveBeenCalledWith({}, { projectId: "project_1", newStartDate: new Date("2026-04-03T00:00:00.000Z"), newEndDate: new Date("2026-04-12T00:00:00.000Z"), }); expect(buildTimelineShiftPreviewDetailResponseMock).toHaveBeenCalledWith({ project: { id: "project_1" }, requestedShift: { newStartDate: new Date("2026-04-03T00:00:00.000Z"), newEndDate: new Date("2026-04-12T00:00:00.000Z"), }, preview: { valid: true }, }); }); it("loads plain shift preview responses through procedure support", async () => { previewTimelineProjectShiftMock.mockResolvedValueOnce({ valid: true } as never); await expect( readTimelineProjectShiftPreview({} as never, { projectId: "project_1", newStartDate: new Date("2026-04-03T00:00:00.000Z"), newEndDate: new Date("2026-04-12T00:00:00.000Z"), }), ).resolves.toEqual({ valid: true }); expect(previewTimelineProjectShiftMock).toHaveBeenCalledWith({}, { projectId: "project_1", newStartDate: new Date("2026-04-03T00:00:00.000Z"), newEndDate: new Date("2026-04-12T00:00:00.000Z"), }); }); it("builds budget status responses from project bookings", async () => { findTimelineProjectOrThrowMock.mockResolvedValueOnce({ id: "project_1", budgetCents: 100_000, winProbability: 80, startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-10T00:00:00.000Z"), } as never); listAssignmentBookingsMock.mockResolvedValueOnce([{ id: "booking_1" }] as never); buildTimelineBudgetStatusAllocationsMock.mockReturnValueOnce([{ id: "allocation_1" }] as never); computeBudgetStatusMock.mockReturnValueOnce({ withinBudget: true } as never); buildTimelineBudgetStatusResponseMock.mockReturnValueOnce({ budget: true } as never); await expect(readTimelineProjectBudgetStatusResponse({} as never, "project_1")).resolves.toEqual({ budget: true, }); expect(findTimelineProjectOrThrowMock).toHaveBeenCalledWith({}, { projectId: "project_1", select: timelineBudgetStatusProjectSelect, }); expect(listAssignmentBookingsMock).toHaveBeenCalledWith({}, { projectIds: ["project_1"], }); expect(buildTimelineBudgetStatusAllocationsMock).toHaveBeenCalledWith([{ id: "booking_1" }]); expect(computeBudgetStatusMock).toHaveBeenCalledWith( 100_000, 80, [{ id: "allocation_1" }], new Date("2026-04-01T00:00:00.000Z"), new Date("2026-04-10T00:00:00.000Z"), ); expect(buildTimelineBudgetStatusResponseMock).toHaveBeenCalledWith({ project: { id: "project_1", budgetCents: 100_000, winProbability: 80, startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-10T00:00:00.000Z"), }, budgetStatus: { withinBudget: true }, totalAllocations: 1, }); }); });