156 lines
5.8 KiB
TypeScript
156 lines
5.8 KiB
TypeScript
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,
|
|
});
|
|
});
|
|
});
|