Files
Nexus/packages/api/src/__tests__/timeline-shift-support.test.ts
T

152 lines
4.2 KiB
TypeScript

import type { ShiftValidationResult } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { describe, expect, it, vi } from "vitest";
const { validateShiftMock } = vi.hoisted(() => ({
validateShiftMock: vi.fn(),
}));
vi.mock("@capakraken/engine", async (importOriginal) => {
const actual = await importOriginal<typeof import("@capakraken/engine")>();
return {
...actual,
validateShift: validateShiftMock,
};
});
import {
assertTimelineProjectShiftValid,
buildTimelineProjectShiftEventPayload,
buildTimelineProjectShiftValidation,
} from "../router/timeline-shift-support.js";
function createValidValidationResult(
overrides: Partial<ShiftValidationResult> = {},
): ShiftValidationResult {
return {
valid: true,
errors: [],
warnings: [],
conflictDetails: [],
costImpact: {
currentTotalCents: 100_000,
newTotalCents: 112_000,
deltaCents: 12_000,
budgetCents: 200_000,
budgetUtilizationBefore: 50,
budgetUtilizationAfter: 56,
wouldExceedBudget: false,
},
...overrides,
};
}
describe("timeline shift support", () => {
it("builds validation requests from loaded shift context", () => {
const expectedValidation = createValidValidationResult();
validateShiftMock.mockReturnValue(expectedValidation);
const result = buildTimelineProjectShiftValidation({
context: {
project: {
id: "project_1",
budgetCents: 200_000,
winProbability: 80,
startDate: new Date("2026-04-01"),
endDate: new Date("2026-04-10"),
},
demandRequirements: [],
assignments: [],
shiftPlan: {
validationAllocations: [
{
id: "assignment_1",
resourceId: "resource_1",
startDate: new Date("2026-04-01"),
endDate: new Date("2026-04-05"),
hoursPerDay: 8,
percentage: 100,
role: "Compositing",
dailyCostCents: 40_000,
resource: {
id: "resource_1",
displayName: "Alice",
lcrCents: 5_000,
availability: {
monday: 8,
tuesday: 8,
wednesday: 8,
thursday: 8,
friday: 8,
},
},
allAllocationsForResource: [],
},
],
},
},
newStartDate: new Date("2026-04-03"),
newEndDate: new Date("2026-04-12"),
});
expect(result).toBe(expectedValidation);
expect(validateShiftMock).toHaveBeenCalledWith({
project: {
id: "project_1",
budgetCents: 200_000,
winProbability: 80,
startDate: new Date("2026-04-01"),
endDate: new Date("2026-04-10"),
},
newStartDate: new Date("2026-04-03"),
newEndDate: new Date("2026-04-12"),
allocations: [
expect.objectContaining({
id: "assignment_1",
resourceId: "resource_1",
}),
],
});
});
it("throws a BAD_REQUEST error for invalid shift validations", () => {
expect(() =>
assertTimelineProjectShiftValid(createValidValidationResult({
valid: false,
errors: [
{
code: "AVAILABILITY_CONFLICT",
message: "Alice exceeds capacity",
},
],
})),
).toThrowError(new TRPCError({
code: "BAD_REQUEST",
message: "Shift validation failed: Alice exceeds capacity",
}));
});
it("builds project shifted event payloads", () => {
const validation = createValidValidationResult();
expect(
buildTimelineProjectShiftEventPayload({
projectId: "project_1",
newStartDate: new Date("2026-04-03T00:00:00.000Z"),
newEndDate: new Date("2026-04-12T00:00:00.000Z"),
validation,
assignments: [
{ resourceId: "resource_1" },
{ resourceId: null },
] as never,
}),
).toEqual({
projectId: "project_1",
newStartDate: "2026-04-03T00:00:00.000Z",
newEndDate: "2026-04-12T00:00:00.000Z",
costDeltaCents: 12_000,
resourceIds: ["resource_1", null],
});
});
});