67 lines
3.2 KiB
JavaScript
67 lines
3.2 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
import ts from "typescript";
|
|
|
|
const repoRoot = path.resolve(import.meta.dirname, "../../..");
|
|
const sourcePath = path.join(repoRoot, "web/protocol/diagnostic-report.ts");
|
|
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, []);
|
|
const moduleUrl = "data:text/javascript;base64," + Buffer.from(transpiled.outputText).toString("base64");
|
|
const { APP_DIAGNOSTIC_MESSAGES, appendAppDiagnostic, createAppDiagnosticEntry, createAppDiagnosticReport } = await import(moduleUrl);
|
|
|
|
test("M7-17 keeps user summaries stable while retaining detailed causes", () => {
|
|
const cause = new Error("native allocator detail");
|
|
const error = new Error("WORKER_CRASH_INJECTED: engine.worker.ts:91", { cause });
|
|
error.code = "WORKER_TERMINATED";
|
|
const entry = createAppDiagnosticEntry({
|
|
sequence: 1,
|
|
occurredAt: "2026-08-15T20:00:00.000Z",
|
|
area: "ENGINE",
|
|
code: "ENGINE_WORKER_TERMINATED",
|
|
error,
|
|
context: { projectId: "basic_scene", revision: 9 },
|
|
});
|
|
assert.equal(entry.summary, APP_DIAGNOSTIC_MESSAGES.ENGINE_WORKER_TERMINATED);
|
|
assert.equal(entry.summary.includes("WORKER_CRASH_INJECTED"), false);
|
|
assert.match(entry.detail, /WORKER_CRASH_INJECTED/);
|
|
assert.equal(entry.sourceCode, "WORKER_TERMINATED");
|
|
assert.equal(entry.cause, "native allocator detail");
|
|
});
|
|
|
|
test("M7-17 bounds the ledger and exports a sequence-sorted schema v1 report", () => {
|
|
const entries = [3, 1, 2].map((sequence) => createAppDiagnosticEntry({
|
|
sequence,
|
|
occurredAt: `2026-08-15T20:00:0${sequence}.000Z`,
|
|
area: "STORAGE",
|
|
code: "STORAGE_BUDGET_FAILED",
|
|
error: { code: "STORAGE_TRANSACTION", message: `detail-${sequence}` },
|
|
}));
|
|
assert.deepEqual(appendAppDiagnostic(entries.slice(0, 2), entries[2], 2).map((entry) => entry.sequence), [1, 2]);
|
|
const report = createAppDiagnosticReport({
|
|
generatedAt: "2026-08-15T20:01:00.000Z",
|
|
runtime: { url: "http://127.0.0.1:5173/", userAgent: "test", language: "zh-CN", crossOriginIsolated: true },
|
|
project: { projectId: "basic_scene", revision: 9 },
|
|
entries,
|
|
});
|
|
assert.equal(report.schemaVersion, 1);
|
|
assert.equal(report.product, "Web Blender Modeler V1");
|
|
assert.deepEqual(report.entries.map((entry) => entry.sequence), [1, 2, 3]);
|
|
assert.deepEqual(report.entries.map((entry) => entry.detail), ["detail-1", "detail-2", "detail-3"]);
|
|
});
|
|
|
|
test("M7-17 user-visible status setters never interpolate raw exception detail", () => {
|
|
const appSource = fs.readFileSync(path.join(repoRoot, "web/app/src/app/App.tsx"), "utf8");
|
|
const statusLines = appSource.split("\n").filter((line) => /set(?:Engine|Storage|Wasm|Manifest)Status\(|setViewportError\(/.test(line));
|
|
assert.ok(statusLines.length > 20, "expected to audit all user-visible status boundaries");
|
|
for (const line of statusLines) {
|
|
assert.doesNotMatch(line, /error\.message|errorMessage\(|fault\.error\.message/, line.trim());
|
|
}
|
|
assert.doesNotMatch(appSource, /\{fault\.error\.message\}/);
|
|
});
|