import { describe, expect, it } from "vitest"; import { CreateBlueprintSchema, UpdateBlueprintSchema, BlueprintFieldDefinitionSchema, generateDynamicZodSchema, } from "../schemas/blueprint.schema.js"; import { CreateOrgUnitSchema, UpdateOrgUnitSchema } from "../schemas/org-unit.schema.js"; import { CreateManagementLevelGroupSchema, UpdateManagementLevelGroupSchema, CreateManagementLevelSchema, UpdateManagementLevelSchema, } from "../schemas/management-level.schema.js"; import { CreateUtilizationCategorySchema, UpdateUtilizationCategorySchema, } from "../schemas/utilization-category.schema.js"; import { CreateHolidayCalendarSchema, UpdateHolidayCalendarSchema, CreateHolidayCalendarEntrySchema, UpdateHolidayCalendarEntrySchema, PreviewResolvedHolidaysSchema, } from "../schemas/holiday-calendar.schema.js"; import { CreateCalculationRuleSchema, UpdateCalculationRuleSchema, } from "../schemas/calculation-rules.schema.js"; import { BlueprintTarget, FieldType } from "../types/enums.js"; // ─── BlueprintFieldDefinitionSchema ───────────────────────────────────────── describe("BlueprintFieldDefinitionSchema", () => { const validField = { id: "field-1", label: "Project Name", key: "project_name", type: FieldType.TEXT, order: 0, }; it("accepts a minimal valid field definition", () => { const result = BlueprintFieldDefinitionSchema.parse(validField); expect(result.id).toBe("field-1"); expect(result.required).toBe(false); }); it("accepts a full field with options and validation", () => { const result = BlueprintFieldDefinitionSchema.parse({ ...validField, required: true, description: "The name of the project", placeholder: "Enter name…", defaultValue: "Untitled", options: [{ value: "a", label: "Option A", color: "#ff0000" }], validation: { minLength: 2, maxLength: 50 }, group: "General", }); expect(result.required).toBe(true); expect(result.options).toHaveLength(1); expect(result.validation?.maxLength).toBe(50); expect(result.group).toBe("General"); }); it("rejects key that does not match snake_case pattern", () => { expect(() => BlueprintFieldDefinitionSchema.parse({ ...validField, key: "Project-Name" }), ).toThrow(); }); it("rejects key starting with a digit", () => { expect(() => BlueprintFieldDefinitionSchema.parse({ ...validField, key: "1bad_key" }), ).toThrow(); }); it("rejects negative order value", () => { expect(() => BlueprintFieldDefinitionSchema.parse({ ...validField, order: -1 })).toThrow(); }); it("rejects label longer than 200 characters", () => { expect(() => BlueprintFieldDefinitionSchema.parse({ ...validField, label: "x".repeat(201) }), ).toThrow(); }); }); // ─── CreateBlueprintSchema ─────────────────────────────────────────────────── describe("CreateBlueprintSchema", () => { it("accepts minimal valid input and applies defaults", () => { const result = CreateBlueprintSchema.parse({ name: "Project Template", target: BlueprintTarget.PROJECT, }); expect(result.name).toBe("Project Template"); expect(result.target).toBe("PROJECT"); expect(result.fieldDefs).toEqual([]); expect(result.defaults).toEqual({}); expect(result.validationRules).toEqual([]); expect(result.description).toBeUndefined(); }); it("accepts full input with fieldDefs and validationRules", () => { const result = CreateBlueprintSchema.parse({ name: "Resource Blueprint", target: BlueprintTarget.RESOURCE, description: "Used for all 3D artists", fieldDefs: [ { id: "f1", label: "Skill Level", key: "skill_level", type: FieldType.SELECT, order: 0, options: [ { value: "junior", label: "Junior" }, { value: "senior", label: "Senior" }, ], }, ], defaults: { skill_level: "junior" }, validationRules: [{ field: "skill_level", rule: "required_if", params: { other: "active" } }], }); expect(result.fieldDefs).toHaveLength(1); expect(result.defaults.skill_level).toBe("junior"); expect(result.validationRules[0]?.rule).toBe("required_if"); }); it("rejects empty name", () => { expect(() => CreateBlueprintSchema.parse({ name: "", target: BlueprintTarget.PROJECT }), ).toThrow(); }); it("rejects name longer than 200 characters", () => { expect(() => CreateBlueprintSchema.parse({ name: "x".repeat(201), target: BlueprintTarget.PROJECT }), ).toThrow(); }); it("rejects invalid BlueprintTarget enum value", () => { expect(() => CreateBlueprintSchema.parse({ name: "Test", target: "TEAM" })).toThrow(); }); }); // ─── UpdateBlueprintSchema ─────────────────────────────────────────────────── describe("UpdateBlueprintSchema", () => { it("accepts an empty object (all fields optional)", () => { const result = UpdateBlueprintSchema.parse({}); expect(result.name).toBeUndefined(); expect(result.target).toBeUndefined(); }); it("accepts a partial update with only name", () => { const result = UpdateBlueprintSchema.parse({ name: "Updated Name" }); expect(result.name).toBe("Updated Name"); expect(result.fieldDefs).toBeUndefined(); }); it("accepts a partial update with only target", () => { const result = UpdateBlueprintSchema.parse({ target: BlueprintTarget.RESOURCE }); expect(result.target).toBe("RESOURCE"); }); it("rejects invalid target even in partial update", () => { expect(() => UpdateBlueprintSchema.parse({ target: "INVALID" })).toThrow(); }); }); // ─── generateDynamicZodSchema ──────────────────────────────────────────────── describe("generateDynamicZodSchema", () => { it("generates a TEXT field as optional string by default", () => { const schema = generateDynamicZodSchema([ { id: "f1", label: "Title", key: "title", type: FieldType.TEXT, order: 0, required: false }, ]); const result = schema.parse({}); expect(result.title).toBeUndefined(); const withValue = schema.parse({ title: "Hello" }); expect(withValue.title).toBe("Hello"); }); it("generates a TEXT field as required when required=true", () => { const schema = generateDynamicZodSchema([ { id: "f1", label: "Title", key: "title", type: FieldType.TEXT, order: 0, required: true }, ]); expect(() => schema.parse({})).toThrow(); expect(schema.parse({ title: "Hello" }).title).toBe("Hello"); }); it("generates a NUMBER field with min/max validation", () => { const schema = generateDynamicZodSchema([ { id: "f2", label: "Rating", key: "rating", type: FieldType.NUMBER, order: 0, required: true, validation: { min: 1, max: 10 }, }, ]); expect(schema.parse({ rating: 5 }).rating).toBe(5); expect(() => schema.parse({ rating: 0 })).toThrow(); expect(() => schema.parse({ rating: 11 })).toThrow(); }); it("generates a BOOLEAN field", () => { const schema = generateDynamicZodSchema([ { id: "f3", label: "Active", key: "is_active", type: FieldType.BOOLEAN, order: 0, required: true, }, ]); expect(schema.parse({ is_active: true }).is_active).toBe(true); expect(schema.parse({ is_active: false }).is_active).toBe(false); expect(() => schema.parse({ is_active: "yes" })).toThrow(); }); it("generates a DATE field using coerce", () => { const schema = generateDynamicZodSchema([ { id: "f4", label: "Start Date", key: "start_date", type: FieldType.DATE, order: 0, required: true, }, ]); const result = schema.parse({ start_date: "2026-01-15" }); expect(result.start_date).toBeInstanceOf(Date); }); it("generates a SELECT field restricted to defined option values", () => { const schema = generateDynamicZodSchema([ { id: "f5", label: "Status", key: "status", type: FieldType.SELECT, order: 0, required: true, options: [ { value: "draft", label: "Draft" }, { value: "active", label: "Active" }, ], }, ]); expect(schema.parse({ status: "draft" }).status).toBe("draft"); expect(() => schema.parse({ status: "unknown" })).toThrow(); }); it("generates a MULTI_SELECT field as an array of valid enum values", () => { const schema = generateDynamicZodSchema([ { id: "f6", label: "Tags", key: "tags", type: FieldType.MULTI_SELECT, order: 0, required: true, options: [ { value: "cg", label: "CG" }, { value: "vfx", label: "VFX" }, ], }, ]); expect(schema.parse({ tags: ["cg", "vfx"] }).tags).toEqual(["cg", "vfx"]); expect(() => schema.parse({ tags: ["cg", "invalid"] })).toThrow(); }); it("generates an EMAIL field that validates email format", () => { const schema = generateDynamicZodSchema([ { id: "f7", label: "Email", key: "email", type: FieldType.EMAIL, order: 0, required: true }, ]); expect(schema.parse({ email: "user@example.com" }).email).toBe("user@example.com"); expect(() => schema.parse({ email: "not-an-email" })).toThrow(); }); it("generates a URL field that validates URL format", () => { const schema = generateDynamicZodSchema([ { id: "f8", label: "Website", key: "website", type: FieldType.URL, order: 0, required: true }, ]); expect(schema.parse({ website: "https://example.com" }).website).toBe("https://example.com"); expect(() => schema.parse({ website: "not-a-url" })).toThrow(); }); it("generates a TEXT field with minLength/maxLength validation", () => { const schema = generateDynamicZodSchema([ { id: "f9", label: "Code", key: "code", type: FieldType.TEXT, order: 0, required: true, validation: { minLength: 3, maxLength: 8 }, }, ]); expect(schema.parse({ code: "ABC" }).code).toBe("ABC"); expect(() => schema.parse({ code: "AB" })).toThrow(); expect(() => schema.parse({ code: "TOOLONGCODE" })).toThrow(); }); it("handles a SELECT field with no options as a plain string", () => { const schema = generateDynamicZodSchema([ { id: "f10", label: "Tag", key: "tag", type: FieldType.SELECT, order: 0, required: true }, ]); expect(schema.parse({ tag: "anything" }).tag).toBe("anything"); }); }); // ─── CreateOrgUnitSchema ───────────────────────────────────────────────────── describe("CreateOrgUnitSchema", () => { it("accepts valid minimal input with defaults", () => { const result = CreateOrgUnitSchema.parse({ name: "Studio Munich", level: 5 }); expect(result.name).toBe("Studio Munich"); expect(result.level).toBe(5); expect(result.sortOrder).toBe(0); expect(result.shortName).toBeUndefined(); expect(result.parentId).toBeUndefined(); }); it("accepts full input with all optional fields", () => { const result = CreateOrgUnitSchema.parse({ name: "Department VFX", shortName: "VFX", level: 6, parentId: "parent-1", sortOrder: 3, }); expect(result.shortName).toBe("VFX"); expect(result.parentId).toBe("parent-1"); expect(result.sortOrder).toBe(3); }); it("rejects level below 5", () => { expect(() => CreateOrgUnitSchema.parse({ name: "Test", level: 4 })).toThrow(); }); it("rejects level above 7", () => { expect(() => CreateOrgUnitSchema.parse({ name: "Test", level: 8 })).toThrow(); }); it("rejects shortName longer than 50 characters", () => { expect(() => CreateOrgUnitSchema.parse({ name: "Test", level: 5, shortName: "x".repeat(51) }), ).toThrow(); }); }); // ─── UpdateOrgUnitSchema ───────────────────────────────────────────────────── describe("UpdateOrgUnitSchema", () => { it("accepts empty object (all optional)", () => { const result = UpdateOrgUnitSchema.parse({}); expect(result.name).toBeUndefined(); expect(result.isActive).toBeUndefined(); }); it("accepts nullable shortName to clear the field", () => { const result = UpdateOrgUnitSchema.parse({ shortName: null }); expect(result.shortName).toBeNull(); }); it("accepts nullable parentId to unset parent", () => { const result = UpdateOrgUnitSchema.parse({ parentId: null }); expect(result.parentId).toBeNull(); }); it("accepts isActive flag", () => { const result = UpdateOrgUnitSchema.parse({ isActive: false }); expect(result.isActive).toBe(false); }); it("rejects name longer than 200 characters", () => { expect(() => UpdateOrgUnitSchema.parse({ name: "x".repeat(201) })).toThrow(); }); }); // ─── CreateManagementLevelGroupSchema ─────────────────────────────────────── describe("CreateManagementLevelGroupSchema", () => { it("accepts valid input with defaults", () => { const result = CreateManagementLevelGroupSchema.parse({ name: "Senior Management", targetPercentage: 0.15, }); expect(result.name).toBe("Senior Management"); expect(result.targetPercentage).toBe(0.15); expect(result.sortOrder).toBe(0); }); it("accepts boundary values for targetPercentage (0 and 1)", () => { expect( CreateManagementLevelGroupSchema.parse({ name: "A", targetPercentage: 0 }).targetPercentage, ).toBe(0); expect( CreateManagementLevelGroupSchema.parse({ name: "B", targetPercentage: 1 }).targetPercentage, ).toBe(1); }); it("rejects targetPercentage above 1", () => { expect(() => CreateManagementLevelGroupSchema.parse({ name: "Test", targetPercentage: 1.01 }), ).toThrow(); }); it("rejects targetPercentage below 0", () => { expect(() => CreateManagementLevelGroupSchema.parse({ name: "Test", targetPercentage: -0.01 }), ).toThrow(); }); it("rejects empty name", () => { expect(() => CreateManagementLevelGroupSchema.parse({ name: "", targetPercentage: 0.1 }), ).toThrow(); }); }); // ─── UpdateManagementLevelGroupSchema ─────────────────────────────────────── describe("UpdateManagementLevelGroupSchema", () => { it("accepts empty object", () => { const result = UpdateManagementLevelGroupSchema.parse({}); expect(result.name).toBeUndefined(); expect(result.targetPercentage).toBeUndefined(); }); it("accepts partial update with only sortOrder", () => { const result = UpdateManagementLevelGroupSchema.parse({ sortOrder: 5 }); expect(result.sortOrder).toBe(5); expect(result.name).toBeUndefined(); }); it("rejects targetPercentage out of range in partial update", () => { expect(() => UpdateManagementLevelGroupSchema.parse({ targetPercentage: 2 })).toThrow(); }); }); // ─── CreateManagementLevelSchema ──────────────────────────────────────────── describe("CreateManagementLevelSchema", () => { it("accepts valid input", () => { const result = CreateManagementLevelSchema.parse({ name: "Director", groupId: "grp-1" }); expect(result.name).toBe("Director"); expect(result.groupId).toBe("grp-1"); }); it("rejects empty name", () => { expect(() => CreateManagementLevelSchema.parse({ name: "", groupId: "grp-1" })).toThrow(); }); it("rejects missing groupId", () => { expect(() => CreateManagementLevelSchema.parse({ name: "Director" })).toThrow(); }); it("rejects name longer than 100 characters", () => { expect(() => CreateManagementLevelSchema.parse({ name: "x".repeat(101), groupId: "grp-1" }), ).toThrow(); }); }); // ─── UpdateManagementLevelSchema ──────────────────────────────────────────── describe("UpdateManagementLevelSchema", () => { it("accepts empty object", () => { const result = UpdateManagementLevelSchema.parse({}); expect(result.name).toBeUndefined(); expect(result.groupId).toBeUndefined(); }); it("accepts partial update with only groupId", () => { const result = UpdateManagementLevelSchema.parse({ groupId: "grp-2" }); expect(result.groupId).toBe("grp-2"); }); it("rejects empty name in partial update", () => { expect(() => UpdateManagementLevelSchema.parse({ name: "" })).toThrow(); }); }); // ─── CreateUtilizationCategorySchema ──────────────────────────────────────── describe("CreateUtilizationCategorySchema", () => { it("accepts minimal valid input with defaults", () => { const result = CreateUtilizationCategorySchema.parse({ code: "PROD", name: "Production" }); expect(result.code).toBe("PROD"); expect(result.name).toBe("Production"); expect(result.sortOrder).toBe(0); expect(result.isDefault).toBe(false); expect(result.description).toBeUndefined(); }); it("accepts full input including description", () => { const result = CreateUtilizationCategorySchema.parse({ code: "SICK", name: "Sick Leave", description: "Covers illness absence", sortOrder: 2, isDefault: true, }); expect(result.isDefault).toBe(true); expect(result.description).toBe("Covers illness absence"); }); it("rejects code longer than 20 characters", () => { expect(() => CreateUtilizationCategorySchema.parse({ code: "x".repeat(21), name: "Test" }), ).toThrow(); }); it("rejects description longer than 500 characters", () => { expect(() => CreateUtilizationCategorySchema.parse({ code: "ABC", name: "Test", description: "x".repeat(501), }), ).toThrow(); }); it("rejects empty code", () => { expect(() => CreateUtilizationCategorySchema.parse({ code: "", name: "Test" })).toThrow(); }); }); // ─── UpdateUtilizationCategorySchema ──────────────────────────────────────── describe("UpdateUtilizationCategorySchema", () => { it("accepts empty object", () => { const result = UpdateUtilizationCategorySchema.parse({}); expect(result.code).toBeUndefined(); expect(result.isDefault).toBeUndefined(); }); it("accepts nullable description to clear the field", () => { const result = UpdateUtilizationCategorySchema.parse({ description: null }); expect(result.description).toBeNull(); }); it("accepts isActive flag", () => { const result = UpdateUtilizationCategorySchema.parse({ isActive: false }); expect(result.isActive).toBe(false); }); it("rejects code longer than 20 characters in partial update", () => { expect(() => UpdateUtilizationCategorySchema.parse({ code: "x".repeat(21) })).toThrow(); }); }); // ─── CreateHolidayCalendarSchema ───────────────────────────────────────────── describe("CreateHolidayCalendarSchema", () => { it("accepts valid COUNTRY-scope calendar", () => { const result = CreateHolidayCalendarSchema.parse({ name: "Germany Public Holidays", scopeType: "COUNTRY", countryId: "DE", }); expect(result.name).toBe("Germany Public Holidays"); expect(result.scopeType).toBe("COUNTRY"); expect(result.stateCode).toBeUndefined(); expect(result.priority).toBeUndefined(); }); it("accepts valid STATE-scope calendar with stateCode", () => { const result = CreateHolidayCalendarSchema.parse({ name: "Bavaria Holidays", scopeType: "STATE", countryId: "DE", stateCode: "BY", priority: 10, isActive: true, }); expect(result.stateCode).toBe("BY"); expect(result.priority).toBe(10); }); it("rejects invalid scopeType", () => { expect(() => CreateHolidayCalendarSchema.parse({ name: "Test", scopeType: "REGION", countryId: "DE" }), ).toThrow(); }); it("rejects priority below -100", () => { expect(() => CreateHolidayCalendarSchema.parse({ name: "Test", scopeType: "COUNTRY", countryId: "DE", priority: -101, }), ).toThrow(); }); it("rejects priority above 100", () => { expect(() => CreateHolidayCalendarSchema.parse({ name: "Test", scopeType: "COUNTRY", countryId: "DE", priority: 101, }), ).toThrow(); }); it("rejects stateCode longer than 16 characters", () => { expect(() => CreateHolidayCalendarSchema.parse({ name: "Test", scopeType: "STATE", countryId: "DE", stateCode: "x".repeat(17), }), ).toThrow(); }); }); // ─── UpdateHolidayCalendarSchema ───────────────────────────────────────────── describe("UpdateHolidayCalendarSchema", () => { it("accepts empty object", () => { const result = UpdateHolidayCalendarSchema.parse({}); expect(result.name).toBeUndefined(); expect(result.stateCode).toBeUndefined(); }); it("accepts nullable stateCode to clear it", () => { const result = UpdateHolidayCalendarSchema.parse({ stateCode: null }); expect(result.stateCode).toBeNull(); }); it("accepts partial update with name and isActive", () => { const result = UpdateHolidayCalendarSchema.parse({ name: "Updated Calendar", isActive: false }); expect(result.name).toBe("Updated Calendar"); expect(result.isActive).toBe(false); }); it("rejects name longer than 120 characters", () => { expect(() => UpdateHolidayCalendarSchema.parse({ name: "x".repeat(121) })).toThrow(); }); }); // ─── CreateHolidayCalendarEntrySchema ──────────────────────────────────────── describe("CreateHolidayCalendarEntrySchema", () => { it("accepts valid entry with date coercion from string", () => { const result = CreateHolidayCalendarEntrySchema.parse({ holidayCalendarId: "cal-1", date: "2026-12-25", name: "Christmas Day", }); expect(result.holidayCalendarId).toBe("cal-1"); expect(result.date).toBeInstanceOf(Date); expect(result.name).toBe("Christmas Day"); expect(result.isRecurringAnnual).toBeUndefined(); }); it("accepts full entry with all optional fields", () => { const result = CreateHolidayCalendarEntrySchema.parse({ holidayCalendarId: "cal-1", date: "2026-10-03", name: "German Unity Day", isRecurringAnnual: true, source: "Official Federal Calendar", }); expect(result.isRecurringAnnual).toBe(true); expect(result.source).toBe("Official Federal Calendar"); }); it("rejects empty name", () => { expect(() => CreateHolidayCalendarEntrySchema.parse({ holidayCalendarId: "cal-1", date: "2026-01-01", name: "", }), ).toThrow(); }); it("rejects missing holidayCalendarId", () => { expect(() => CreateHolidayCalendarEntrySchema.parse({ date: "2026-01-01", name: "New Year" }), ).toThrow(); }); }); // ─── UpdateHolidayCalendarEntrySchema ──────────────────────────────────────── describe("UpdateHolidayCalendarEntrySchema", () => { it("accepts empty object", () => { const result = UpdateHolidayCalendarEntrySchema.parse({}); expect(result.name).toBeUndefined(); expect(result.date).toBeUndefined(); }); it("accepts partial update with date coercion", () => { const result = UpdateHolidayCalendarEntrySchema.parse({ date: "2027-01-01" }); expect(result.date).toBeInstanceOf(Date); }); it("accepts nullable source to clear it", () => { const result = UpdateHolidayCalendarEntrySchema.parse({ source: null }); expect(result.source).toBeNull(); }); }); // ─── PreviewResolvedHolidaysSchema ─────────────────────────────────────────── describe("PreviewResolvedHolidaysSchema", () => { it("accepts valid input with required fields", () => { const result = PreviewResolvedHolidaysSchema.parse({ countryId: "DE", year: 2026 }); expect(result.countryId).toBe("DE"); expect(result.year).toBe(2026); expect(result.stateCode).toBeUndefined(); }); it("accepts input with optional stateCode and metroCityId", () => { const result = PreviewResolvedHolidaysSchema.parse({ countryId: "DE", stateCode: "BY", metroCityId: "city-1", year: 2026, }); expect(result.stateCode).toBe("BY"); expect(result.metroCityId).toBe("city-1"); }); it("rejects year below 2000", () => { expect(() => PreviewResolvedHolidaysSchema.parse({ countryId: "DE", year: 1999 })).toThrow(); }); it("rejects year above 2100", () => { expect(() => PreviewResolvedHolidaysSchema.parse({ countryId: "DE", year: 2101 })).toThrow(); }); it("rejects non-integer year", () => { expect(() => PreviewResolvedHolidaysSchema.parse({ countryId: "DE", year: 2026.5 })).toThrow(); }); }); // ─── CreateCalculationRuleSchema ───────────────────────────────────────────── describe("CreateCalculationRuleSchema", () => { const validRule = { name: "Public Holiday Charge Rule", triggerType: "PUBLIC_HOLIDAY", costEffect: "CHARGE", chargeabilityEffect: "COUNT", }; it("accepts minimal valid input with defaults", () => { const result = CreateCalculationRuleSchema.parse(validRule); expect(result.name).toBe("Public Holiday Charge Rule"); expect(result.triggerType).toBe("PUBLIC_HOLIDAY"); expect(result.costEffect).toBe("CHARGE"); expect(result.chargeabilityEffect).toBe("COUNT"); expect(result.priority).toBe(0); expect(result.isActive).toBe(true); expect(result.description).toBeUndefined(); }); it("accepts full input with all optional fields", () => { const result = CreateCalculationRuleSchema.parse({ ...validRule, description: "Charges client on public holidays", triggerType: "SICK", costEffect: "REDUCE", costReductionPercent: 50, projectId: "proj-1", orderType: "CHARGEABLE", chargeabilityEffect: "SKIP", priority: 100, isActive: false, }); expect(result.costReductionPercent).toBe(50); expect(result.triggerType).toBe("SICK"); expect(result.chargeabilityEffect).toBe("SKIP"); expect(result.isActive).toBe(false); }); it("rejects invalid triggerType", () => { expect(() => CreateCalculationRuleSchema.parse({ ...validRule, triggerType: "OVERTIME" }), ).toThrow(); }); it("rejects costReductionPercent above 100", () => { expect(() => CreateCalculationRuleSchema.parse({ ...validRule, costReductionPercent: 101 }), ).toThrow(); }); it("rejects priority above 1000", () => { expect(() => CreateCalculationRuleSchema.parse({ ...validRule, priority: 1001 })).toThrow(); }); it("rejects priority below 0", () => { expect(() => CreateCalculationRuleSchema.parse({ ...validRule, priority: -1 })).toThrow(); }); }); // ─── UpdateCalculationRuleSchema ───────────────────────────────────────────── describe("UpdateCalculationRuleSchema", () => { it("accepts minimal input with only required id", () => { const result = UpdateCalculationRuleSchema.parse({ id: "rule-1" }); expect(result.id).toBe("rule-1"); expect(result.name).toBeUndefined(); expect(result.triggerType).toBeUndefined(); }); it("accepts partial update with name and priority", () => { const result = UpdateCalculationRuleSchema.parse({ id: "rule-1", name: "Updated Rule", priority: 5, }); expect(result.name).toBe("Updated Rule"); expect(result.priority).toBe(5); }); it("requires id to be present", () => { expect(() => UpdateCalculationRuleSchema.parse({ name: "Some Rule" })).toThrow(); }); it("rejects invalid costEffect in partial update", () => { expect(() => UpdateCalculationRuleSchema.parse({ id: "rule-1", costEffect: "IGNORE" }), ).toThrow(); }); });