import { describe, expect, it } from "vitest"; import { imageExportModule } from "./built-ins/image-export/image-export-module"; import { svgExportModule } from "./built-ins/svg-export/svg-export-module"; import { timelineModule } from "./built-ins/timeline/timeline-module"; import { videoExportModule } from "./built-ins/video-export/video-export-module"; import { preflightToolcraftModuleContributions } from "./testing/module-resolution-test-support"; import { resolveToolcraftModuleContributions } from "./testing/module-resolution-test-support"; import { getSettingsContribution, getTestModuleContributions, permutations, } from "./testing/module-contribution-test-fixtures"; import { unsafeCastTestOnlyToolcraftProductModuleContribution } from "./testing/create-test-module-definition"; describe("resolveToolcraftModuleContributions ownership preflight", () => { it("rejects duplicate contribution ids globally with every owner", () => { expect(() => resolveToolcraftModuleContributions([ ...imageExportModule().contributions, ...imageExportModule().contributions, ]), ).toThrowError( 'Toolcraft module contribution id "image-export.action" has multiple owners: image-export/image-export.action, image-export/image-export.action.', ); }); it("rejects duplicate runtime section resources before materialization", () => { const imageSettings = getSettingsContribution( imageExportModule(), "image-export.settings", ); const videoSettings = getSettingsContribution( videoExportModule(), "video-export.settings", ); const contributions = [ { ...videoSettings, runtimeSectionId: "runtime.shared", }, { ...imageSettings, runtimeSectionId: "runtime.shared", }, ] as const; for (const declarationOrder of permutations(contributions)) { expect(() => resolveToolcraftModuleContributions(declarationOrder), ).toThrowError( 'Toolcraft module ownership key "control-section:runtime.shared" has multiple owners: image-export/image-export.settings, video-export/video-export.settings.', ); } }); it("rejects duplicate control target resources before materialization", () => { const imageSettings = getSettingsContribution( imageExportModule(), "image-export.settings", ); const videoSettings = getSettingsContribution( videoExportModule(), "video-export.settings", ); const videoFormat = videoSettings.section.controls.videoFormat!; const videoResolution = videoSettings.section.controls.videoResolution!; const contributions = [ { ...videoSettings, section: { id: "test-section-1", controls: { videoFormat: { ...videoFormat, target: "export.image.format", }, videoResolution, }, layoutGroups: [], title: videoSettings.section.title, }, }, imageSettings, ] as const; for (const declarationOrder of permutations(contributions)) { expect(() => resolveToolcraftModuleContributions(declarationOrder), ).toThrowError( 'Toolcraft module ownership key "control-target:export.image.format" has multiple owners: image-export/image-export.settings, video-export/video-export.settings.', ); } }); it.each([ { id: "svg-export.action", kind: "panel-action", moduleId: "svg-export", role: "export-image", }, { id: "video-export.action", kind: "panel-action", moduleId: "video-export", role: "export-image", }, ] as const)( "rejects the invalid $id action correlation before ownership classification", (invalidContribution) => { expect(() => preflightToolcraftModuleContributions([ unsafeCastTestOnlyToolcraftProductModuleContribution( invalidContribution, ), ]), ).toThrowError( `Invalid Toolcraft module contribution "${invalidContribution.id}": payload does not match its closed contract.`, ); }, ); it("assigns aggregate action resources to the first canonical action role", () => { const cases = [ { definitions: [videoExportModule()], representativeId: "video-export.action", }, { definitions: [videoExportModule(), svgExportModule()], representativeId: "svg-export.action", }, { definitions: [ videoExportModule(), svgExportModule(), imageExportModule(), ], representativeId: "image-export.action", }, ] as const; for (const { definitions, representativeId } of cases) { for (const declarationOrder of permutations(definitions)) { const ownership = resolveToolcraftModuleContributions( getTestModuleContributions(declarationOrder), ).ownership; const aggregateOwners = ownership.filter(({ ownershipKeys }) => ownershipKeys.includes("control-section:runtime.export"), ); expect(aggregateOwners).toHaveLength(1); expect(aggregateOwners[0]).toMatchObject({ contributionId: representativeId, ownershipKeys: expect.arrayContaining([ "control-section:runtime.export", "control-target:actions.output", ]), }); } } }); it("returns frozen input-order-independent ownership", () => { const definitions = [ videoExportModule(), timelineModule({ mode: "playback" }), svgExportModule(), imageExportModule(), ]; const expected = resolveToolcraftModuleContributions( getTestModuleContributions(definitions), ); const expectedJson = JSON.stringify(expected); for (const declarationOrder of permutations(definitions)) { expect( JSON.stringify( resolveToolcraftModuleContributions( getTestModuleContributions(declarationOrder), ), ), ).toBe(expectedJson); } expect(expected.ownership).toEqual([ { contributionId: "image-export.action", kind: "panel-action", moduleId: "image-export", ownershipKeys: [ "control-section:runtime.export", "control-target:actions.output", "panel-action:export-image", ], }, { contributionId: "image-export.settings", kind: "control-section", moduleId: "image-export", ownershipKeys: [ "control-section:runtime.image-export", "control-target:export.image.format", "control-target:export.image.resolution", ], }, { contributionId: "svg-export.action", kind: "panel-action", moduleId: "svg-export", ownershipKeys: ["panel-action:export-svg"], }, { contributionId: "timeline.persistence", kind: "persistence-requirement", moduleId: "timeline", ownershipKeys: ["persistence-slice:timeline"], }, { contributionId: "timeline.surface", kind: "panel-surface", moduleId: "timeline", ownershipKeys: ["panel-surface:timeline"], }, { contributionId: "video-export.action", kind: "panel-action", moduleId: "video-export", ownershipKeys: ["panel-action:export-video"], }, { contributionId: "video-export.settings", kind: "control-section", moduleId: "video-export", ownershipKeys: [ "control-section:runtime.video-export", "control-target:export.video.format", "control-target:export.video.resolution", ], }, ]); expect(Object.isFrozen(expected.ownership)).toBe(true); expect(expected.ownership.every(Object.isFrozen)).toBe(true); expect( expected.ownership.every(({ ownershipKeys }) => Object.isFrozen(ownershipKeys), ), ).toBe(true); }); });