import { describe, expect, it } from "vitest"; import { defineToolcraft } from "./define-toolcraft"; import type { ToolcraftControlSchema } from "./types"; import { createFileDropSchema, getSourceControl, MODEL_LIMIT_KEYS, } from "./define-toolcraft.control-sections.test-support"; describe("defineToolcraft fileDrop normalization", () => { it("keeps model limits as positive safe integers at or below protected ceilings", () => { const defaultApp = defineToolcraft( createFileDropSchema({ assetKind: "model" }), ); const defaultLimits = getSourceControl(defaultApp)?.modelLimits; expect(defaultLimits).toBeDefined(); expect(Object.keys(defaultLimits ?? {}).sort()).toEqual( [...MODEL_LIMIT_KEYS].sort(), ); for (const limitName of MODEL_LIMIT_KEYS) { const ceiling = defaultLimits?.[limitName]; expect(ceiling).toSatisfy( (value: unknown) => Number.isSafeInteger(value) && (value as number) > 0, ); if (ceiling === undefined) { throw new Error(`Missing protected model import limit ${limitName}.`); } const narrowedLimit = Math.max(1, Math.floor(ceiling / 2)); const narrowedApp = defineToolcraft( createFileDropSchema({ assetKind: "model", modelLimits: { [limitName]: narrowedLimit }, }), ); expect(getSourceControl(narrowedApp)?.modelLimits?.[limitName]).toBe( narrowedLimit, ); expect(() => defineToolcraft( createFileDropSchema({ assetKind: "model", modelLimits: { [limitName]: ceiling + 1 }, }), ), ).toThrow( new RegExp( `model fileDrop.*${limitName}.*protected ceiling`, "u", ), ); } }); it("rejects fractional model import limits", () => { expect(() => defineToolcraft( createFileDropSchema({ assetKind: "model", modelLimits: { maxNodes: 1.5 }, }), ), ).toThrow(/model fileDrop.*maxNodes.*safe integer/u); }); it.each(["unsupportedLimit", ""])( "rejects unknown modelLimits own key %j", (unknownLimitName) => { expect(() => defineToolcraft( createFileDropSchema({ assetKind: "model", modelLimits: { [unknownLimitName]: 1 }, }), ), ).toThrow(/model fileDrop.*unsupported model limit/u); }, ); it("snapshots modelLimits own keys and reads accepted values once", () => { let maxNodesReads = 0; let ownKeysCalls = 0; const modelLimits = new Proxy( { maxNodes: 500 }, { get(target, property, receiver) { if (property === "maxNodes") { maxNodesReads += 1; } return Reflect.get(target, property, receiver); }, ownKeys(target) { ownKeysCalls += 1; return ownKeysCalls === 1 ? Reflect.ownKeys(target) : []; }, }, ); const app = defineToolcraft( createFileDropSchema({ assetKind: "model", modelLimits }), ); expect(getSourceControl(app)?.modelLimits?.maxNodes).toBe(500); expect(ownKeysCalls).toBe(1); expect(maxNodesReads).toBe(1); }); it("keeps model fileDrops structural while custom control types stay open", () => { const validModelControl: ToolcraftControlSchema = { assetKind: "model", modelFormats: ["glb"], modelLimits: { maxNodes: 500 }, multiple: false, target: "source.model", topologyProfile: "solid-mesh", type: "fileDrop", }; const validCustomControl: ToolcraftControlSchema = { assetKind: "image", multiple: true, target: "product.mask", type: "productMaskEditor", }; const acceptControl = (_control: ToolcraftControlSchema): void => {}; // @ts-expect-error Model fileDrops require multiple to be false when provided. acceptControl({ assetKind: "model", multiple: true, target: "source.model", type: "fileDrop", }); // @ts-expect-error Model asset kinds are reserved for fileDrop controls. acceptControl({ assetKind: "model", target: "product.model", type: "productModelEditor", }); // @ts-expect-error Non-model controls cannot declare modelFormats. acceptControl({ assetKind: "image", modelFormats: ["glb"] as const, target: "source.image", type: "fileDrop", }); // @ts-expect-error Non-model controls cannot declare modelLimits. acceptControl({ modelLimits: { maxNodes: 500 }, target: "product.mask", type: "productMaskEditor", }); // @ts-expect-error Non-model controls cannot declare topologyProfile. acceptControl({ target: "product.mask", topologyProfile: "realtime-mesh", type: "productMaskEditor", }); expect([validModelControl.type, validCustomControl.type]).toEqual([ "fileDrop", "productMaskEditor", ]); });});