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 { model3dModule } from "./built-ins/model-3d/model-3d-module"; import { spatialViewModule } from "./built-ins/spatial-view/spatial-view-module"; import { timelineModule } from "./built-ins/timeline/timeline-module"; import { videoExportModule } from "./built-ins/video-export/video-export-module"; import type { ToolcraftProductModuleDefinition } from "./contract/module-definition"; import { resolveToolcraftModuleDependencies } from "./testing/module-resolution-test-support"; import { createTestToolcraftProductModuleDefinition } from "./testing/create-test-module-definition"; import { permutations } from "./testing/module-contribution-test-fixtures"; const emptyTestModuleFields = { contributions: [], defaultProviders: [], portRequirements: [], } as const; function expectRecursivelyFrozen(value: unknown): void { if (value === null || typeof value !== "object") { return; } expect(Object.isFrozen(value)).toBe(true); for (const nestedValue of Object.values(value)) { expectRecursivelyFrozen(nestedValue); } } type DependencyResolver = ( definitions: ToolcraftProductModuleDefinition[], ) => ReturnType; function expectCanonicalResolutionAfterFailure(): void { const resolution = resolveToolcraftModuleDependencies([ model3dModule(), imageExportModule(), ]); const expectedResolution = { capabilities: [ { capabilityId: "artifact.image-export", moduleId: "image-export", }, { capabilityId: "media.source", moduleId: "media-source" }, { capabilityId: "model.3d", moduleId: "model-3d" }, ], definitions: [imageExportModule(), mediaSourceModule(), model3dModule()], modules: [ { id: "image-export", origin: "explicit", provides: ["artifact.image-export"], requestedBy: [], requires: [], }, { id: "media-source", origin: "default-provider", provides: ["media.source"], requestedBy: ["model-3d"], requires: [], }, { id: "model-3d", origin: "explicit", provides: ["model.3d"], requestedBy: [], requires: ["media.source"], }, ], }; expect(JSON.stringify(resolution)).toBe(JSON.stringify(expectedResolution)); for (const [index, definition] of resolution.definitions.entries()) { expect(definition).toBe(expectedResolution.definitions[index]); } expectRecursivelyFrozen(resolution); } function expectIsolatedFailure( definitions: ToolcraftProductModuleDefinition[], expectedMessage: string, resolver: DependencyResolver = resolveToolcraftModuleDependencies, ): void { const inputIdentitySnapshot = [...definitions]; const inputSerializationSnapshot = definitions.map((definition) => JSON.stringify(definition), ); const inputFrozenSnapshot = definitions.map((definition) => Object.isFrozen(definition), ); expect(inputFrozenSnapshot.every(Boolean)).toBe(true); expect(() => resolver(definitions)).toThrowError(expectedMessage); expectCanonicalResolutionAfterFailure(); if ( definitions.length !== inputIdentitySnapshot.length || definitions.some( (definition, index) => definition !== inputIdentitySnapshot[index], ) ) { throw new Error( "Failure isolation helper detected caller input order or identity mutation.", ); } expect(definitions.map((definition) => JSON.stringify(definition))).toEqual( inputSerializationSnapshot, ); expect(definitions.map((definition) => Object.isFrozen(definition))).toEqual( inputFrozenSnapshot, ); } describe("Project B module dependency resolution", () => { it("detects a mutating-then-throwing resolver in the failure helper", () => { const definitions = [model3dModule(), mediaSourceModule()]; expect(() => expectIsolatedFailure(definitions, "stand-in failure", (callerInput) => { callerInput.reverse(); throw new Error("stand-in failure"); }), ).toThrowError( "Failure isolation helper detected caller input order or identity mutation.", ); }); it("injects exactly one canonical Media Source for Model 3D", () => { const resolution = resolveToolcraftModuleDependencies([model3dModule()]); expect(resolution.definitions).toEqual([ mediaSourceModule(), model3dModule(), ]); expect(resolution.definitions[0]).toBe(mediaSourceModule()); expect(resolution.modules).toEqual([ { id: "media-source", origin: "default-provider", provides: ["media.source"], requestedBy: ["model-3d"], requires: [], }, { id: "model-3d", origin: "explicit", provides: ["model.3d"], requestedBy: [], requires: ["media.source"], }, ]); expect(resolution.capabilities).toEqual([ { capabilityId: "media.source", moduleId: "media-source" }, { capabilityId: "model.3d", moduleId: "model-3d" }, ]); expectRecursivelyFrozen(resolution); }); it("uses explicit Media Source without injecting or recording a default requester", () => { const resolution = resolveToolcraftModuleDependencies([ model3dModule(), mediaSourceModule(), ]); expect(resolution.definitions).toEqual([ mediaSourceModule(), model3dModule(), ]); expect(resolution.definitions[0]).toBe(mediaSourceModule()); expect(resolution.definitions[1]).toBe(model3dModule()); expect(resolution.modules).toEqual([ { id: "media-source", origin: "explicit", provides: ["media.source"], requestedBy: [], requires: [], }, { id: "model-3d", origin: "explicit", provides: ["model.3d"], requestedBy: [], requires: ["media.source"], }, ]); }); it("returns byte-equal complete frozen resolution for every Project B declaration order", () => { const definitions = [ spatialViewModule(), model3dModule({ presentation: "custom" }), mediaSourceModule(), layersModule(), canvasEditingModule(), ]; const expectedDefinitions = [ canvasEditingModule(), layersModule(), mediaSourceModule(), model3dModule({ presentation: "custom" }), spatialViewModule(), ]; const expectedResolution = resolveToolcraftModuleDependencies(definitions); const expectedJson = JSON.stringify(expectedResolution); const publicDefinitionKeys = [ "contractVersion", "contributions", "defaultProviders", "id", "portRequirements", "provides", "requires", ]; expect(Object.keys(expectedResolution).sort()).toEqual([ "capabilities", "definitions", "modules", ]); expect(expectedJson).toContain('"definitions":'); expect(expectedResolution.definitions).toEqual(expectedDefinitions); for (const [ index, definition, ] of expectedResolution.definitions.entries()) { expect(definition).toBe(expectedDefinitions[index]); expect(Object.keys(definition).sort()).toEqual(publicDefinitionKeys); const serializedDefinition = JSON.stringify(definition); for (const key of publicDefinitionKeys) { expect(serializedDefinition).toContain(`"${key}":`); } } for (const declarationOrder of permutations(definitions)) { const resolution = resolveToolcraftModuleDependencies(declarationOrder); expect(JSON.stringify(resolution)).toBe(expectedJson); expect(resolution.definitions.map(({ id }) => id)).toEqual([ "canvas-editing", "layers", "media-source", "model-3d", "spatial-view", ]); for (const [index, definition] of resolution.definitions.entries()) { expect(definition).toBe(expectedDefinitions[index]); } expectRecursivelyFrozen(resolution); } }); it("rejects runtime and custom Model 3D variants as duplicate ids first", () => { expectIsolatedFailure( [model3dModule(), model3dModule({ presentation: "custom" })], 'Toolcraft product module id "model-3d" is declared more than once.', ); }); it("rejects an occupied media-source id without mutating inputs or later resolutions", () => { const occupiedMediaSource = createTestToolcraftProductModuleDefinition({ ...emptyTestModuleFields, id: "media-source", provides: [], requires: [], }); const definitions = [model3dModule(), occupiedMediaSource]; const expectedMessage = 'Toolcraft default provider "media.source-default" cannot insert module "media-source" because that id is already occupied by an explicit module along requirement path model-3d -> media.source.'; for (const declarationOrder of permutations(definitions)) { expectIsolatedFailure([...declarationOrder], expectedMessage); } }); it("composes Media Source and playback Timeline defaults in one order-independent closure", () => { const definitions = [videoExportModule(), model3dModule()]; const expectedModules = [ { id: "media-source", origin: "default-provider", provides: ["media.source"], requestedBy: ["model-3d"], requires: [], }, { id: "model-3d", origin: "explicit", provides: ["model.3d"], requestedBy: [], requires: ["media.source"], }, { id: "timeline", origin: "default-provider", provides: ["timeline.playback"], requestedBy: ["video-export"], requires: [], }, { id: "video-export", origin: "explicit", provides: ["artifact.video-export"], requestedBy: [], requires: ["timeline.playback"], }, ]; for (const declarationOrder of permutations(definitions)) { const resolution = resolveToolcraftModuleDependencies(declarationOrder); expect(resolution.modules).toEqual(expectedModules); expect(resolution.definitions).toEqual([ mediaSourceModule(), model3dModule(), timelineModule({ mode: "playback" }), videoExportModule(), ]); expect(resolution.definitions[0]).toBe(mediaSourceModule()); expect(resolution.definitions[2]).toBe( timelineModule({ mode: "playback" }), ); } }); it.each(["keyframes", "playback"] as const)( "keeps an explicit %s Timeline while independently injecting Media Source", (mode) => { const resolution = resolveToolcraftModuleDependencies([ videoExportModule(), model3dModule(), timelineModule({ mode }), ]); expect(resolution.definitions).toEqual([ mediaSourceModule(), model3dModule(), timelineModule({ mode }), videoExportModule(), ]); expect(resolution.modules.find(({ id }) => id === "timeline")).toEqual({ id: "timeline", origin: "explicit", provides: mode === "keyframes" ? ["timeline.keyframes", "timeline.playback"] : ["timeline.playback"], requestedBy: [], requires: [], }); expect( resolution.modules.find(({ id }) => id === "media-source"), ).toEqual({ id: "media-source", origin: "default-provider", provides: ["media.source"], requestedBy: ["model-3d"], requires: [], }); }, ); it("keeps missing, provider-conflict, and cycle failures deterministic and isolated", () => { const cases = [ { definitions: [ createTestToolcraftProductModuleDefinition({ ...emptyTestModuleFields, id: "model-3d", provides: ["model.3d"], requires: ["media.source"], }), ], message: 'Toolcraft capability "media.source" is missing along requirement path model-3d -> media.source; module "model-3d" declares no default provider for it.', }, { definitions: [ model3dModule(), mediaSourceModule(), createTestToolcraftProductModuleDefinition({ ...emptyTestModuleFields, id: "layers", provides: ["layers.management", "media.source"], requires: [], }), ], message: 'Toolcraft capability "media.source" has multiple providers: layers, media-source.', }, { definitions: [ createTestToolcraftProductModuleDefinition({ ...emptyTestModuleFields, id: "model-3d", provides: ["model.3d"], requires: ["media.source"], }), createTestToolcraftProductModuleDefinition({ ...emptyTestModuleFields, id: "media-source", provides: ["media.source"], requires: ["model.3d"], }), ], message: "Toolcraft module dependency cycle: media-source -> model-3d -> media-source.", }, ] as const; for (const { definitions, message } of cases) { for (const declarationOrder of permutations(definitions)) { expectIsolatedFailure([...declarationOrder], message); } } }); });