import { describe, expect, it } from "vitest"; import { createToolcraftArtifactExportActions } from "./artifact-export-actions"; import { canvasEditingModule } from "../modules/built-ins/canvas-editing/canvas-editing-module"; import { imageExportModule } from "../modules/built-ins/image-export/image-export-module"; import { layersModule } from "../modules/built-ins/layers/layers-module"; import { mediaSourceModule } from "../modules/built-ins/media-source/media-source-module"; import { model3dModule } from "../modules/built-ins/model-3d/model-3d-module"; import { spatialViewModule } from "../modules/built-ins/spatial-view/spatial-view-module"; import { timelineModule } from "../modules/built-ins/timeline/timeline-module"; import { videoExportModule } from "../modules/built-ins/video-export/video-export-module"; import { TOOLCRAFT_ARTIFACT_SETTINGS_SECTION_IDS } from "../modules/contract/contribution"; import { permutations } from "../modules/testing/module-contribution-test-fixtures"; import type { ToolcraftProductBase } from "./product-base"; import { resolveToolcraftProductDefinition } from "./resolve-toolcraft-product-definition"; import { artifactModuleFixtures as artifactModules, cloneUnsafeProductBaseFixture as cloneUnsafeBase, createProductBaseFixture as createBase, expectProductFixtureRecursivelyFrozen as expectRecursivelyFrozen, } from "./resolve-toolcraft-product-definition.test-support"; describe("resolveToolcraftProductDefinition", () => { it("keeps artifact section identity in one frozen canonical contract", () => { expect(Object.isFrozen(TOOLCRAFT_ARTIFACT_SETTINGS_SECTION_IDS)).toBe(true); expect(imageExportModule().contributions[0]).toMatchObject({ runtimeSectionId: TOOLCRAFT_ARTIFACT_SETTINGS_SECTION_IDS.image, }); expect(videoExportModule().contributions[0]).toMatchObject({ runtimeSectionId: TOOLCRAFT_ARTIFACT_SETTINGS_SECTION_IDS.video, }); }); it("composes one deterministic frozen schema and compact module plan", () => { const base = createBase(); const before = JSON.stringify(base); const result = resolveToolcraftProductDefinition({ base, modules: artifactModules, }); expect(result.identity).toEqual({ id: "module-composition", title: "Module Composition", }); expect(result.panels.controls?.sections.map(({ title }) => title)).toEqual([ "Defaults", "Settings", "Product", "Image Export", "Video Export", "Export", ]); expect(result.panels.timeline).toMatchObject({ enabled: true, mode: "playback", }); expect(result.persistence).toEqual({ additionalValueTargets: ["product.seed"], include: ["canvas", "panels", "timeline", "values"], key: "toolcraft:module-composition:state:v2", storage: "localStorage", version: 2, }); expect(result.modulePlan.modules).toContainEqual({ id: "timeline", origin: "default-provider", provides: ["timeline.playback"], requestedBy: ["video-export"], requires: [], }); expect(Object.keys(result)).toContain("modulePlan"); expect(JSON.stringify(result)).not.toContain('"contributions"'); expect(JSON.stringify(result)).not.toContain('"definitions"'); expect(JSON.stringify(base)).toBe(before); expectRecursivelyFrozen(result); expect(Object.isFrozen(artifactModules)).toBe(true); }); it("is byte-identical for every declaration order", () => { const expected = JSON.stringify( resolveToolcraftProductDefinition({ base: createBase(), modules: artifactModules, }), ); for (const modules of permutations(artifactModules)) { expect( JSON.stringify( resolveToolcraftProductDefinition({ base: createBase(), modules }), ), ).toBe(expected); } }); it("snapshots product aliases so later caller mutation cannot change output", () => { const base = createBase(); const declarations = [imageExportModule()]; const result = resolveToolcraftProductDefinition({ base, modules: declarations, }); const before = JSON.stringify(result); base.canvas.enabled = false; base.panels.controls!.sections[0]!.controls.intensity!.target = "product.changed"; declarations.splice(0, 1, videoExportModule()); expect(JSON.stringify(result)).toBe(before); expect(result.modulePlan.modules.map(({ id }) => id)).toEqual([ "image-export", ]); expect(result.canvas.enabled).toBe(true); expect( result.panels.controls?.sections.some(({ controls }) => Object.values(controls).some( ({ target }) => target === "product.changed", ), ), ).toBe(false); }); it("derives every normalized identity consumer only from required base.identity", () => { const original = createBase(); const base = { ...original, identity: { id: " Canonical Product ", title: " Canonical Title " }, panels: { controls: { ...original.panels.controls!, title: "Unrelated Controls Title", }, }, settingsTransfer: {}, } satisfies ToolcraftProductBase; const schema = resolveToolcraftProductDefinition({ base, modules: [], }); expect(schema.identity).toEqual({ id: "canonical-product", title: "Canonical Title", }); expect(schema.persistence).toMatchObject({ key: "toolcraft:canonical-product:state:v2", }); expect(schema.settingsTransfer).toMatchObject({ appId: "canonical-product", fileName: "canonical-product-settings.json", }); }); it("rejects a required identity id whose canonical slug is empty", () => { const base = { ...createBase(), identity: { id: "🔥", title: "Fire" }, } satisfies ToolcraftProductBase; expect(() => resolveToolcraftProductDefinition({ base, modules: [] }), ).toThrow(/does not contain a canonical ASCII id/); }); it.each([ [ "missing section id", (base: Record) => { delete base.panels.controls.sections[0].id; }, /product-authored control section at index 0 requires an explicit id/, ], [ "missing control applicability", (base: Record) => { delete base.panels.controls.sections[0].controls.intensity .applicability; }, /product-authored control "intensity" requires explicit applicability/, ], [ "authored applicability origin", (base: Record) => { base.panels.controls.sections[0].controls.intensity.applicability.origin = "explicit"; }, /product-authored control "intensity" cannot author applicability\.origin/, ], [ "control-level visibleWhen", (base: Record) => { base.panels.controls.sections[0].controls.intensity.visibleWhen = { equals: true, target: "product.enabled", }; }, /product-authored control "intensity" cannot declare visibleWhen/, ], ])( "rejects forged JavaScript with %s before generic normalization", (_label, mutate, expected) => { const base = cloneUnsafeBase(createBase()); mutate(base); expect(() => resolveToolcraftProductDefinition({ base: base as unknown as ToolcraftProductBase, modules: [], }), ).toThrow(expected); }, ); it.each([ ["Date", new Date("2026-08-19T00:00:00.000Z")], ["Map", new Map([["key", "value"]])], ["typed array", new Uint8Array([1, 2, 3])], ])( "rejects non-plain %s aliases before resolving a product snapshot", (_label, value) => { const base = createBase(); base.panels.controls!.sections[0]!.controls.intensity!.defaultValue = value; expect(() => resolveToolcraftProductDefinition({ base, modules: [] }), ).toThrow(/serializable plain data/); expect( base.panels.controls!.sections[0]!.controls.intensity!.defaultValue, ).toBe(value); }, ); it("materializes Layers, Timeline, Media persistence, and behavior facts once", () => { const controls = createBase().panels.controls!.sections[0]!.controls; const base = createBase({ ...controls, model: { applicability: { mode: "always" as const }, assetKind: "model", label: "Model", target: "source.model", type: "fileDrop", }, orientation: { applicability: { mode: "always" as const }, defaultValue: { position: [3, 3, 3], up: [0, 1, 0] }, keyframeable: false, label: false, target: "view.orbit", type: "orientationGizmo", }, }); const result = resolveToolcraftProductDefinition({ base, modules: [ layersModule(), model3dModule(), canvasEditingModule(), spatialViewModule(), timelineModule({ mode: "keyframes" }), ], }); expect(result.panels.layers).toBe(true); expect(result.panels.timeline).toMatchObject({ mode: "keyframes" }); expect(result.persistence.storage).toBe("localStorage"); if (result.persistence.storage === "localStorage") { expect(result.persistence.include).toEqual([ "canvas", "layers", "media", "panels", "timeline", "values", ]); for (const slice of ["layers", "media", "timeline"] as const) { expect( result.persistence.include.filter((value) => value === slice), ).toHaveLength(1); } } }); it.each([ ["section", "runtime.image-export"], ["target", "export.image.format"], ["role", "export-svg"], ] as const)( "rejects a reserved artifact %s even without its module", (kind, value) => { const base = createBase(); const unsafe = cloneUnsafeBase(base); const panels = unsafe.panels as ToolcraftProductBase["panels"]; const section = panels.controls!.sections[0]!; if (kind === "section") { section.id = value; } else if (kind === "target") { section.controls.intensity!.target = value; } else { section.controls.actions = { applicability: { mode: "always" as const }, actions: createToolcraftArtifactExportActions([value]), target: "product.actions", type: "panelActions", }; } expect(() => resolveToolcraftProductDefinition({ base: unsafe as unknown as ToolcraftProductBase, modules: [], }), ).toThrow(/reserved standard artifact/); }, ); it("rejects module-owned surfaces and resolved ownership collisions", () => { const unsafe = cloneUnsafeBase(createBase()); (unsafe.panels as Record).layers = true; expect(() => resolveToolcraftProductDefinition({ base: unsafe as unknown as ToolcraftProductBase, modules: [layersModule()], }), ).toThrow(/panels\.layers/); const targetCollision = cloneUnsafeBase(createBase()); const controls = (targetCollision.panels as ToolcraftProductBase["panels"]) .controls!.sections[0]!.controls; controls.intensity!.target = "export.video.resolution"; expect(() => resolveToolcraftProductDefinition({ base: targetCollision as unknown as ToolcraftProductBase, modules: [videoExportModule()], }), ).toThrow(/export\.video\.resolution/); }); it("rejects module control contributions without a Controls panel", () => { expect(() => resolveToolcraftProductDefinition({ base: { ...createBase(), panels: {} }, modules: [imageExportModule()], }), ).toThrow(/without a base controls panel/); }); it("enforces media, model, canvas, and spatial capability correspondence both ways", () => { expect(() => resolveToolcraftProductDefinition({ base: { ...createBase(), canvas: { ...createBase().canvas, upload: true }, }, modules: [], }), ).toThrow(/media\.source/); const modelBase = createBase({ model: { applicability: { mode: "always" as const }, assetKind: "model", label: "Model", target: "source.model", type: "fileDrop", }, }); expect(() => resolveToolcraftProductDefinition({ base: modelBase, modules: [mediaSourceModule()], }), ).toThrow(/model\.3d/); expect(() => resolveToolcraftProductDefinition({ base: createBase(), modules: [mediaSourceModule()], }), ).toThrow( /media\.source.*requires a canvas upload, fileDrop source, or media\.defaultAssets/, ); const defaultModelBase = { ...createBase(), media: { defaultAssets: [ { assetKind: "model", fileName: "fixture.glb", sourceFiles: [ { dataUrl: "data:model/gltf-binary;base64,AA==", path: "fixture.glb", }, ], }, ], }, } satisfies ToolcraftProductBase; expect(() => resolveToolcraftProductDefinition({ base: defaultModelBase, modules: [mediaSourceModule()], }), ).toThrow(/model\.3d/); expect(() => resolveToolcraftProductDefinition({ base: createBase(), modules: [model3dModule()], }), ).toThrow(/canonical model source/); const orientationBase = createBase({ orientation: { applicability: { mode: "always" as const }, defaultValue: { position: [3, 3, 3], up: [0, 1, 0] }, keyframeable: false, label: false, target: "view.orbit", type: "orientationGizmo", }, }); expect(() => resolveToolcraftProductDefinition({ base: orientationBase, modules: [] }), ).toThrow(/spatial\.view/); expect(() => resolveToolcraftProductDefinition({ base: createBase(), modules: [spatialViewModule()], }), ).toThrow(/orientationGizmo/); expect(() => resolveToolcraftProductDefinition({ base: { ...createBase(), canvas: { enabled: false } }, modules: [canvasEditingModule()], }), ).toThrow(/canvas\.enabled true/); }); });