import { describe, expect, it } from "vitest"; import type { ToolcraftModelFileDropSchema, ToolcraftModelImportLimits, } from "../schema/types"; import { TOOLCRAFT_ADVERTISED_MODEL_FORMATS, TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS, TOOLCRAFT_MODEL_IMPORT_LIMIT_CEILINGS, normalizeToolcraftModelImportLimits, normalizeToolcraftModelFileDrop, } from "./model-import-limits"; const limitNames = Object.keys( TOOLCRAFT_MODEL_IMPORT_LIMIT_CEILINGS, ) as (keyof ToolcraftModelImportLimits)[]; function modelControl( overrides: Partial = {}, ): ToolcraftModelFileDropSchema { return { assetKind: "model", target: "source.model", type: "fileDrop", ...overrides, }; } describe("model import limits", () => { it("defines the protected package and appearance ceilings", () => { expect(TOOLCRAFT_MODEL_IMPORT_LIMIT_CEILINGS).toMatchObject({ maxArchiveCompressionRatio: 100, maxArchiveEntries: 4_096, maxArchiveEntryBytes: 32 * 1_024 * 1_024, maxArchivePathLength: 1_024, maxArchiveUncompressedBytes: 64 * 1_024 * 1_024, maxTextureBytes: 32 * 1_024 * 1_024, maxTextureCount: 256, maxTextureDimension: 8_192, maxTexturePixels: 33_554_432, }); }); it("accepts a finite fractional archive compression ratio of at least one", () => { expect( normalizeToolcraftModelImportLimits({ maxArchiveCompressionRatio: 1.5, } as Partial), ).toMatchObject({ maxArchiveCompressionRatio: 1.5 }); for (const value of [0, 0.5, Number.NaN, Number.POSITIVE_INFINITY]) { expect(() => normalizeToolcraftModelImportLimits({ maxArchiveCompressionRatio: value, } as Partial), ).toThrow(/maxArchiveCompressionRatio.*finite.*at least 1/u); } }); it("exposes the same strict narrowing validator for direct runtime callers", () => { expect(normalizeToolcraftModelImportLimits({ maxNodes: 25 }).maxNodes).toBe(25); expect(() => normalizeToolcraftModelImportLimits({ unknown: 1, } as never)).toThrow(/unsupported model limit "unknown"/u); expect(() => normalizeToolcraftModelImportLimits({ maxNodes: TOOLCRAFT_MODEL_IMPORT_LIMIT_CEILINGS.maxNodes + 1, })).toThrow(/maxNodes.*protected ceiling/u); }); it("uses the advertised formats and protected ceilings by default", () => { const normalized = normalizeToolcraftModelFileDrop(modelControl()); expect(normalized.modelFormats).toEqual(TOOLCRAFT_ADVERTISED_MODEL_FORMATS); expect(normalized.modelLimits).toEqual( TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS, ); expect(normalized.multiple).toBe(false); expect(normalized.topologyProfile).toBe("realtime-mesh"); }); it.each(limitNames)("allows %s to narrow but not widen", (limitName) => { const ceiling = TOOLCRAFT_MODEL_IMPORT_LIMIT_CEILINGS[limitName]; const narrowed = Math.max(1, Math.floor(ceiling / 2)); expect( normalizeToolcraftModelFileDrop( modelControl({ modelLimits: { [limitName]: narrowed } }), ).modelLimits[limitName], ).toBe(narrowed); expect(() => normalizeToolcraftModelFileDrop( modelControl({ modelLimits: { [limitName]: ceiling + 1 } }), ), ).toThrow(new RegExp(`${limitName}.*protected ceiling`, "u")); }); it.each([0, -1, 1.5, Number.NaN, Number.POSITIVE_INFINITY])( "rejects a non-safe-integer limit %s", (limit) => { expect(() => normalizeToolcraftModelFileDrop( modelControl({ modelLimits: { maxNodes: limit } }), ), ).toThrow(/maxNodes must be finite and positive/u); }, ); it("rejects unsupported limit keys and model formats", () => { expect(() => normalizeToolcraftModelFileDrop( modelControl({ modelLimits: { unknown: 1 } as Partial, }), ), ).toThrow(/unsupported model limit "unknown"/u); expect(() => normalizeToolcraftModelFileDrop( modelControl({ modelFormats: ["usd"] as never }), ), ).toThrow(/supported formats are glb, gltf, fbx, obj, stl, ply/u); }); });