import { describe, expect, it } from "vitest"; import { canvasEditingModule } from "./built-ins/canvas-editing/canvas-editing-module"; import { imageExportModule } from "./built-ins/image-export/image-export-module"; import { layersModule } from "./built-ins/layers/layers-module"; import { mediaSourceModule } from "./built-ins/media-source/media-source-module"; import { spatialViewModule } from "./built-ins/spatial-view/spatial-view-module"; import { svgExportModule } from "./built-ins/svg-export/svg-export-module"; import { preflightToolcraftModuleContributions } from "./testing/module-resolution-test-support"; import { resolveToolcraftModuleContributions } from "./testing/module-resolution-test-support"; import { getTestModuleContributions, permutations, } from "./testing/module-contribution-test-fixtures"; import { unsafeCastTestOnlyToolcraftProductModuleContribution } from "./testing/create-test-module-definition"; const projectBDefinitions = [ layersModule(), mediaSourceModule(), canvasEditingModule(), spatialViewModule(), ] as const; const rawTimelineWithLayersSurface = { id: "timeline.surface", kind: "panel-surface", configuration: true, moduleId: "timeline", surface: "layers", } as const; const rawTimelineWithLayersPersistence = { id: "timeline.persistence", kind: "persistence-requirement", moduleId: "timeline", slice: "layers", } as const; const rawImageExportWithMediaPolicy = { id: "image-export.settings", kind: "media-policy", moduleId: "image-export", policy: "source-workflow", sourceKinds: ["file", "image", "model"], } as const; const rawTimelineWithMediaPersistence = { id: "timeline.persistence", kind: "persistence-requirement", moduleId: "timeline", slice: "media", } as const; const rawSpatialViewWithEditingBehavior = { behavior: "editing", id: "spatial-view.behavior", kind: "canvas-behavior", moduleId: "spatial-view", operations: ["drag", "handles", "resize", "selection"], } as const; const rawCanvasEditingWithSpatialBehavior = { behavior: "spatial-view", id: "canvas-editing.behavior", kind: "canvas-behavior", moduleId: "canvas-editing", operations: ["gizmo", "orbit", "paused-live-sync", "pose"], } as const; describe("Project B module contribution preflight", () => { it("classifies all six contributions into narrow frozen buckets", () => { const preflight = preflightToolcraftModuleContributions( projectBDefinitions.flatMap(({ contributions }) => contributions), ); expect(preflight.actionContributions).toEqual([]); expect(preflight.settingsContributions).toEqual([]); expect(preflight.surfaceContributions.map(({ id }) => id)).toEqual([ "layers.surface", ]); expect(preflight.persistenceContributions.map(({ id }) => id)).toEqual([ "layers.persistence", "media-source.persistence", ]); expect(preflight.mediaPolicyContributions.map(({ id }) => id)).toEqual([ "media-source.policy", ]); expect(preflight.canvasBehaviorContributions.map(({ id }) => id)).toEqual([ "canvas-editing.behavior", "spatial-view.behavior", ]); expect(Object.isFrozen(preflight)).toBe(true); expect( [ preflight.actionContributions, preflight.canvasBehaviorContributions, preflight.mediaPolicyContributions, preflight.persistenceContributions, preflight.settingsContributions, preflight.surfaceContributions, preflight.ownership, ].every(Object.isFrozen), ).toBe(true); expect(preflight.ownership.every(Object.isFrozen)).toBe(true); expect( preflight.ownership.every(({ ownershipKeys }) => Object.isFrozen(ownershipKeys), ), ).toBe(true); expect( [ ...preflight.canvasBehaviorContributions, ...preflight.mediaPolicyContributions, ...preflight.persistenceContributions, ...preflight.surfaceContributions, ].every(Object.isFrozen), ).toBe(true); expect( preflight.canvasBehaviorContributions.every(({ operations }) => Object.isFrozen(operations), ), ).toBe(true); expect( preflight.mediaPolicyContributions.every(({ sourceKinds }) => Object.isFrozen(sourceKinds), ), ).toBe(true); }); it.each([ [rawTimelineWithLayersSurface, "timeline.surface"], [rawTimelineWithLayersPersistence, "timeline.persistence"], [rawImageExportWithMediaPolicy, "image-export.settings"], [rawTimelineWithMediaPersistence, "timeline.persistence"], [rawSpatialViewWithEditingBehavior, "spatial-view.behavior"], [rawCanvasEditingWithSpatialBehavior, "canvas-editing.behavior"], ] as const)( "rejects the invalid %s correlation before ownership classification", (invalidContribution, contributionId) => { expect(() => preflightToolcraftModuleContributions([ unsafeCastTestOnlyToolcraftProductModuleContribution( invalidContribution, ), ]), ).toThrowError( `Invalid Toolcraft module contribution "${contributionId}": payload does not match its closed contract.`, ); }, ); it("rejects duplicate contribution ids before ownership materialization", () => { expect(() => preflightToolcraftModuleContributions([ ...canvasEditingModule().contributions, ...canvasEditingModule().contributions, ]), ).toThrowError( 'Toolcraft module contribution id "canvas-editing.behavior" has multiple owners: canvas-editing/canvas-editing.behavior, canvas-editing/canvas-editing.behavior.', ); }); it("preserves Project A aggregate action ownership byte-for-byte", () => { const definitions = [imageExportModule(), svgExportModule()]; const expected = JSON.stringify( resolveToolcraftModuleContributions( getTestModuleContributions(definitions), ), ); for (const declarationOrder of permutations(definitions)) { expect( JSON.stringify( resolveToolcraftModuleContributions( getTestModuleContributions(declarationOrder), ), ), ).toBe(expected); } }); });