mirror of
https://github.com/openclaw/openclaw.git
synced 2026-09-28 14:12:28 +08:00
* fix(release): consolidate frozen target harness contracts
Keep current targets strict, source-qualify the frozen plugin fixture dialect, and omit only the mobile reconnect lane when the selected Gateway lacks its required iPhone watch-relay capability.
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: Dallin Romney <dallinromney@gmail.com>
* fix(release): make frozen capability probes pipefail safe
* fix(release): restore frozen core harness dialects
* fix(release): default frozen code-mode catalog mode
* fix(release): bind frozen identity for docker chunks
* fix(release): derive frozen live package mode
* fix(release): materialize frozen bundle MCP client
* fix(release): use frozen typed onboarding contract
* fix(release): make frozen client mount traversable
* fix(release): preserve frozen client module scope
* fix(release): select frozen runtime-context input contract
* fix(release): qualify frozen MCP permission ownership
* fix(release): retain failed Docker harness diagnostics
* test(release): preserve harness cleanup assertion
* Revert "fix(release): qualify frozen MCP permission ownership"
This reverts commit 10028a0837.
---------
Co-authored-by: Dallin Romney <dallinromney@gmail.com>
171 lines
5.5 KiB
TypeScript
171 lines
5.5 KiB
TypeScript
// Docker E2E Observability tests cover docker e2e observability script behavior.
|
|
import { spawnSync } from "node:child_process";
|
|
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
|
|
|
|
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
|
|
|
|
function successTail(scriptPath: string): string {
|
|
const script = readFileSync(scriptPath, "utf8");
|
|
const index = script.lastIndexOf('if [ "$status" -ne 0 ]; then');
|
|
if (index === -1) {
|
|
throw new Error(`missing status tail in ${scriptPath}`);
|
|
}
|
|
return script.slice(index);
|
|
}
|
|
|
|
function runSuccessTail(scriptPath: string) {
|
|
const tempDir = tempDirs.make("openclaw-docker-e2e-observability-");
|
|
const clientLog = path.join(tempDir, "client.log");
|
|
writeFileSync(clientLog, "client proof log\n", "utf8");
|
|
const harness = [
|
|
"set -euo pipefail",
|
|
`CLIENT_LOG=${JSON.stringify(clientLog)}`,
|
|
"status=0",
|
|
"docker_e2e_print_log() {",
|
|
' printf \'LOG:%s\\n\' "$(cat "$1")"',
|
|
"}",
|
|
successTail(scriptPath),
|
|
].join("\n");
|
|
|
|
return spawnSync("bash", ["-c", harness], { encoding: "utf8" });
|
|
}
|
|
|
|
describe("Docker E2E observability", () => {
|
|
it("prints the bounded heartbeat log before signal cleanup", () => {
|
|
const tempDir = tempDirs.make("openclaw-heartbeat-signal-log-");
|
|
const result = spawnSync(
|
|
"bash",
|
|
[
|
|
"-c",
|
|
`
|
|
set -euo pipefail
|
|
source scripts/lib/docker-e2e-logs.sh
|
|
run_logged_print_heartbeat signal-proof 30 bash -c 'printf "old log head%0256drecent failure tail\\n" 0; kill -TERM "$PPID"'
|
|
`,
|
|
],
|
|
{
|
|
encoding: "utf8",
|
|
timeout: 5_000,
|
|
env: { ...process.env, TMPDIR: tempDir, OPENCLAW_DOCKER_E2E_LOG_PRINT_BYTES: "64" },
|
|
},
|
|
);
|
|
expect(result.status, result.stderr).toBe(143);
|
|
expect(result.stdout).not.toContain("old log head");
|
|
expect(result.stdout.match(/recent failure tail/g)).toHaveLength(1);
|
|
expect(result.stdout).toContain("showing last 64");
|
|
expect(readdirSync(tempDir)).toEqual([]);
|
|
});
|
|
|
|
it.each([
|
|
[0, "", true],
|
|
[1, "", true],
|
|
[143, "TERM", false],
|
|
[143, "TERM", true],
|
|
[130, "INT", false],
|
|
[129, "HUP", false],
|
|
] as const)(
|
|
"preserves redirected Codex run diagnostics on exit %i (%s, long=%s)",
|
|
(status, signal, long) => {
|
|
const tempDir = tempDirs.make("openclaw-codex-run-cleanup-");
|
|
const script = readFileSync("scripts/e2e/codex-npm-plugin-live-docker.sh", "utf8");
|
|
const cleanupSetup = script.slice(
|
|
script.indexOf('run_log=""'),
|
|
script.indexOf("trap cleanup EXIT") + "trap cleanup EXIT".length,
|
|
);
|
|
const result = spawnSync(
|
|
"bash",
|
|
[
|
|
"-c",
|
|
`
|
|
set -Eeuo pipefail
|
|
# Bound the pre-fix self-copy if EXIT cleanup still writes into its input log.
|
|
ulimit -f 8
|
|
source scripts/lib/docker-e2e-package.sh
|
|
proof_status="$1"
|
|
proof_signal="$2"
|
|
proof_long="$3"
|
|
docker_e2e_docker_cmd() {
|
|
printf '%s\\n' "$*" >>"$TMPDIR/docker-cleanup"
|
|
}
|
|
docker_e2e_docker_run_cmd() {
|
|
while [ "$#" -gt 0 ]; do
|
|
if [ "$1" = --cidfile ]; then
|
|
printf 'proof-container\\n' >"$2"
|
|
break
|
|
fi
|
|
shift
|
|
done
|
|
cat >"$TMPDIR/container-stdin"
|
|
if [ "$proof_long" = true ]; then
|
|
printf 'old log head%0256d' 0
|
|
fi
|
|
printf 'recent failure tail\\n'
|
|
if [ -n "$proof_signal" ]; then
|
|
kill -"$proof_signal" "$$"
|
|
else
|
|
return "$proof_status"
|
|
fi
|
|
}
|
|
${cleanupSetup}
|
|
run_log="$TMPDIR/run.log"
|
|
# Use the actual harness: its signal trap exits inside this function redirection.
|
|
if ! docker_e2e_run_with_harness image-name bash -s >"$run_log" 2>&1 <<'SH'; then
|
|
container stdin proof
|
|
SH
|
|
exit 1
|
|
fi
|
|
`,
|
|
"bash",
|
|
String(status),
|
|
signal,
|
|
String(long),
|
|
],
|
|
{
|
|
encoding: "utf8",
|
|
timeout: 5_000,
|
|
killSignal: "SIGKILL",
|
|
env: { ...process.env, TMPDIR: tempDir, OPENCLAW_DOCKER_E2E_LOG_PRINT_BYTES: "64" },
|
|
},
|
|
);
|
|
expect(result.status, JSON.stringify({ stderr: result.stderr, signal: result.signal })).toBe(
|
|
status,
|
|
);
|
|
expect(readFileSync(path.join(tempDir, "container-stdin"), "utf8")).toBe(
|
|
"container stdin proof\n",
|
|
);
|
|
const dockerCommands = readFileSync(path.join(tempDir, "docker-cleanup"), "utf8");
|
|
expect(dockerCommands.trimEnd().split("\n").at(-1)).toBe("rm -f proof-container");
|
|
expect(readdirSync(tempDir).sort()).toEqual(["container-stdin", "docker-cleanup"]);
|
|
expect(result.stdout).not.toContain("old log head");
|
|
if (status === 0) {
|
|
expect(result.stdout).toBe("");
|
|
} else {
|
|
expect(result.stdout.match(/recent failure tail/g)).toHaveLength(1);
|
|
expect(result.stdout.includes("showing last 64")).toBe(long);
|
|
}
|
|
},
|
|
);
|
|
|
|
it("feeds the cron CLI Docker proof body through container stdin", () => {
|
|
const script = readFileSync("scripts/e2e/cron-cli-docker.sh", "utf8");
|
|
|
|
expect(script).toMatch(
|
|
/docker_e2e_run_with_harness[\s\S]*\n {2}-i \\\n {2}"\$IMAGE_NAME" \\\n {2}bash -s >"\$CLIENT_LOG" 2>&1 <<'INNER'/u,
|
|
);
|
|
});
|
|
|
|
it.each([
|
|
"scripts/e2e/mcp-channels-docker.sh",
|
|
"scripts/e2e/cron-cli-docker.sh",
|
|
"scripts/e2e/cron-mcp-cleanup-docker.sh",
|
|
])("prints successful MCP client proof logs from %s", (scriptPath) => {
|
|
const result = runSuccessTail(scriptPath);
|
|
|
|
expect(result.status, result.stderr).toBe(0);
|
|
expect(result.stdout.trim().split("\n")).toEqual(["LOG:client proof log", "OK"]);
|
|
});
|
|
});
|