import { describe, expect, it } from "vitest"; import { validateToolcraftModelDocument } from "../canonical/model-document-validation"; import { ASCII_STL_FIXTURE, contextFor, createBinaryColoredStlFixture, createBinaryStlFixture, transferForStaticModelBytes, } from "../test-fixtures/model-fixtures"; import { TOOLCRAFT_STL_ADAPTER_VERSION, toolcraftStlModelFormatAdapter, } from "./stl-model-format-adapter"; function context(bytes: Uint8Array) { return contextFor(transferForStaticModelBytes(bytes, "mesh.stl", "model/stl")); } describe("STL model format adapter", () => { it("is immutable and worker-capable", () => { expect(toolcraftStlModelFormatAdapter).toMatchObject({ adapterVersion: TOOLCRAFT_STL_ADAPTER_VERSION, format: "stl", rootExtensions: [".stl"], workerCapable: true, }); expect(Object.isFrozen(toolcraftStlModelFormatAdapter)).toBe(true); }); it.each([ ["ASCII", ASCII_STL_FIXTURE, [10, 20, 30, 12, 20, 30, 10, 23, 30]], ["binary", createBinaryStlFixture(), [-4, -2, 1, -2, -2, 1, -4, -1, 1]], ] as const)("decodes %s STL geometry", async (_encoding, bytes, positions) => { const result = await toolcraftStlModelFormatAdapter.decode(context(bytes)); expect(validateToolcraftModelDocument(result.document)).toEqual({ ok: true }); expect(result.document.primitives).toHaveLength(1); expect(result.document.primitives[0]!.positions).toEqual( new Float32Array(positions), ); expect(result.document.primitives[0]!.indices).toEqual( new Uint32Array([0, 1, 2]), ); expect(result.document.provenance).toEqual({ adapterVersion: TOOLCRAFT_STL_ADAPTER_VERSION, canonicalSchemaVersion: 2, operations: ["sequential-indexing"], sourceFormat: "stl", }); expect(result.document.version).toBe(2); if (result.document.version !== 2) throw new Error("Expected v2 document."); expect(result.document.materials).toEqual([]); }); it("preserves the deterministic Magics header color and alpha", async () => { const result = await toolcraftStlModelFormatAdapter.decode( context(createBinaryColoredStlFixture()), ); expect(result.document.version).toBe(2); if (result.document.version !== 2) throw new Error("Expected v2 document."); expect(result.document.primitives[0]!.colors).toEqual({ components: 3, values: new Float32Array([1, 0, 0, 1, 0, 0, 1, 0, 0]), }); expect(result.document.primitives[0]!.materialId).toBe("material:0"); expect(result.document.materials).toEqual([ expect.objectContaining({ alphaMode: "blend", id: "material:0", opacity: 128 / 255, usesVertexColor: true, }), ]); }); it("rejects truncated, empty, and non-finite STL structures", async () => { await expect( toolcraftStlModelFormatAdapter.decode(context(new Uint8Array(20))), ).rejects.toMatchObject({ category: "format" }); await expect( toolcraftStlModelFormatAdapter.decode( context( new TextEncoder().encode( `solid empty\n${" ".repeat(100)}\nendsolid empty\n`, ), ), ), ).rejects.toMatchObject({ category: "geometry" }); const nonFinite = createBinaryStlFixture(); new DataView(nonFinite.buffer).setFloat32(84 + 12, Number.NaN, true); await expect( toolcraftStlModelFormatAdapter.decode(context(nonFinite)), ).rejects.toMatchObject({ category: "geometry", code: "non-finite-position" }); }); it("preflights declared binary counts against limits", async () => { await expect( toolcraftStlModelFormatAdapter.decode({ ...context(createBinaryStlFixture()), limits: { ...context(createBinaryStlFixture()).limits, maxVertices: 2 }, }), ).rejects.toMatchObject({ category: "resource-limit", code: "max-vertices-exceeded", }); }); });