import assert from "node:assert/strict"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import test from "node:test"; import ts from "typescript"; const repoRoot = path.resolve(import.meta.dirname, "../../.."); const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "paint-depth-visibility-unit-")); function transpile(sourceName, outputName, transform = (source) => source) { const sourcePath = path.join(repoRoot, "web/protocol", sourceName); const transpiled = ts.transpileModule(fs.readFileSync(sourcePath, "utf8"), { compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 }, fileName: sourcePath, reportDiagnostics: true, }); assert.deepEqual(transpiled.diagnostics, []); fs.writeFileSync(path.join(temporary, outputName), transform(transpiled.outputText)); } transpile("paint.ts", "paint.mjs"); transpile("paint-depth-visibility.ts", "paint-depth-visibility.mjs", (source) => source.replace('from "./paint"', 'from "./paint.mjs"')); const visibility = await import(pathToFileURL(path.join(temporary, "paint-depth-visibility.mjs"))); const request = { schemaVersion: 1, objectId: "object:Paint", meshId: "mesh:Paint", revision: 7, vertexIndices: [6, 2, 4], }; test("M9-09 binds GPU depth visibility to stable vertex identities and Main revision", () => { const parsed = visibility.validatePaintDepthVisibilityRequest(request, 7); assert.deepEqual(parsed.vertexIndices, [2, 4, 6]); const result = visibility.validatePaintDepthVisibilityResult({ ...parsed, backend: "MAIN_THREAD_WEBGL2", source: "GPU_RGBA_DEPTH_READBACK", width: 64, height: 32, depthReadbackBytes: 64 * 32 * 4, occluderPixelCount: 512, visibleVertexIndices: [2, 6], }, parsed); assert.deepEqual(result.visibleVertexIndices, [2, 6]); assert.equal(result.source, "GPU_RGBA_DEPTH_READBACK"); }); test("M9-09 fails closed on stale, forged, duplicate and over-budget depth samples", () => { assert.throws(() => visibility.validatePaintDepthVisibilityRequest(request, 8), (error) => error.code === "REVISION_CONFLICT"); assert.throws(() => visibility.validatePaintDepthVisibilityRequest({ ...request, objectId: "mesh:Paint" }, 7), (error) => error.code === "PAINT_SCHEMA_INVALID"); assert.throws(() => visibility.validatePaintDepthVisibilityRequest({ ...request, vertexIndices: [2, 2] }, 7), (error) => error.code === "PAINT_SCHEMA_INVALID"); assert.throws(() => visibility.validatePaintDepthVisibilityRequest({ ...request, undeclared: true }, 7), (error) => error.code === "PAINT_SCHEMA_INVALID"); assert.throws( () => visibility.validatePaintDepthVisibilityRequest({ ...request, vertexIndices: Array.from({ length: 100_001 }, (_, index) => index) }, 7), (error) => error.code === "PAINT_BUDGET_EXCEEDED", ); }); test("M9-09 rejects result drift and accepts a verified empty visible set", () => { const parsed = visibility.validatePaintDepthVisibilityRequest(request, 7); const base = { ...parsed, backend: "OFFSCREEN_WEBGL2", source: "GPU_RGBA_DEPTH_READBACK", width: 32, height: 32, depthReadbackBytes: 4096, occluderPixelCount: 128, visibleVertexIndices: [], }; assert.deepEqual(visibility.validatePaintDepthVisibilityResult(base, parsed).visibleVertexIndices, []); assert.throws(() => visibility.validatePaintDepthVisibilityResult({ ...base, visibleVertexIndices: [99] }, parsed), (error) => error.code === "PAINT_SCHEMA_INVALID"); assert.throws(() => visibility.validatePaintDepthVisibilityResult({ ...base, depthReadbackBytes: 4095 }, parsed), (error) => error.code === "PAINT_BUDGET_EXCEEDED"); assert.throws(() => visibility.validatePaintDepthVisibilityResult({ ...base, source: "CPU_RAYCAST" }, parsed), (error) => error.code === "PAINT_SCHEMA_INVALID"); });