refactor(api): extract scenario procedures

This commit is contained in:
2026-03-31 19:18:56 +02:00
parent 814d5adfde
commit f7fe5c6d19
3 changed files with 173 additions and 46 deletions
@@ -0,0 +1,104 @@
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",
});
});
});