108 lines
4.0 KiB
JavaScript
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)))]),
|
|
),
|
|
};
|
|
}
|