import { describe, expect, it } from "vitest"; import { buildEstimateDemandLineRows, buildEffortRuleCreateManyRows, buildEffortRuleNestedCreateRows, buildEffortRuleSetCreateData, buildEffortRuleSetUpdateData, effortRuleInclude, toEffortRuleEngineInputs, toScopeItemInputs, } from "../router/effort-rule-support.js"; describe("effort rule support", () => { it("exposes the rule include ordering", () => { expect(effortRuleInclude).toEqual({ rules: { orderBy: { sortOrder: "asc" } }, }); }); it("builds rule rows and preserves fallback sort order", () => { expect(buildEffortRuleCreateManyRows([ { scopeType: "shot", discipline: "Compositing", chapter: "VFX", unitMode: "per_frame", hoursPerUnit: 0.08, }, { scopeType: "asset", discipline: "Modeling", unitMode: "per_item", hoursPerUnit: 8, description: "Hero asset", sortOrder: 5, }, ], "ers_1")).toEqual([ { ruleSetId: "ers_1", scopeType: "shot", discipline: "Compositing", chapter: "VFX", unitMode: "per_frame", hoursPerUnit: 0.08, sortOrder: 0, }, { ruleSetId: "ers_1", scopeType: "asset", discipline: "Modeling", unitMode: "per_item", hoursPerUnit: 8, description: "Hero asset", sortOrder: 5, }, ]); expect(buildEffortRuleNestedCreateRows([ { scopeType: "shot", discipline: "Compositing", unitMode: "per_frame", hoursPerUnit: 0.08, }, ])).toEqual([ { scopeType: "shot", discipline: "Compositing", unitMode: "per_frame", hoursPerUnit: 0.08, sortOrder: 0, }, ]); }); it("builds create and sparse update payloads", () => { expect(buildEffortRuleSetCreateData({ name: "VFX Standard", description: "Default effort rules", isDefault: true, rules: [ { scopeType: "shot", discipline: "Compositing", unitMode: "per_frame", hoursPerUnit: 0.08, sortOrder: 0, }, ], })).toEqual({ name: "VFX Standard", description: "Default effort rules", isDefault: true, rules: { create: [ { scopeType: "shot", discipline: "Compositing", unitMode: "per_frame", hoursPerUnit: 0.08, sortOrder: 0, }, ], }, }); expect(buildEffortRuleSetUpdateData({ description: null, isDefault: false, })).toEqual({ description: null, isDefault: false, }); }); it("maps scope items and rules into engine inputs", () => { expect(toScopeItemInputs([ { name: "Shot_001", scopeType: "shot", frameCount: 100, itemCount: null, unitMode: "per_frame", }, ])).toEqual([ { name: "Shot_001", scopeType: "shot", frameCount: 100, itemCount: null, unitMode: "per_frame", }, ]); expect(toEffortRuleEngineInputs([ { scopeType: "shot", discipline: "Compositing", chapter: null, unitMode: "per_frame", hoursPerUnit: 0.08, sortOrder: 0, }, ])).toEqual([ { scopeType: "shot", discipline: "Compositing", chapter: null, unitMode: "per_frame", hoursPerUnit: 0.08, sortOrder: 0, }, ]); }); it("builds estimate demand line rows with the current optional chapter semantics", () => { expect(buildEstimateDemandLineRows({ estimateVersionId: "ver_1", currency: "EUR", ruleSet: { id: "ers_1", name: "VFX Standard" }, lines: [ { scopeItemName: "Shot_001", scopeType: "shot", discipline: "Compositing", chapter: "", hours: 16, unitMode: "per_frame", unitCount: 200, hoursPerUnit: 0.08, }, ], })).toEqual([ { estimateVersionId: "ver_1", lineType: "LABOR", name: "Compositing — Shot_001", hours: 16, costRateCents: 0, billRateCents: 0, currency: "EUR", costTotalCents: 0, priceTotalCents: 0, monthlySpread: {}, staffingAttributes: {}, metadata: { effortRule: { ruleSetId: "ers_1", ruleSetName: "VFX Standard", discipline: "Compositing", unitMode: "per_frame", unitCount: 200, hoursPerUnit: 0.08, }, }, }, ]); }); });