验证ToolDb浏览器证据

结论:已新增Tool DB browser smoke,验证protocol transcript、OPFS flat-file/transcript persistence和diagnostics guard,baseline保持28/28/131/0且promotion继续锁定。
This commit is contained in:
2026-06-19 06:39:23 +08:00
parent d8e0196c61
commit e559dfa2a2
2 changed files with 189 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>LinuxCNC Tool DB Process Browser Smoke</title>
</head>
<body>
<pre id="status">running</pre>
<script type="module">
import {
createLinuxCncToolDbProcessPort,
createToolDbProcessDiagnostics,
createToolDbStorePaths,
loadToolDbFile,
loadToolDbTranscript,
saveToolDbFile,
saveToolDbTranscript,
validateToolDbTranscript,
} from "../../runtime/sdk/src/index.js";
const status = document.getElementById("status");
function assertEqual(actual, expected, label) {
if (actual !== expected) {
throw new Error(`${label}: expected ${expected}, got ${actual}`);
}
}
function assertMatch(value, pattern, label) {
if (!pattern.test(value)) {
throw new Error(`${label}: ${value}`);
}
}
try {
const machineId = "browser-tool-db-gate";
const runId = "browser-tool-db-run";
const paths = createToolDbStorePaths(machineId, { runId });
assertEqual(paths.dbFile, "linuxcnc/machines/browser-tool-db-gate/tool-db/db_nonran_file", "DB file path");
assertEqual(
paths.transcript,
"linuxcnc/machines/browser-tool-db-gate/tool-db/transcripts/browser-tool-db-run/transcript.json",
"transcript path",
);
const port = createLinuxCncToolDbProcessPort({
dbProgramPath: "./db_nonran.py",
sourceFiles: [
"configs/sim/axis/db_demo/db_nonran.py",
"configs/sim/axis/db_demo/db.py",
"lib/python/tooldb.py",
],
runtimeMode: "contract-only",
});
const startResult = await port.start();
assertEqual(startResult.runtimeExecutionReady, false, "contract-only runtime execution");
const planResult = await port.runTransactionPlan();
assertEqual(planResult.status, "blocked_runtime_adapter_required", "contract-only plan status");
assertEqual(planResult.tblFallbackSufficient, false, "tbl fallback guard");
const transcript = [
{ direction: "read", line: "v2.1" },
{ direction: "write", line: "g" },
{ direction: "read", line: "T10 P110 D0.10 Z0.10" },
{ direction: "read", line: "FINI (get_cmd)" },
{ direction: "write", line: "p t11 p111 d0.33 z0.11" },
{ direction: "write", line: "l t14 p0" },
{ direction: "write", line: "u t0 p0" },
];
assertEqual(validateToolDbTranscript(transcript).ready, true, "transcript readiness");
const dbFileText = "T11 P111 D0.33 Z0.11\nT14 P114 D0.14 Z0.14\n";
const savedDb = await saveToolDbFile(machineId, dbFileText);
const savedTranscript = await saveToolDbTranscript(machineId, runId, transcript);
assertEqual(savedDb.displayPath, "/machines/browser-tool-db-gate/tool-db/db_nonran_file", "display DB path");
assertEqual(
savedTranscript.displayPath,
"/machines/browser-tool-db-gate/tool-db/transcripts/browser-tool-db-run/transcript.json",
"display transcript path",
);
assertMatch(savedDb.dbFileHash, /^[0-9a-f]{8}$/, "DB hash");
assertMatch(savedTranscript.transcriptHash, /^[0-9a-f]{8}$/, "transcript hash");
const loadedDb = await loadToolDbFile(machineId);
const loadedTranscript = await loadToolDbTranscript(machineId, runId);
assertEqual(loadedDb.text, dbFileText, "loaded DB file");
assertEqual(loadedTranscript.payload.schema, "linuxcnc.toolDbProcessTranscript.v1", "transcript schema");
assertEqual(loadedTranscript.payload.transcript.length, transcript.length, "loaded transcript length");
const diagnostics = createToolDbProcessDiagnostics({
dbProgramPath: "./db_nonran.py",
opfsDbPath: savedDb.displayPath,
transcript,
dbFileText,
startupToolCount: 10,
mutationCount: 3,
runtimeMode: "wasm-tool-db-protocol-worker",
});
assertEqual(diagnostics.runtimeMode, "wasm-tool-db-protocol-worker", "diagnostics runtime mode");
assertEqual(diagnostics.dbProgramPath, "./db_nonran.py", "diagnostics DB_PROGRAM");
assertEqual(diagnostics.protocolTranscriptReady, true, "diagnostics transcript");
assertEqual(diagnostics.opfsPersistenceReady, true, "diagnostics OPFS");
assertEqual(diagnostics.tblFallbackSufficient, false, "diagnostics tbl fallback guard");
assertEqual(diagnostics.runtimeExecutionReady, false, "diagnostics runtime execution");
assertEqual(diagnostics.executionEnabled, false, "diagnostics execution");
assertEqual(diagnostics.promotionAllowed, false, "diagnostics promotion");
status.textContent = "browser_tool_db_process_smoke=ok";
} catch (error) {
status.textContent = `browser_tool_db_process_smoke=fail\n${error.stack || error}`;
throw error;
}
</script>
</body>
</html>

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
CHROMIUM="${CHROMIUM:-$(command -v chromium || command -v chromium-browser || command -v google-chrome || command -v google-chrome-stable || true)}"
if [[ -z "$CHROMIUM" ]]; then
echo "missing Chromium-compatible browser; set CHROMIUM=/path/to/browser" >&2
exit 1
fi
TMP_DIR="$(mktemp -d)"
PORT_FILE="$TMP_DIR/port"
SERVER_LOG="$TMP_DIR/server.log"
CHROME_PROFILE="$TMP_DIR/chrome-profile"
mkdir -p "$CHROME_PROFILE"
cleanup() {
if [[ -n "${SERVER_PID:-}" ]]; then
kill "$SERVER_PID" 2>/dev/null || true
wait "$SERVER_PID" 2>/dev/null || true
fi
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
python3 - <<'PY' "$ROOT_DIR" "$PORT_FILE" >"$SERVER_LOG" 2>&1 &
import functools
import http.server
import pathlib
import socketserver
import sys
root = pathlib.Path(sys.argv[1])
port_file = pathlib.Path(sys.argv[2])
handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=str(root))
with socketserver.TCPServer(("127.0.0.1", 0), handler) as httpd:
port_file.write_text(str(httpd.server_address[1]), encoding="ascii")
httpd.serve_forever()
PY
SERVER_PID=$!
for _ in $(seq 1 100); do
[[ -s "$PORT_FILE" ]] && break
sleep 0.05
done
if [[ ! -s "$PORT_FILE" ]]; then
echo "Tool DB process browser smoke HTTP server did not start" >&2
cat "$SERVER_LOG" >&2 || true
exit 1
fi
PORT="$(cat "$PORT_FILE")"
URL="http://127.0.0.1:$PORT/tests/browser/tool_db_process_browser_smoke.html"
OUT="$TMP_DIR/chromium.out"
"$CHROMIUM" \
--headless=new \
--disable-gpu \
--no-sandbox \
--user-data-dir="$CHROME_PROFILE" \
--virtual-time-budget=10000 \
--dump-dom \
"$URL" >"$OUT" 2>&1
if ! grep -Fq "browser_tool_db_process_smoke=ok" "$OUT"; then
echo "Tool DB process browser smoke failed" >&2
sed -n '1,220p' "$OUT" >&2
exit 1
fi
echo "browser_tool_db_process_smoke=ok"