Files
workinf_Blender_Wasm/web/tests/unit/external-vfont.test.mjs
mes123456 0fe8d2bb56
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Advance M8-M11 parity workflows
2026-08-17 04:37:07 -04:00

166 lines
7.8 KiB
JavaScript

import assert from "node:assert/strict";
import crypto from "node:crypto";
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(), "external-vfont-unit-"));
for (const name of ["asset-path", "external-vfont"]) {
const sourcePath = path.join(repoRoot, `web/protocol/${name}.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, []);
fs.writeFileSync(path.join(temporary, `${name}.mjs`), transpiled.outputText.replace('"./asset-path"', '"./asset-path.mjs"'));
}
const font = await import(pathToFileURL(path.join(temporary, "external-vfont.mjs")));
const importSourcePath = path.join(repoRoot, "web/app/src/fonts/external-vfont-import.ts");
const importTranspiled = ts.transpileModule(fs.readFileSync(importSourcePath, "utf8"), {
compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 },
fileName: importSourcePath,
reportDiagnostics: true,
});
assert.deepEqual(importTranspiled.diagnostics, []);
fs.writeFileSync(path.join(temporary, "external-vfont-import.mjs"), importTranspiled.outputText.replace('"../../../protocol/external-vfont"', '"./external-vfont.mjs"'));
const importer = await import(pathToFileURL(path.join(temporary, "external-vfont-import.mjs")));
const pfb = fs.readFileSync(path.join(repoRoot, "blender-5.2.0/release/datafiles/bfont.pfb"));
const data = pfb.buffer.slice(pfb.byteOffset, pfb.byteOffset + pfb.byteLength);
const digest = crypto.createHash("sha256").update(pfb).digest("hex");
const request = { sourcePath: "//fonts/bfont.pfb", mimeType: "application/x-font-type1", byteLength: pfb.byteLength, sha256: digest, data };
test("M9-01 validates a real project-relative PFB before storage or Main mutation", async () => {
const protocolSource = fs.readFileSync(path.join(repoRoot, "web/protocol/external-vfont.ts"), "utf8");
assert.doesNotMatch(protocolSource, /StorageClient|WebEngineClient|storage\.worker|web-engine\.worker/);
const validated = await font.validateExternalVFontImport(request);
assert.deepEqual({ ...validated, data: undefined }, {
schemaVersion: 1,
sourcePath: "//fonts/bfont.pfb",
fileName: "bfont.pfb",
format: "PFB",
mimeType: "application/x-font-type1",
byteLength: 25181,
sha256: "a33954fdab9fb09b9d308cb7f970518293128922ffc523c0a22b3b314a9a56c6",
data: undefined,
});
assert.notEqual(validated.data, request.data);
assert.deepEqual(new Uint8Array(validated.data), new Uint8Array(request.data));
});
test("M9-01 blocks path escape, type spoofing, size overflow and hash drift", async () => {
const cases = [
[{ ...request, sourcePath: "../../outside.pfb" }, "NON_MESH_RESOURCE_OUTSIDE_PROJECT"],
[{ ...request, sourcePath: "//assets/bfont.pfb" }, "NON_MESH_RESOURCE_OUTSIDE_PROJECT"],
[{ ...request, sourcePath: "//fonts/bfont.ttf", mimeType: "font/ttf" }, "NON_MESH_BINARY_INVALID"],
[{ ...request, mimeType: "font/otf" }, "NON_MESH_BINARY_INVALID"],
[{ ...request, byteLength: font.EXTERNAL_VFONT_MAX_BYTES + 1 }, "NON_MESH_DATA_BUDGET_EXCEEDED"],
[{ ...request, byteLength: request.byteLength - 1 }, "NON_MESH_BINARY_INVALID"],
[{ ...request, sha256: "0".repeat(64) }, "ASSET_SOURCE_HASH_MISMATCH"],
];
for (const [value, code] of cases) await assert.rejects(font.validateExternalVFontImport(value), (error) => error.code === code);
});
test("M9-02 accepts only a matching OPFS content-addressed receipt before Main import", async () => {
const validated = await font.validateExternalVFontImport(request);
const stored = {
assetId: `sha256:${digest}`,
projectId: "m9-vfont-unit",
sha256: digest,
bytes: pfb.byteLength,
mimeType: request.mimeType,
sourcePath: "fonts/bfont.pfb",
path: `projects/m9-vfont-unit/assets/sha256/${digest.slice(0, 2)}/${digest}`,
createdAt: "2026-08-16T00:00:00.000Z",
lastAccessAt: "2026-08-16T00:00:00.000Z",
persisted: true,
deduplicated: false,
};
const mainImport = font.createExternalVFontMainImport(validated, stored);
assert.deepEqual({ ...mainImport, data: undefined }, {
schemaVersion: 1,
projectId: stored.projectId,
assetId: stored.assetId,
assetPath: stored.path,
sourcePath: "//fonts/bfont.pfb",
name: "bfont",
format: "PFB",
mimeType: request.mimeType,
byteLength: pfb.byteLength,
sha256: digest,
data: undefined,
});
assert.notEqual(mainImport.data, validated.data);
for (const receipt of [
{ ...stored, persisted: false },
{ ...stored, sha256: "0".repeat(64) },
{ ...stored, bytes: stored.bytes - 1 },
{ ...stored, path: `indexeddb:${stored.projectId}:${digest}` },
]) {
assert.throws(() => font.createExternalVFontMainImport(validated, receipt), (error) =>
error.code === "ASSET_SOURCE_HASH_MISMATCH" || error.code === "NON_MESH_RESOURCE_MISSING");
}
});
test("M9-03 verifies the project asset before one Main font-style replacement", async () => {
const stored = {
asset: {
assetId: `sha256:${digest}`,
projectId: "m9-vfont-unit",
sha256: digest,
bytes: pfb.byteLength,
mimeType: request.mimeType,
sourcePath: "fonts/bfont.pfb",
path: `projects/m9-vfont-unit/assets/sha256/${digest.slice(0, 2)}/${digest}`,
createdAt: "2026-08-16T00:00:00.000Z",
lastAccessAt: "2026-08-16T00:00:00.000Z",
},
data: data.slice(0),
};
const vfont = { id: "vfont:bfont", name: "bfont", sourcePath: "//fonts/bfont.pfb", builtin: false, packed: true, packedByteLength: pfb.byteLength, sha256: digest };
const original = { regular: "vfont:Bfont", bold: "vfont:Bfont", italic: "vfont:Bfont", boldItalic: "vfont:Bfont" };
const snapshot = { nonMeshData: [{ id: "data:font", type: "FONT", fontLinks: original }], vfonts: [vfont] };
const events = [];
const result = await importer.replaceExternalVFontStyleInMain({
projectId: stored.asset.projectId,
sha256: digest,
dataId: "data:font",
vfontId: vfont.id,
style: "regular",
snapshot,
storage: { readAsset: async () => { events.push("storage:verified"); return stored; } },
engine: { applyCommand: async (command) => {
events.push("main:committed");
return { snapshot: { ...snapshot, nonMeshData: [{ ...snapshot.nonMeshData[0], fontLinks: command.links }] } };
} },
});
assert.deepEqual(events, ["storage:verified", "main:committed"]);
assert.deepEqual(result.previousLinks, original);
assert.equal(result.links.regular, vfont.id);
});
test("M9-03 blocks a missing project asset before Main replacement", async () => {
let mainCalls = 0;
const snapshot = {
nonMeshData: [{ id: "data:font", type: "FONT", fontLinks: { regular: "vfont:Bfont", bold: "vfont:Bfont", italic: "vfont:Bfont", boldItalic: "vfont:Bfont" } }],
vfonts: [{ id: "vfont:bfont", name: "bfont", sourcePath: "//fonts/bfont.pfb", builtin: false, packed: true, packedByteLength: pfb.byteLength, sha256: digest }],
};
await assert.rejects(importer.replaceExternalVFontStyleInMain({
projectId: "m9-vfont-unit",
sha256: digest,
dataId: "data:font",
vfontId: "vfont:bfont",
style: "regular",
snapshot,
storage: { readAsset: async () => { const error = new Error("missing"); error.code = "NON_MESH_RESOURCE_MISSING"; throw error; } },
engine: { applyCommand: async () => { mainCalls += 1; throw new Error("unexpected Main call"); } },
}), (error) => error.code === "NON_MESH_RESOURCE_MISSING");
assert.equal(mainCalls, 0);
});
test.after(() => fs.rmSync(temporary, { recursive: true, force: true }));