import { describe, expect, it } from "vitest"; import { TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS } from "../model-import-limits"; import { analyzeToolcraftModel } from "./model-topology-analysis"; import { estimateToolcraftTopologyResources, preflightToolcraftModelTopologyResources, } from "./model-topology-resources"; import { boundsForPositions, createClosedSolidWithDuplicatePrimitive, createDisconnectedClosedPrimitive, createOpenPlanePrimitive, createRepairablePrimitive, createTopologyDocument, createTopologyPrimitive, } from "./model-topology-test-support"; describe("analyzeToolcraftModel", () => { it("rejects resource counts before graph construction", () => { class GraphHostilePositions extends Float32Array { override [Symbol.iterator](): ArrayIterator { throw new Error("topology graph enumerated positions"); } } const positions = new GraphHostilePositions([ 0, 0, 0, 1, 0, 0, 0, 1, 0, ]); const primitive = createTopologyPrimitive({ bounds: { min: [0, 0, 0], max: [1, 1, 0] }, indices: new Uint32Array([0, 1, 2]), positions, }); const analysis = analyzeToolcraftModel( createTopologyDocument([primitive]), "realtime-mesh", { ...TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS, maxVertices: 2, }, ); expect(analysis.outcome).toBe("fatal"); expect(analysis.repairPlan).toBeUndefined(); expect(analysis.statistics.vertexCount).toBe(3); expect(analysis.statistics.edgeCount).toBe(0); expect(analysis.diagnostics).toContainEqual(expect.objectContaining({ affectedCount: 3, code: "resource-limit-exceeded", severity: "fatal", })); }); it("rejects conservative worker memory before graph construction", () => { class GraphHostilePositions extends Float32Array { override [Symbol.iterator](): ArrayIterator { throw new Error("topology graph enumerated positions"); } } const positions = new GraphHostilePositions([ 0, 0, 0, 1, 0, 0, 0, 1, 0, ]); const primitive = createTopologyPrimitive({ bounds: { min: [0, 0, 0], max: [1, 1, 0] }, indices: new Uint32Array([0, 1, 2]), positions, }); const document = createTopologyDocument([primitive]); const preflight = preflightToolcraftModelTopologyResources(document); const analysis = analyzeToolcraftModel( document, "realtime-mesh", { ...TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS, maxEstimatedWorkerBytes: preflight.estimatedPeakWorkerBytes - 1, }, ); expect(analysis.outcome).toBe("fatal"); expect(analysis.repairPlan).toBeUndefined(); expect(analysis.statistics.estimatedPeakWorkerBytes).toBe( preflight.estimatedPeakWorkerBytes, ); expect(analysis.diagnostics).toContainEqual(expect.objectContaining({ affectedCount: preflight.estimatedPeakWorkerBytes, code: "resource-limit-exceeded", severity: "fatal", })); }); it("conservatively rejects the combined 1M triangle and vertex envelope", () => { const estimate = estimateToolcraftTopologyResources({ decodedBytes: 36_000_000, nodeCount: 1, primitiveCount: 1, triangleCount: 1_000_000, vertexCount: 1_000_000, }); expect(estimate.estimatedPeakWorkerBytes).toBeGreaterThan( TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS.maxEstimatedWorkerBytes, ); expect(estimate.estimatedRepairBytes).toBeGreaterThan(600_000_000); }); it("saturates hostile resource arithmetic without overflow", () => { const estimate = estimateToolcraftTopologyResources({ decodedBytes: Number.MAX_SAFE_INTEGER - 1, nodeCount: Number.MAX_SAFE_INTEGER, primitiveCount: Number.MAX_SAFE_INTEGER, triangleCount: Number.MAX_SAFE_INTEGER, vertexCount: Number.MAX_SAFE_INTEGER, }); expect(estimate).toEqual({ decodedBytes: Number.MAX_SAFE_INTEGER - 1, estimatedPeakWorkerBytes: Number.MAX_SAFE_INTEGER, estimatedRepairBytes: Number.MAX_SAFE_INTEGER, }); }); it("reports the same resource estimate used by allocation preflight", () => { const document = createTopologyDocument([createOpenPlanePrimitive()]); const preflight = preflightToolcraftModelTopologyResources(document); const analysis = analyzeToolcraftModel(document, "realtime-mesh"); expect(analysis.statistics).toMatchObject(preflight); expect(analysis.statistics.decodedBytes).toBe(preflight.decodedBytes); expect(analysis.statistics.estimatedRepairBytes).toBe( preflight.estimatedRepairBytes, ); expect(analysis.statistics.estimatedPeakWorkerBytes).toBe( preflight.estimatedPeakWorkerBytes, ); }); it("reports exact clean closed-manifold statistics", () => { const analysis = analyzeToolcraftModel( createTopologyDocument(), "solid-mesh", ); expect(analysis.outcome).toBe("clean"); expect(analysis.repairPlan).toBeUndefined(); expect(analysis.statistics).toMatchObject({ boundaryEdgeCount: 0, componentCount: 1, edgeCount: 6, nonManifoldEdgeCount: 0, nonManifoldVertexCount: 0, primitiveCount: 1, triangleCount: 4, vertexCount: 4, }); expect(analysis.diagnostics.every((diagnostic) => diagnostic.severity === "info" )).toBe(true); }); it("permits an open plane for realtime and rejects it for solid", () => { const document = createTopologyDocument([createOpenPlanePrimitive()]); const realtime = analyzeToolcraftModel(document, "realtime-mesh"); const solid = analyzeToolcraftModel(document, "solid-mesh"); expect(realtime.outcome).toBe("clean"); expect(realtime.diagnostics).toContainEqual(expect.objectContaining({ affectedCount: 4, code: "boundary-edges", primitiveId: "primitive-1", primitiveIndex: 0, severity: "info", })); expect(solid.outcome).toBe("fatal"); expect(solid.repairPlan).toBeUndefined(); expect(solid.diagnostics).toContainEqual(expect.objectContaining({ code: "boundary-edges", severity: "fatal", })); }); it("permits multiple disconnected closed manifold shells under both profiles", () => { const document = createTopologyDocument([ createDisconnectedClosedPrimitive(), ]); expect(analyzeToolcraftModel(document, "realtime-mesh")).toMatchObject({ outcome: "clean", statistics: { componentCount: 2 }, }); const solid = analyzeToolcraftModel(document, "solid-mesh"); expect(solid.outcome).toBe("clean"); expect(solid.repairPlan).toBeUndefined(); expect(solid.diagnostics).toContainEqual(expect.objectContaining({ affectedCount: 2, code: "disconnected-components", severity: "info", })); }); it("compiles a safe solid candidate for exact duplicate removal", () => { const analysis = analyzeToolcraftModel( createTopologyDocument([createClosedSolidWithDuplicatePrimitive()]), "solid-mesh", ); expect(analysis.outcome).toBe("repairable"); expect(analysis.diagnostics).toContainEqual(expect.objectContaining({ affectedCount: 1, code: "duplicate-triangles", severity: "repairable", })); expect(analysis.statistics.nonManifoldEdgeCount).toBe(3); expect(analysis.diagnostics).toContainEqual(expect.objectContaining({ affectedCount: 3, code: "non-manifold-edges", severity: "repairable", })); expect(analysis.repairPlan?.operations.map(({ type }) => type)).toEqual([ "remove-duplicate-triangles", "recalculate-bounds", ]); }); it("reports renderable non-manifold edges and vertices without proposing unsupported repair", () => { const positions = new Float32Array([ 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, ]); const primitive = createTopologyPrimitive({ bounds: boundsForPositions(positions), indices: new Uint32Array([0, 1, 2, 1, 0, 3, 0, 1, 4]), normals: new Float32Array(positions.length).fill(1), positions, }); const document = createTopologyDocument([primitive]); const realtime = analyzeToolcraftModel(document, "realtime-mesh"); expect(realtime.outcome).toBe("warning"); expect(realtime.repairPlan).toBeUndefined(); expect(realtime.diagnostics).toEqual(expect.arrayContaining([ expect.objectContaining({ code: "non-manifold-edges", severity: "warning" }), expect.objectContaining({ code: "non-manifold-vertices", severity: "warning" }), ])); const solid = analyzeToolcraftModel(document, "solid-mesh"); expect(solid.outcome).toBe("fatal"); expect(solid.repairPlan).toBeUndefined(); }); it("compiles one complete repair plan for removable triangle defects and unused vertices", () => { const analysis = analyzeToolcraftModel( createTopologyDocument([createRepairablePrimitive()]), "realtime-mesh", ); expect(analysis.outcome).toBe("repairable"); expect(analysis.diagnostics).toEqual(expect.arrayContaining([ expect.objectContaining({ affectedCount: 1, code: "repeated-index-triangles", severity: "repairable" }), expect.objectContaining({ affectedCount: 1, code: "zero-area-triangles", severity: "repairable" }), expect.objectContaining({ affectedCount: 1, code: "duplicate-triangles", severity: "repairable" }), expect.objectContaining({ affectedCount: 3, code: "unused-vertices", severity: "repairable" }), expect.objectContaining({ affectedCount: 7, code: "normals-missing", severity: "repairable" }), ])); expect(analysis.repairPlan?.operations.map(({ type }) => type)).toEqual([ "remove-invalid-triangles", "remove-duplicate-triangles", "compact-unused-vertices", "regenerate-normals", "recalculate-bounds", ]); expect(Object.isFrozen(analysis.repairPlan)).toBe(true); expect(Object.isFrozen(analysis.repairPlan?.operations)).toBe(true); }); it.each([ ["normals-missing", undefined], ["normals-count-mismatch", new Float32Array([0, 0, 1])], ["normals-non-finite", new Float32Array([NaN, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1])], ["normals-zero-length", new Float32Array(12)], ] as const)("marks %s as deterministically repairable", (code, normals) => { const primitive = createTopologyPrimitive({ normals }); const analysis = analyzeToolcraftModel( createTopologyDocument([primitive]), "realtime-mesh", ); expect(analysis.outcome).toBe("repairable"); expect(analysis.diagnostics).toContainEqual(expect.objectContaining({ code, severity: "repairable", })); expect(analysis.repairPlan?.operations.map(({ type }) => type)).toContain( "regenerate-normals", ); }); it("plans local winding repair only for a proven orientable component", () => { const primitive = createOpenPlanePrimitive({ indices: new Uint32Array([0, 1, 2, 0, 3, 2]), }); const analysis = analyzeToolcraftModel( createTopologyDocument([primitive]), "realtime-mesh", ); expect(analysis.outcome).toBe("repairable"); expect(analysis.diagnostics).toContainEqual(expect.objectContaining({ affectedCount: 1, code: "inconsistent-local-winding", severity: "repairable", })); expect(analysis.repairPlan?.operations).toContainEqual(expect.objectContaining({ flippedTriangleIndices: [1], primitiveId: "primitive-1", type: "repair-local-winding", })); }); it("never plans local winding repair across a non-manifold vertex fan", () => { const positions = new Float32Array([ 0, 0, 0, 1, 0, 0, 0, 1, 0, -2, 0, 0, 0, 0, 1, 0, -1, 0, ]); const primitive = createTopologyPrimitive({ bounds: boundsForPositions(positions), indices: new Uint32Array([0, 1, 2, 0, 3, 2, 0, 4, 5]), normals: undefined, positions, }); const analysis = analyzeToolcraftModel( createTopologyDocument([primitive]), "realtime-mesh", ); expect(analysis.diagnostics).toContainEqual(expect.objectContaining({ code: "non-manifold-vertices", severity: "warning", })); expect(analysis.outcome).toBe("repairable"); expect(analysis.repairPlan?.operations.map(({ type }) => type)).toContain( "regenerate-normals", ); expect(analysis.repairPlan?.operations.map(({ type }) => type)).not.toContain( "repair-local-winding", ); }); it("returns fatal without a plan for structurally unsafe geometry", () => { const positions = new Float32Array([0, 0, 0, 1, 0, 0, NaN, 1, 0]); const primitive = createTopologyPrimitive({ bounds: { min: [0, 0, 0], max: [1, 1, 0] }, indices: new Uint32Array([0, 1, 7]), positions, }); const analysis = analyzeToolcraftModel( createTopologyDocument([primitive]), "realtime-mesh", ); expect(analysis.outcome).toBe("fatal"); expect(analysis.repairPlan).toBeUndefined(); expect(analysis.diagnostics).toEqual(expect.arrayContaining([ expect.objectContaining({ code: "non-finite-positions", severity: "fatal" }), expect.objectContaining({ code: "indices-out-of-range", severity: "fatal" }), ])); }); it("reports invalid bounds as repairable when finite geometry can recalculate them", () => { const primitive = createTopologyPrimitive({ bounds: { min: [-10, -10, -10], max: [10, 10, 10] }, }); const document = createTopologyDocument([primitive]); document.bounds = { min: [-20, -20, -20], max: [20, 20, 20] }; const analysis = analyzeToolcraftModel(document, "realtime-mesh"); expect(analysis.outcome).toBe("repairable"); expect(analysis.diagnostics).toEqual(expect.arrayContaining([ expect.objectContaining({ code: "invalid-primitive-bounds", severity: "repairable" }), expect.objectContaining({ code: "invalid-document-bounds", severity: "repairable" }), ])); }); it("uses overflow-safe exact resource estimates and enforces supplied ceilings", () => { const document = createTopologyDocument([createOpenPlanePrimitive()]); const baseline = analyzeToolcraftModel(document, "realtime-mesh"); expect(baseline.statistics.decodedBytes).toBe(120); expect(baseline.statistics.estimatedRepairBytes).toBeGreaterThan(0); expect(baseline.statistics.estimatedPeakWorkerBytes).toBe( baseline.statistics.decodedBytes + baseline.statistics.estimatedRepairBytes, ); const limited = analyzeToolcraftModel(document, "realtime-mesh", { ...TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS, maxDecodedBytes: 119, }); expect(limited.outcome).toBe("fatal"); expect(limited.repairPlan).toBeUndefined(); expect(limited.diagnostics).toContainEqual(expect.objectContaining({ affectedCount: 120, code: "resource-limit-exceeded", severity: "fatal", })); }); it.each([ ["null", null], ["array", []], ["unknown key", { unknown: 1 }], ["zero", { maxNodes: 0 }], ["negative", { maxNodes: -1 }], ["NaN", { maxNodes: Number.NaN }], ["infinity", { maxNodes: Number.POSITIVE_INFINITY }], ["above protected ceiling", { maxNodes: TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS.maxNodes + 1, }], ] as const)("rejects hostile direct requestedLimits: %s", (_label, limits) => { expect(() => analyzeToolcraftModel( createTopologyDocument(), "realtime-mesh", limits as never, )).toThrow(); }); it("emits diagnostics in deterministic primitive/code order with explanations", () => { const first = createOpenPlanePrimitive({ id: "z-primitive", normals: undefined }); const second = createOpenPlanePrimitive({ id: "a-primitive", normals: undefined }); const analysis = analyzeToolcraftModel( createTopologyDocument([first, second]), "realtime-mesh", ); expect(analysis.diagnostics.every(({ affectedCount, code, explanation, severity }) => Number.isSafeInteger(affectedCount) && affectedCount >= 0 && code.length > 0 && explanation.length > 0 && severity.length > 0 )).toBe(true); expect(analysis.diagnostics.filter(({ primitiveIndex }) => primitiveIndex !== undefined) .map(({ primitiveIndex }) => primitiveIndex)).toEqual([0, 0, 1, 1]); }); });