Files
openclaw/test/scripts/run-with-lease-fence.test.ts
Ayaan Zaidi 10c774cf38 feat(mantis): keep QA leases alive and stage fixture plugins (#127804)
* feat(mantis): keep leases alive and stage fixture plugins

* refactor(mantis): deduplicate lease keepalive guard

* fix(mantis): retry keepalive on unexpected broker errors

* fix(mantis): fence active proof on terminal lease loss
2026-08-22 14:09:28 +05:30

76 lines
2.5 KiB
TypeScript

import { spawn, spawnSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { waitForChildClose, waitForFile, waitForPidFile } from "../helpers/process-wait.js";
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
const SCRIPT = "scripts/mantis/run-with-lease-fence.sh";
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
const posixIt = process.platform === "win32" ? it.skip : it;
describe("run-with-lease-fence", () => {
posixIt(
"stops the active process group after terminal lease loss",
async () => {
const root = tempDirs.make("openclaw-lease-fence-");
const lostMarker = path.join(root, "lease.lost");
const commandPidFile = path.join(root, "command.pid");
const ticksFile = path.join(root, "ticks.log");
let stderr = "";
const child = spawn(
SCRIPT,
[
lostMarker,
"--",
"/bin/bash",
"-c",
'trap "exit 0" TERM; printf "%s\\n" "$$" >"$1"; while :; do printf "tick\\n" >>"$2"; sleep 1; done',
"lease-fence-command",
commandPidFile,
ticksFile,
],
{ stdio: ["ignore", "ignore", "pipe"] },
);
child.stderr.setEncoding("utf8");
child.stderr.on("data", (chunk: string) => {
stderr += chunk;
});
const commandPid = await waitForPidFile(commandPidFile, 2_000);
await waitForFile(ticksFile, 2_000);
fs.writeFileSync(lostMarker, "lost\n");
try {
await expect(waitForChildClose(child, 12_000)).resolves.toEqual({
code: 97,
signal: null,
});
const ticksAfterFence = fs.readFileSync(ticksFile, "utf8");
await new Promise((resolve) => setTimeout(resolve, 1_200));
expect(fs.readFileSync(ticksFile, "utf8")).toBe(ticksAfterFence);
expect(stderr).toContain("::error::Telegram QA lease lost mid-run; fencing proof");
} finally {
if (child.exitCode === null) {
child.kill("SIGKILL");
}
try {
process.kill(-commandPid, "SIGKILL");
} catch {}
}
},
20_000,
);
posixIt("propagates a clean command exit", () => {
const root = tempDirs.make("openclaw-lease-fence-clean-");
const result = spawnSync(SCRIPT, [path.join(root, "lease.lost"), "--", "/bin/true"], {
encoding: "utf8",
});
expect(result.status, result.stderr).toBe(0);
expect(result.stderr).toBe("");
});
});