Files
FreeCADWasmDev/tools/run_p6_document_contract_gate.mjs
wangdequan 9a7c4f0335 Save FreeCAD Web workspace sources and user validation evidence
IFC native binding remains incomplete; preserve accurate test results.
2026-09-15 22:01:58 +08:00

58 lines
3.9 KiB
JavaScript

#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const docs = path.join(root, "docs");
const p6 = path.join(docs, "p6");
const read = (file) => JSON.parse(fs.readFileSync(path.join(p6, file), "utf8"));
const hash = (file) => crypto.createHash("sha256").update(fs.readFileSync(path.join(p6, file))).digest("hex");
const chrome = read("document-contract-chrome.json");
const cases = Object.fromEntries(chrome.cases.map((item) => [item.id, item]));
const required = ["document-ready", "document-action", "document-duplicate", "document-expected-error", "document-cancel", "document-reset", "document-repeat"];
const resultState = cases["document-action"]?.state?.response?.result;
const repeatState = cases["document-repeat"]?.state?.response?.result;
const contracts = Object.fromEntries(required.map((id) => [id, {
status: cases[id]?.status ?? "missing",
stage: cases[id]?.state?.stage ?? "missing",
responseKind: cases[id]?.state?.response?.kind ?? null,
error: cases[id]?.state?.response?.error?.code ?? null,
progressEvents: cases[id]?.state?.progress?.length ?? 0,
}]));
const result = chrome.result === "pass"
&& required.every((id) => cases[id]?.status === "pass")
&& cases["document-action"]?.state?.response?.kind === "result"
&& cases["document-action"]?.state?.progress?.length === 5
&& cases["document-duplicate"]?.state?.response?.error?.code === "duplicate_request"
&& cases["document-cancel"]?.state?.response?.error?.code === "cancelled"
&& resultState?.semanticRoundTrip === true
&& repeatState?.semanticRoundTrip === true
&& resultState?.recovery?.invalidError === "Length of box too small"
&& resultState?.recovery?.hasError === 0
&& resultState?.opened?.objectType === "Part::Box"
&& resultState?.opened?.dimensions?.join(",") === "2,3,4"
&& resultState?.transaction?.undoResult === 1
&& resultState?.transaction?.redoResult === 1
&& resultState?.resourcesReleased === true
&& chrome.cases.flatMap((item) => item.externalRequests ?? []).length === 0
&& chrome.cases.flatMap((item) => item.exceptions ?? []).length === 0;
const report = {
schema: "freecad-web-p6-document-contract-gate/v1",
taskId: "FW-P6-FPA-C",
result: result ? "pass" : "fail",
contracts,
semantic: { action: resultState ?? null, repeat: repeatState ?? null, compares: ["objectType", "dimensions", "volume", "recovery", "transaction"] },
chrome: { artifact: "docs/p6/document-contract-chrome.json", sha256: hash("document-contract-chrome.json"), screenshot: chrome.evidence?.screenshot ?? null, screenshotSha256: chrome.evidence?.screenshotSha256 ?? null, browser: chrome.chrome?.version ?? null, externalRequests: chrome.cases.flatMap((item) => item.externalRequests ?? []).length, exceptions: chrome.cases.flatMap((item) => item.exceptions ?? []).length },
boundaries: [
"C proves one native Part::Box document semantic round-trip and transaction/recovery contract; it does not close the 403 object inventory.",
"FCStd acceptance compares semantic fields, not archive bytes.",
"Cross-module links and larger object graphs remain explicit D/C follow-up work.",
],
};
fs.writeFileSync(path.join(p6, "document-contract-gate.json"), `${JSON.stringify(report, null, 2)}\n`);
fs.writeFileSync(path.join(docs, "freecad-p6-document-contract-gate.log"), `${JSON.stringify({ result: report.result, cases: required.length, semanticRoundTrip: resultState?.semanticRoundTrip ?? false, externalRequests: report.chrome.externalRequests, exceptions: report.chrome.exceptions })}\n`);
console.log(JSON.stringify({ result: report.result, cases: required.length, semanticRoundTrip: resultState?.semanticRoundTrip ?? false, externalRequests: report.chrome.externalRequests, exceptions: report.chrome.exceptions }));
if (!result) process.exitCode = 1;