import fs from "node:fs"; import path from "node:path"; import { expect, test } from "@playwright/test"; const root = path.resolve(import.meta.dirname, "../../.."); const source = fs.readFileSync(path.join(root, "tests/files/web/m12_glb_desktop_v1/mesh.glb")); const maxBytes = 512 * 1024; function rewriteJson(mutator: (document: any) => void): Buffer { const jsonLength = source.readUInt32LE(12); const document = JSON.parse(source.subarray(20, 20 + jsonLength).toString("utf8").trim()); mutator(document); const json = Buffer.from(JSON.stringify(document)); const paddedLength = (json.length + 3) & ~3; const output = Buffer.alloc(12 + 8 + paddedLength + (source.length - (20 + jsonLength))); output.writeUInt32LE(0x46546c67, 0); output.writeUInt32LE(2, 4); output.writeUInt32LE(output.length, 8); output.writeUInt32LE(paddedLength, 12); output.writeUInt32LE(0x4e4f534a, 16); json.copy(output, 20); output.fill(0x20, 20 + json.length, 20 + paddedLength); output.writeUInt32LE(source.readUInt32LE(20 + jsonLength), 20 + paddedLength); output.writeUInt32LE(source.readUInt32LE(24 + jsonLength), 24 + paddedLength); source.subarray(28 + jsonLength).copy(output, 28 + paddedLength); return output; } test("rejects GLB sparse, extension, external URI and over-budget cases in a Chromium Worker", async ({ page }) => { await page.goto("/"); const cases = [ { id: "sparse", bytes: rewriteJson((document) => { document.accessors[0].sparse = { count: 1 }; }) }, { id: "extension", bytes: rewriteJson((document) => { document.extensionsUsed = ["KHR_draco_mesh_compression"]; }) }, { id: "external-uri", bytes: rewriteJson((document) => { document.buffers[0].uri = "external.bin"; }) }, { id: "over-budget", bytes: Buffer.concat([source, Buffer.alloc(maxBytes + 1 - source.length)]) }, ].map((candidate) => ({ id: candidate.id, bytes: Array.from(candidate.bytes) })); const result = await page.evaluate((input) => new Promise<{ ok: boolean; results?: Array<{ id: string; code: string }>; error?: string }>((resolve, reject) => { const worker = new Worker("/src/workers/glb-negative-cases-test.worker.ts", { type: "module" }); worker.onmessage = (event: MessageEvent<{ ok: boolean; results?: Array<{ id: string; code: string }>; error?: string }>) => { worker.terminate(); resolve(event.data); }; worker.onerror = (event) => { worker.terminate(); reject(new Error(event.message)); }; const cases = input.map((candidate) => ({ id: candidate.id, bytes: Uint8Array.from(candidate.bytes).buffer })); worker.postMessage({ cases }, cases.map((candidate) => candidate.bytes)); }), cases); expect(result).toEqual({ ok: true, results: [ { id: "sparse", code: "GLB_SPARSE_ACCESSOR_UNSUPPORTED" }, { id: "extension", code: "GLB_EXTENSION_UNSUPPORTED" }, { id: "external-uri", code: "GLB_EXTERNAL_URI_BLOCKED" }, { id: "over-budget", code: "GLB_IMPORT_BUDGET_EXCEEDED" }, ] }); });