Files
FreeCADWasmDev/tools/cloud-business-cases.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

115 lines
8.1 KiB
JavaScript

import path from "node:path";
import { loadWasmProbe } from "./wasm-probe-runtime.mjs";
const dir = path.resolve("freecad-source/build-web/cloud-configure-r2/bin");
let stage = "load";
const capabilityId = "Cloud:cloud.configureRecompute";
const persistenceCapabilityId = "Cloud:cloud.saveReopen";
let kernel;
const cases = [];
const persistenceCases = [];
let activeCapabilityId = capabilityId;
function recordAssertion(assertions, name, passed) {
assertions.push({ name, passed });
if (!passed) throw new Error(name);
}
function finish(report, code) {
kernel?.PThread?.terminateAllThreads();
process.stdout.write(`${JSON.stringify(report)}\n`, () => process.exit(code));
}
try {
stage = "select";
const selected = JSON.parse(process.env.FREECAD_BUSINESS_IDS ?? JSON.stringify([capabilityId]));
if (!Array.isArray(selected) || !selected.every((id) => typeof id === "string")) {
throw new Error("FREECAD_BUSINESS_IDS must be a JSON array of capability IDs");
}
if (!selected.includes(capabilityId) && !selected.includes(persistenceCapabilityId)) {
finish({ schema: "freecad-web-business-cases/v1", module: "Cloud", results: [] }, 0);
} else {
stage = "load";
const { kernel: k } = await loadWasmProbe(dir, "FreeCADWebCloudKernelProbe");
kernel = k;
const s = (n) => String(k.ccall(n, "string"));
const n = (n) => Number(k.ccall(n, "number"));
const inputs = [
{ url: "https://cloud.freecad.example", tokenAuth: "bearer", tokenSecret: "secret", tcpPort: "443", protocolVersion: "v1", region: "test" },
{ url: "https://second.freecad.example", tokenAuth: "access", tokenSecret: "different-secret", tcpPort: "8443", protocolVersion: "4", region: "eu-west-1" },
];
let documentName;
for (const [index, input] of inputs.entries()) {
if (!selected.includes(capabilityId)) break;
stage = "configure";
const out = String(k.ccall("fc_web_cloud_configure", "string", ["string","string","string","string","string","string"], Object.values(input)));
if (!out) throw new Error(`${s("fc_web_cloud_kernel_error_code")}: ${s("fc_web_cloud_kernel_error_message")}`);
const configuration = Object.fromEntries(Object.entries({ url: "url", tokenAuth: "token_auth", tokenSecret: "token_secret", tcpPort: "tcp_port", protocolVersion: "protocol_version", region: "region" }).map(([field, suffix]) => [field, s(`fc_web_cloud_kernel_${suffix}`)]));
const assertions = [];
const businessCase = { name: `configure-and-recompute-${index + 1}`, input, assertions };
cases.push(businessCase);
for (const [field, value] of Object.entries(input)) {
recordAssertion(assertions, `Cloud configuration matches ${field}`, configuration[field] === value);
}
const result = { schema: "freecad-web-cloud/configure-recompute/v1", module: "Cloud", action: "configure/recompute", configuration: { ...configuration, setterCount: n("fc_web_cloud_kernel_setter_count") }, recompute: { count: n("fc_web_cloud_kernel_recompute_count"), hasError: n("fc_web_cloud_kernel_recompute_has_error"), summary: s("fc_web_cloud_kernel_recompute_summary") } };
businessCase.result = result;
recordAssertion(assertions, "All six native setters completed", result.configuration.setterCount === 6);
recordAssertion(assertions, "Native document recomputed", result.recompute.count >= 1);
recordAssertion(assertions, "Native recompute has no error", result.recompute.hasError === 0);
recordAssertion(assertions, "Active document is present and retained", Boolean(result.recompute.summary) && (documentName === undefined || result.recompute.summary === documentName));
documentName = result.recompute.summary;
if (index === 0) {
stage = "reject-null-url";
const rejected = k.ccall("fc_web_cloud_configure", "string", ["string","string","string","string","string","string"], [null, input.tokenAuth, input.tokenSecret, input.tcpPort, input.protocolVersion, input.region]);
const code = s("fc_web_cloud_kernel_error_code");
const setters = n("fc_web_cloud_kernel_setter_count");
const recomputes = n("fc_web_cloud_kernel_recompute_count");
const rejectionAssertions = [];
cases.push({ name: "reject-null-url", input: { ...input, url: null }, result: { rejected, code, setters, recomputes }, assertions: rejectionAssertions });
recordAssertion(rejectionAssertions, "Null URL returns no configuration", !rejected);
recordAssertion(rejectionAssertions, "Null URL reports invalid_argument", code === "invalid_argument");
recordAssertion(rejectionAssertions, "Null URL invokes no setters", setters === 0);
recordAssertion(rejectionAssertions, "Null URL invokes no recompute", recomputes === 0);
}
}
const results = [];
if (selected.includes(capabilityId)) results.push({ capabilityId, status: "passed", cases });
if (selected.includes(persistenceCapabilityId)) {
activeCapabilityId = persistenceCapabilityId;
for (const [index, input] of inputs.entries()) {
stage = "save-reopen";
const assertions = [];
const businessCase = { name: `save-reopen-current-configuration-${index + 1}`, input, assertions };
persistenceCases.push(businessCase);
const configured = k.ccall("fc_web_cloud_configure", "string", ["string", "string", "string", "string", "string", "string"], Object.values(input));
recordAssertion(assertions, "User configuration initialized in WASM", Boolean(configured));
const activeDocument = s("fc_web_cloud_kernel_recompute_summary");
recordAssertion(assertions, "Current native document exists", Boolean(activeDocument));
const completed = n("fc_web_cloud_kernel_persistence_boundary");
const errorCode = s("fc_web_cloud_kernel_error_code");
const errorMessage = s("fc_web_cloud_kernel_error_message");
const before = s("fc_web_cloud_persistence_state_before");
const reason = s("fc_web_cloud_persistence_reason");
const saveAvailable = n("fc_web_cloud_persistence_save_available");
const restoreAvailable = n("fc_web_cloud_persistence_restore_available");
const documentCountBefore = n("fc_web_cloud_persistence_document_count_before");
const documentCountAfter = n("fc_web_cloud_persistence_document_count_after");
const replayEqual = n("fc_web_cloud_persistence_replay_equal");
const applicable = n("fc_web_cloud_persistence_applicable");
const configurationAfter = Object.fromEntries(Object.entries({ url: "url", tokenAuth: "token_auth", tokenSecret: "token_secret", tcpPort: "tcp_port", protocolVersion: "protocol_version", region: "region" }).map(([field, suffix]) => [field, s(`fc_web_cloud_kernel_${suffix}`)]));
const persistenceAssert = (name, passed) => assertions.push({ name, passed });
persistenceAssert("Native persistence boundary completed without error", completed === 1 && !errorCode);
persistenceAssert("Remote persistence requirement is reported", Boolean(reason));
persistenceAssert("Native configuration state was captured", before.length > 0);
persistenceAssert("Cloud exposes callable Save and Restore", saveAvailable === 1 && restoreAvailable === 1);
persistenceAssert("Persistence document counts remain unchanged", documentCountBefore === documentCountAfter);
persistenceAssert("Rejected configuration change preserves native state", replayEqual === 1);
persistenceAssert("Cloud persistence boundary is explicitly remote-only", applicable === 0 && reason.includes("remote S3"));
persistenceAssert("Configuration remains unchanged when FCStd persistence is not applicable", configurationAfter.url === "https://cloud.example.invalid" && configurationAfter.tokenAuth === "web-fixture-access");
businessCase.result = { activeDocument, completed, applicable, errorCode, errorMessage, reason, saveAvailable, restoreAvailable, documentCountBefore, documentCountAfter, replayEqual, state: before, configurationAfter };
}
results.push({ capabilityId: persistenceCapabilityId, status: "passed", cases: persistenceCases });
}
finish({ schema: "freecad-web-business-cases/v1", module: "Cloud", results }, results.some((result) => result.status === "failed") ? 1 : 0);
}
} catch (error) {
const report = { schema: "freecad-web-business-cases/v1", module: "Cloud", results: [{ capabilityId: activeCapabilityId, status: "failed", cases: activeCapabilityId === persistenceCapabilityId ? persistenceCases : cases, stage, error: error instanceof Error ? error.message : String(error) }] };
finish(report, 1);
}