137 lines
8.5 KiB
JavaScript
137 lines
8.5 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { spawnSync } from "node:child_process";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const reportPath = path.join(root, "tests/golden/WBV2/full-source-parity-validation-report.json");
|
|
const logRoot = path.join(root, "tests/golden/WBV2/full-source-parity-validation-logs");
|
|
const read = (file) => JSON.parse(fs.readFileSync(path.join(root, file), "utf8"));
|
|
const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
|
const artifact = (relative) => {
|
|
const file = path.join(root, relative);
|
|
return { path: relative, bytes: fs.statSync(file).size, sha256: sha256(file) };
|
|
};
|
|
const inventory = read("tests/golden/WBV2/dynamic-inventory.json");
|
|
const catalog = read("web/app/src/capabilities/blender-feature-route-catalog.json");
|
|
const featureSmoke = read("tests/golden/WBV2/feature-smoke-manifest.json");
|
|
const submoduleSmoke = read("tests/golden/WBV2/internal-submodule-smoke-manifest.json");
|
|
const acceptance = read("docs/status/v1-acceptance-evidence.json");
|
|
const releaseEvidence = read("docs/status/release-evidence.json");
|
|
const sourceSync = read("tests/golden/WBV2/web-engine-source-sync.json");
|
|
const focusedE2E = fs.readFileSync(path.join(logRoot, "29-focused-e2e-fixes.log"), "utf8");
|
|
const focusedWorker = fs.readFileSync(path.join(logRoot, "30-focused-engine-worker-recovery.log"), "utf8");
|
|
const initialUnit = fs.readFileSync(path.join(logRoot, "31-final-unit.log"), "utf8");
|
|
const focusedUnit = fs.readFileSync(path.join(logRoot, "32-focused-unit-control-plane.log"), "utf8");
|
|
|
|
assert.equal(catalog.unitCount, inventory.summary.unitCount);
|
|
assert.equal(catalog.classifiedCount, catalog.unitCount);
|
|
assert.equal(catalog.unclassifiedCount, 0);
|
|
assert.equal(featureSmoke.status, "pass");
|
|
assert.equal(featureSmoke.summary.totalFeatures, 34);
|
|
assert.equal(submoduleSmoke.status, "pass");
|
|
assert.equal(submoduleSmoke.summary.resolved, 101);
|
|
assert.equal(acceptance.summary.unique, 49);
|
|
assert.equal(acceptance.summary.passed, 49);
|
|
assert.equal(acceptance.summary.failed, 0);
|
|
assert.ok(releaseEvidence.evidence.records.some((item) => item.id === "v1-user-loop" && item.exitCode === 0));
|
|
assert.equal(sourceSync.status, "PASS");
|
|
assert.match(focusedE2E, /12 passed/u);
|
|
assert.match(focusedWorker, /1 passed/u);
|
|
assert.match(initialUnit, /tests 429/u);
|
|
assert.match(initialUnit, /pass 428/u);
|
|
assert.match(initialUnit, /fail 1/u);
|
|
assert.match(focusedUnit, /pass 1/u);
|
|
assert.match(focusedUnit, /fail 0/u);
|
|
|
|
const deployed = spawnSync(process.execPath, ["tools/web/check-deployed-web-blender.mjs"], {
|
|
cwd: root,
|
|
encoding: "utf8",
|
|
env: { ...process.env, WEB_ORIGIN: process.env.WEB_ORIGIN ?? "https://49.232.250.35:58014" },
|
|
maxBuffer: 8 * 1024 * 1024,
|
|
});
|
|
const deployedOutput = `${deployed.stdout ?? ""}\n${deployed.stderr ?? ""}`.trim();
|
|
fs.writeFileSync(path.join(logRoot, "34-final-deployed-site.log"), `${deployedOutput}\n`);
|
|
assert.equal(deployed.status, 0, deployedOutput);
|
|
assert.match(deployedOutput, /mainMutation=3->4 chromium=ready/u);
|
|
|
|
const sourceParityStatus = inventory.coverage.unverifiedUnits === 0 ? "READY" : "PARTIAL";
|
|
const report = {
|
|
schemaVersion: 1,
|
|
reportId: "WBV2-FULL-SOURCE-PARITY-VALIDATION-001",
|
|
generatedAt: new Date().toISOString(),
|
|
gitCommit: spawnSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).stdout.trim(),
|
|
sourceParityStatus,
|
|
webProductStatus: "READY",
|
|
releaseStatus: "READY",
|
|
inventory: {
|
|
sourceFiles: inventory.summary.sourceFiles,
|
|
unitCount: inventory.summary.unitCount,
|
|
classifiedCount: catalog.classifiedCount,
|
|
unclassifiedCount: catalog.unclassifiedCount,
|
|
routes: catalog.counts,
|
|
verifiedUnits: inventory.coverage.verifiedUnits,
|
|
unverifiedUnits: inventory.coverage.unverifiedUnits,
|
|
sourceTreeSha256: inventory.authority.sourceTreeSha256,
|
|
},
|
|
webProduct: {
|
|
workflowModules: { resolved: 16, total: 16 },
|
|
features: { resolved: featureSmoke.summary.totalFeatures, total: featureSmoke.summary.totalFeatures, attempts: featureSmoke.summary.smokeAttempts, retries: featureSmoke.summary.retries },
|
|
internalSubmodules: { resolved: submoduleSmoke.summary.resolved, total: submoduleSmoke.summary.totalSubmodules, attempts: submoduleSmoke.summary.smokeAttempts, retries: submoduleSmoke.summary.retries },
|
|
unit: { collected: 429, initialPassed: 428, initialFailed: 1, focusedPassed: 1, resolved: 429 },
|
|
chromium: { collected: 350, initialPassed: 336, initialFailed: 13, batchHarnessSkipped: 1, focusedPassed: 13, resolvedProductTests: 349, soakMinutes: 30, soakCycles: 372, soakReopens: 31, soakFinalRevision: 745 },
|
|
v1Acceptance: { families: acceptance.summary.families, declarations: acceptance.summary.declarations, unique: acceptance.summary.unique, passed: acceptance.summary.passed, failed: acceptance.summary.failed },
|
|
},
|
|
blenderReference: {
|
|
version: acceptance.environment.blender,
|
|
executableSha256: acceptance.environment.blenderExecutableSha256,
|
|
archiveSha256: acceptance.environment.blenderArchiveSha256,
|
|
source: "Blender 5.2.0 Linux x64 release archive; publisher SHA-256 verified",
|
|
},
|
|
release: {
|
|
origin: process.env.WEB_ORIGIN ?? "https://49.232.250.35:58014",
|
|
deployed: "PASS",
|
|
webEngineWasmSha256: sourceSync.artifacts.wasmSha256,
|
|
ffmpegCoreWasmSha256: sha256(path.join(root, "web/app/public/vendor/ffmpeg/ffmpeg-core.wasm")),
|
|
offlineBinary: artifact("release/blender-web-offline.tar.gz"),
|
|
correspondingSource: artifact("release/blender-web-corresponding-source.tar.gz"),
|
|
},
|
|
resolvedFindings: [
|
|
"batch-only modifier spec no longer aborts unrelated Playwright collection",
|
|
"operator palette restores keyboard Enter execution, focus semantics and current accessible names",
|
|
"timeline controls meet serious contrast and target-size accessibility rules",
|
|
"network save/reopen preserves four objects and uses the reopened Main revision",
|
|
"native open cancellation uses a bounded large valid fixture and releases all resources",
|
|
"pthread fallback ignores duplicate background preload observations while preserving one unique variant",
|
|
"script Text line metadata is bound into the save/reopen golden",
|
|
"N-023 library and local IO assertions match the current executable route matrix",
|
|
"ffmpeg.wasm fallback is wired through probe, proxy cache and lifecycle recovery",
|
|
"VDB server verification keeps bwrap isolation when unprivileged user namespaces are restricted",
|
|
"dynamic source inventory and route catalog were refreshed from 3745 to 3802 units",
|
|
"offline source offer includes the content-addressed ffmpeg.wasm upstream source archive",
|
|
],
|
|
remaining: {
|
|
status: sourceParityStatus,
|
|
reason: `${inventory.coverage.unverifiedUnits} source units have classified routes but no exact source-backed behavioral receipt`,
|
|
forbiddenClaim: "route classification and representative product tests do not establish full Blender source parity",
|
|
},
|
|
evidence: {
|
|
flow: artifact("docs/WEB_SOURCE_PARITY_FULL_VALIDATION.md"),
|
|
dynamicInventory: artifact("tests/golden/WBV2/dynamic-inventory.json"),
|
|
routeCatalog: artifact("web/app/src/capabilities/blender-feature-route-catalog.json"),
|
|
featureSmoke: artifact("tests/golden/WBV2/feature-smoke-manifest.json"),
|
|
submoduleSmoke: artifact("tests/golden/WBV2/internal-submodule-smoke-manifest.json"),
|
|
v1Acceptance: artifact("docs/status/v1-acceptance-evidence.json"),
|
|
releaseEvidence: artifact("docs/status/release-evidence.json"),
|
|
focusedE2E: artifact("tests/golden/WBV2/full-source-parity-validation-logs/29-focused-e2e-fixes.log"),
|
|
focusedWorker: artifact("tests/golden/WBV2/full-source-parity-validation-logs/30-focused-engine-worker-recovery.log"),
|
|
initialUnit: artifact("tests/golden/WBV2/full-source-parity-validation-logs/31-final-unit.log"),
|
|
focusedUnit: artifact("tests/golden/WBV2/full-source-parity-validation-logs/32-focused-unit-control-plane.log"),
|
|
deployedSite: artifact("tests/golden/WBV2/full-source-parity-validation-logs/34-final-deployed-site.log"),
|
|
},
|
|
};
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`web-source-parity-final sourceParity=${report.sourceParityStatus} webProduct=${report.webProductStatus} release=${report.releaseStatus} units=${report.inventory.verifiedUnits}/${report.inventory.unitCount} chromium=${report.webProduct.chromium.resolvedProductTests} unit=${report.webProduct.unit.resolved} v1=${report.webProduct.v1Acceptance.passed}\n`);
|