105 lines
3.1 KiB
TypeScript
105 lines
3.1 KiB
TypeScript
import { PermissionKey } from "@capakraken/shared";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
applyScenario,
|
|
getProjectScenarioBaseline,
|
|
ScenarioSimulationInputSchema,
|
|
simulateScenario,
|
|
} from "../router/scenario-procedure-support.js";
|
|
|
|
const {
|
|
readProjectScenarioBaseline,
|
|
simulateProjectScenario,
|
|
applyProjectScenario,
|
|
} = vi.hoisted(() => ({
|
|
readProjectScenarioBaseline: vi.fn(),
|
|
simulateProjectScenario: vi.fn(),
|
|
applyProjectScenario: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("../router/scenario-baseline.js", () => ({
|
|
readProjectScenarioBaseline,
|
|
}));
|
|
|
|
vi.mock("../router/scenario-simulation.js", () => ({
|
|
simulateProjectScenario,
|
|
}));
|
|
|
|
vi.mock("../router/scenario-apply.js", () => ({
|
|
applyProjectScenario,
|
|
}));
|
|
|
|
describe("scenario procedure support", () => {
|
|
beforeEach(() => {
|
|
readProjectScenarioBaseline.mockReset();
|
|
simulateProjectScenario.mockReset();
|
|
applyProjectScenario.mockReset();
|
|
});
|
|
|
|
it("enforces at least one scenario change at the schema boundary", () => {
|
|
expect(() => ScenarioSimulationInputSchema.parse({
|
|
projectId: "project_1",
|
|
changes: [],
|
|
})).toThrowError(/at least 1 element/i);
|
|
});
|
|
|
|
it("reads the project baseline only after cost permission is present", async () => {
|
|
readProjectScenarioBaseline.mockResolvedValue({ project: { id: "project_1" } });
|
|
|
|
const result = await getProjectScenarioBaseline({
|
|
db: { project: {} } as never,
|
|
dbUser: { id: "user_1", systemRole: "USER", permissionOverrides: null },
|
|
permissions: new Set([PermissionKey.VIEW_COSTS]),
|
|
}, {
|
|
projectId: "project_1",
|
|
});
|
|
|
|
expect(result).toEqual({ project: { id: "project_1" } });
|
|
expect(readProjectScenarioBaseline).toHaveBeenCalledWith(expect.anything(), "project_1");
|
|
});
|
|
|
|
it("delegates pure simulation to the shared simulation service", async () => {
|
|
simulateProjectScenario.mockResolvedValue({ warnings: [] });
|
|
const ctx = { db: { project: {} } };
|
|
const input = {
|
|
projectId: "project_1",
|
|
changes: [{
|
|
resourceId: "resource_1",
|
|
startDate: new Date("2026-04-01T00:00:00.000Z"),
|
|
endDate: new Date("2026-04-02T00:00:00.000Z"),
|
|
hoursPerDay: 8,
|
|
}],
|
|
};
|
|
|
|
const result = await simulateScenario(ctx as never, input);
|
|
|
|
expect(result).toEqual({ warnings: [] });
|
|
expect(simulateProjectScenario).toHaveBeenCalledWith(ctx.db, input);
|
|
});
|
|
|
|
it("passes the acting user id through when applying a scenario", async () => {
|
|
applyProjectScenario.mockResolvedValue({ appliedCount: 2 });
|
|
const ctx = {
|
|
db: { assignment: {} },
|
|
dbUser: { id: "user_1", systemRole: "CONTROLLER", permissionOverrides: null },
|
|
};
|
|
const input = {
|
|
projectId: "project_1",
|
|
changes: [{
|
|
assignmentId: "assignment_1",
|
|
startDate: new Date("2026-04-01T00:00:00.000Z"),
|
|
endDate: new Date("2026-04-03T00:00:00.000Z"),
|
|
hoursPerDay: 6,
|
|
}],
|
|
};
|
|
|
|
const result = await applyScenario(ctx as never, input);
|
|
|
|
expect(result).toEqual({ appliedCount: 2 });
|
|
expect(applyProjectScenario).toHaveBeenCalledWith(ctx.db, {
|
|
...input,
|
|
userId: "user_1",
|
|
});
|
|
});
|
|
});
|