import { describe, expect, it, vi } from "vitest"; import { model3dModule } from "../modules/built-ins/model-3d/model-3d-module"; import { TOOLCRAFT_ADVERTISED_MODEL_FORMATS } from "../model-import/formats/production-model-format-registry"; import { defineToolcraft } from "./define-toolcraft"; import type { ToolcraftControlSchema, ToolcraftModelFormat, ToolcraftModelImportLimits, ToolcraftModelTopologyProfile, } from "./types"; import { createFileDropSchema, getSourceControl, } from "./define-toolcraft.control-sections.test-support"; describe("defineToolcraft fileDrop normalization", () => { it("defaults model topology to the realtime mesh profile", () => { const app = defineToolcraft( createFileDropSchema({ assetKind: "model" }, [model3dModule()]), ); expect(getSourceControl(app)?.topologyProfile).toBe("realtime-mesh"); }); it("exposes required resolved model fields after assetKind narrowing", () => { const app = defineToolcraft( createFileDropSchema({ assetKind: "model" }, [model3dModule()]), ); const control = getSourceControl(app); if (control?.assetKind !== "model") { throw new Error("Expected a resolved model fileDrop control."); } const requiredFields: readonly [ readonly ToolcraftModelFormat[], ToolcraftModelImportLimits, false, ToolcraftModelTopologyProfile, ] = [ control.modelFormats, control.modelLimits, control.multiple, control.topologyProfile, ]; expect(requiredFields[0]).toEqual(TOOLCRAFT_ADVERTISED_MODEL_FORMATS); expect(requiredFields[1].maxNodes).toBeGreaterThan(0); expect(requiredFields[2]).toBe(false); expect(requiredFields[3]).toBe("realtime-mesh"); }); it("defaults and narrows model formats against the advertised adapter registry", () => { const defaultApp = defineToolcraft( createFileDropSchema({ assetKind: "model" }, [model3dModule()]), ); const narrowedApp = defineToolcraft( createFileDropSchema( { assetKind: "model", modelFormats: ["glb", "gltf"], }, [model3dModule()], ), ); expect(getSourceControl(defaultApp)?.modelFormats).toEqual( TOOLCRAFT_ADVERTISED_MODEL_FORMATS, ); expect(getSourceControl(narrowedApp)?.modelFormats).toEqual([ "glb", "gltf", ]); expect( getSourceControl( defineToolcraft( createFileDropSchema( { assetKind: "model", modelFormats: ["obj"], }, [model3dModule()], ), ), )?.modelFormats, ).toEqual(["obj"]); expect(() => defineToolcraft( createFileDropSchema( { assetKind: "model", modelFormats: ["dae"], }, [model3dModule()], ), ), ).toThrow(/model fileDrop.*unsupported model format.*dae/u); }); it.each([ [ "an undefined entry", ["glb", undefined], /serializable plain data.*undefined values/u, ], [ "a sparse entry", (() => { const formats: unknown[] = ["glb"]; formats.length = 2; return formats; })(), /serializable plain data.*sparse array indices/u, ], ])("rejects modelFormats containing %s", (_label, modelFormats, error) => { expect(() => defineToolcraft( createFileDropSchema( { assetKind: "model", modelFormats, }, [model3dModule()], ), ), ).toThrow(error); }); it("rejects non-canonical modelFormats without invoking the input iterator", () => { const modelFormats = ["glb", "gltf"]; const adversarialIterator = vi.fn(function* () { yield "dae"; }); Object.defineProperty(modelFormats, Symbol.iterator, { configurable: true, value: adversarialIterator, }); expect(() => defineToolcraft( createFileDropSchema({ assetKind: "model", modelFormats }, [ model3dModule(), ]), ), ).toThrow(/arrays may own indices and length only/u); expect(adversarialIterator).not.toHaveBeenCalled(); }); it("snapshots stateful control branch descriptors before validation", () => { let assetKindReads = 0; let ownKeysCalls = 0; let typeReads = 0; const statefulControl = new Proxy( { applicability: { mode: "always" as const }, assetKind: "model", target: "source.model", type: "fileDrop", }, { get(target, property, receiver) { if (property === "assetKind") { assetKindReads += 1; return assetKindReads === 1 ? "model" : "image"; } if (property === "type") { typeReads += 1; return typeReads === 1 ? "fileDrop" : "panelActions"; } return Reflect.get(target, property, receiver); }, ownKeys(target) { ownKeysCalls += 1; return Reflect.ownKeys(target); }, }, ) as unknown as ToolcraftControlSchema; const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, panels: { controls: { sections: [ { id: "test-section-1", controls: { source: statefulControl }, title: "Source", }, ], title: "Controls", }, }, }, modules: [model3dModule()], }); const normalizedControl = getSourceControl(app); expect(normalizedControl?.assetKind).toBe("model"); expect(normalizedControl?.type).toBe("fileDrop"); expect(ownKeysCalls).toBe(1); expect(assetKindReads).toBe(0); expect(typeReads).toBe(0); }); });