4a49ec4f05
- G-01: ProjectWizard renders blueprint fieldDefs with DynamicFieldInput component - G-02: Blueprint rolePresets validated via RolePresetsSchema in wizard; API keeps loose schema - G-03: ProjectWizard step 2/3 validation (role, hoursPerDay, headcount required) - G-04: EstimateWizard validates baseCurrency and demand line cost rates - G-05: Project lifecycle transition guards with ALLOWED_TRANSITIONS map - G-06: Blueprint validator extended for minLength/maxLength/pattern and DATE range checks - G-07: assertBlueprintDynamicFields merges global blueprint fieldDefs into validation - G-08: (tracked — chapter managed dropdown; deferred to backend ticket) - G-09: JSDoc added to lcrCents/ucrCents clarifying LCR/UCR terminology - G-10: Dispo route redirect already in place — closed as done - G-11: packages/ui empty by design — closed as documented - G-12: @deprecated JSDoc added to CreateAllocationSchema and UpdateAllocationSchema - G-13: ProjectWizard review step enhanced with blueprint name, field values, skills, assignments - G-14: ProjectWizard handleSubmit collects per-item warnings instead of silent swallowing - G-15: Vacation cancel reverses usedDays entitlement for APPROVED ANNUAL/OTHER vacations Tests: all 1575 passing (1 pre-existing failure in insights-summary unrelated to these changes) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
103 lines
2.9 KiB
TypeScript
103 lines
2.9 KiB
TypeScript
import { BlueprintTarget, FieldType, type BlueprintFieldDefinition } from "@capakraken/shared";
|
|
import { TRPCError } from "@trpc/server";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { assertBlueprintDynamicFields } from "../router/blueprint-validation.js";
|
|
|
|
function createDbMock(result: { fieldDefs: unknown; target: BlueprintTarget } | null) {
|
|
return {
|
|
blueprint: {
|
|
findUnique: vi.fn().mockResolvedValue(result),
|
|
findMany: vi.fn().mockResolvedValue([]),
|
|
},
|
|
};
|
|
}
|
|
|
|
describe("assertBlueprintDynamicFields", () => {
|
|
it("returns early when no blueprint is set", async () => {
|
|
const db = createDbMock(null);
|
|
|
|
await expect(
|
|
assertBlueprintDynamicFields({
|
|
db,
|
|
blueprintId: undefined,
|
|
dynamicFields: {},
|
|
target: BlueprintTarget.PROJECT,
|
|
}),
|
|
).resolves.toBeUndefined();
|
|
|
|
expect(db.blueprint.findUnique).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("rejects a missing blueprint", async () => {
|
|
const db = createDbMock(null);
|
|
|
|
await expect(
|
|
assertBlueprintDynamicFields({
|
|
db,
|
|
blueprintId: "bp_missing",
|
|
dynamicFields: {},
|
|
target: BlueprintTarget.PROJECT,
|
|
}),
|
|
).rejects.toMatchObject({ code: "NOT_FOUND" } satisfies Partial<TRPCError>);
|
|
});
|
|
|
|
it("rejects a blueprint with the wrong target", async () => {
|
|
const db = createDbMock({ fieldDefs: [], target: BlueprintTarget.RESOURCE });
|
|
|
|
await expect(
|
|
assertBlueprintDynamicFields({
|
|
db,
|
|
blueprintId: "bp_resource",
|
|
dynamicFields: {},
|
|
target: BlueprintTarget.PROJECT,
|
|
}),
|
|
).rejects.toMatchObject({ code: "BAD_REQUEST" } satisfies Partial<TRPCError>);
|
|
});
|
|
|
|
it("rejects invalid dynamic field values", async () => {
|
|
const fieldDefs: BlueprintFieldDefinition[] = [
|
|
{
|
|
id: "cost-center",
|
|
key: "costCenter",
|
|
label: "Cost Center",
|
|
order: 0,
|
|
type: FieldType.NUMBER,
|
|
required: true,
|
|
},
|
|
];
|
|
const db = createDbMock({ fieldDefs, target: BlueprintTarget.PROJECT });
|
|
|
|
await expect(
|
|
assertBlueprintDynamicFields({
|
|
db,
|
|
blueprintId: "bp_project",
|
|
dynamicFields: { costCenter: "abc" },
|
|
target: BlueprintTarget.PROJECT,
|
|
}),
|
|
).rejects.toMatchObject({ code: "UNPROCESSABLE_CONTENT" } satisfies Partial<TRPCError>);
|
|
});
|
|
|
|
it("accepts valid dynamic field values", async () => {
|
|
const fieldDefs: BlueprintFieldDefinition[] = [
|
|
{
|
|
id: "cost-center",
|
|
key: "costCenter",
|
|
label: "Cost Center",
|
|
order: 0,
|
|
type: FieldType.NUMBER,
|
|
required: true,
|
|
},
|
|
];
|
|
const db = createDbMock({ fieldDefs, target: BlueprintTarget.PROJECT });
|
|
|
|
await expect(
|
|
assertBlueprintDynamicFields({
|
|
db,
|
|
blueprintId: "bp_project",
|
|
dynamicFields: { costCenter: 42 },
|
|
target: BlueprintTarget.PROJECT,
|
|
}),
|
|
).resolves.toBeUndefined();
|
|
});
|
|
});
|