Files
workinf_Blender_Wasm/tools/web/v1-acceptance-lib.mjs
2026-08-15 01:01:23 -04:00

108 lines
4.0 KiB
JavaScript

import assert from "node:assert/strict";
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
export const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
export const ledgerPath = path.join(root, "docs/status/parity-ledger.json");
export const packagePath = path.join(root, "web/package.json");
export const evidencePath = path.join(root, "docs/status/v1-acceptance-evidence.json");
export const toolRelativePaths = [
"tools/web/v1-acceptance-lib.mjs",
"tools/web/check-v1-acceptance-coverage.mjs",
"tools/web/run-v1-acceptance.mjs",
"tools/web/check-v1-acceptance-evidence.mjs",
];
export function sha256(value) {
return crypto.createHash("sha256").update(value).digest("hex");
}
export function readAcceptanceInputs() {
const ledgerBytes = fs.readFileSync(ledgerPath);
const packageBytes = fs.readFileSync(packagePath);
return {
ledgerBytes,
packageBytes,
ledger: JSON.parse(ledgerBytes),
packageManifest: JSON.parse(packageBytes),
};
}
function resolveAcceptance(token, scripts) {
assert.equal(typeof token, "string", "acceptance token must be a string");
if (token.startsWith("web:e2e:")) {
const pattern = token.slice("web:e2e:".length);
assert.ok(pattern.trim().length > 0, `${token}: E2E pattern must not be empty`);
assert.equal(typeof scripts["test:e2e"], "string", `${token}: web test:e2e script is missing`);
return {
token,
kind: "e2e",
script: "test:e2e",
args: ["--", "--workers=1", "-g", pattern],
command: `npm --prefix web run test:e2e -- --workers=1 -g ${JSON.stringify(pattern)}`,
};
}
const match = /^web:(test|release):(.+)$/.exec(token);
assert.ok(match, `${token}: unsupported acceptance namespace`);
const script = `${match[1]}:${match[2]}`;
assert.equal(typeof scripts[script], "string", `${token}: web package script ${script} is missing`);
return {
token,
kind: "script",
script,
args: [],
command: `npm --prefix web run ${script}`,
};
}
export function buildAcceptancePlan(ledger, packageManifest) {
assert.equal(ledger.schemaVersion, 2, "unsupported parity ledger schema");
assert.ok(Array.isArray(ledger.families), "parity ledger families must be an array");
assert.equal(ledger.families.length, 12, "V1 acceptance expects N-015 through N-026");
assert.ok(packageManifest.scripts && typeof packageManifest.scripts === "object", "web package scripts are missing");
const byToken = new Map();
let declarationCount = 0;
for (const family of ledger.families) {
assert.match(family.id, /^N-0(?:1[5-9]|2[0-6])$/, `${family.id}: unexpected family id`);
assert.ok(Array.isArray(family.acceptance) && family.acceptance.length > 0, `${family.id}: acceptance is empty`);
assert.equal(new Set(family.acceptance).size, family.acceptance.length, `${family.id}: duplicate acceptance token`);
for (const token of family.acceptance) {
declarationCount += 1;
const existing = byToken.get(token);
if (existing) {
existing.familyIds.push(family.id);
continue;
}
byToken.set(token, { ...resolveAcceptance(token, packageManifest.scripts), familyIds: [family.id] });
}
}
const entries = [...byToken.values()];
return {
familyCount: ledger.families.length,
declarationCount,
uniqueCount: entries.length,
scriptCount: entries.filter((entry) => entry.kind === "script").length,
e2eCount: entries.filter((entry) => entry.kind === "e2e").length,
reusedCount: entries.filter((entry) => entry.familyIds.length > 1).length,
entries,
};
}
export function expectedEvidenceIdentity() {
const inputs = readAcceptanceInputs();
return {
...inputs,
plan: buildAcceptancePlan(inputs.ledger, inputs.packageManifest),
ledgerSha256: sha256(inputs.ledgerBytes),
packageSha256: sha256(inputs.packageBytes),
toolSourceSha256: Object.fromEntries(
toolRelativePaths.map((relativePath) => [relativePath, sha256(fs.readFileSync(path.join(root, relativePath)))]),
),
};
}