Files
openclaw/test/scripts/bench-cli-startup.test.ts
Peter Steinberger 7b68c81b60 test(core,plugins,tooling): remove low-value tests (batch d011) (#158811)
* test(agents): deslop s035 tests

* test(scripts): deslop s030 tests

* test(browser): deslop s039 tests

* test(gateway): deslop s034 tests

* test(copilot): deslop s040 tests

* test(auto-reply): deslop s042 tests

* test(agents): deslop s041 tests

* test(scripts): deslop s022 tests

* test(sessions): deslop s036 tests

* test(cli): deslop s037 tests

* test: adapt d011 fixtures to current owners

* test: preserve current native i18n guidance

* test: preserve d011 authorization coverage and diagnostic assertions

* test: retain fixture imports used by newer main tests

* test: fix lint in d011 test reductions

* test(copilot): remove obsolete max-lines suppression
2026-09-26 14:41:59 +00:00

912 lines
33 KiB
TypeScript

// Bench Cli Startup tests cover bench cli startup script behavior.
import { spawnSync } from "node:child_process";
import { existsSync, mkdirSync, readFileSync, symlinkSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { afterEach, describe, expect, it } from "vitest";
import { testing } from "../../scripts/bench-cli-startup.ts";
import { forceKillVitestProcessGroup } from "../../scripts/vitest-process-group.mts";
import {
resolveRuntimeWorkerArgv,
resolveRuntimeWorkerUrl,
} from "../../src/infra/runtime-worker-url.js";
import { withEnv } from "../../src/test-utils/env.js";
import { resolveTestNodeExecPath } from "../../src/test-utils/node-process.js";
import { isProcessAlive, waitForDead } from "../helpers/process-wait.js";
import { createTempDirTracker, useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
import { toolingTsEntrypoints } from "./tooling-ts-runtime.test-support.js";
const repoRoot = join(__dirname, "../..");
const testNodeExecPath = resolveTestNodeExecPath();
const benchmarkUrl = resolveRuntimeWorkerUrl(toolingTsEntrypoints.benchCli);
const benchmarkArgs = resolveRuntimeWorkerArgv(benchmarkUrl, testNodeExecPath);
type SuiteResult = Parameters<typeof testing.collectFailedSamples>[0];
type CaseResult = SuiteResult["cases"][number];
type CliSample = CaseResult["samples"][number];
function stats(value: number) {
return { avg: value, p50: value, p95: value, min: value, max: value };
}
function cliSample(overrides: Partial<CliSample> = {}): CliSample {
return { ms: 10, firstOutputMs: 5, maxRssMb: 50, exitCode: 0, signal: null, ...overrides };
}
function suiteResult(
{
summary,
...overrides
}: Partial<Omit<CaseResult, "summary">> & {
summary?: Partial<CaseResult["summary"]>;
} = {},
entry = "openclaw.mjs",
): SuiteResult {
return {
entry,
cases: [
{
id: "version",
name: "--version",
args: ["--version"],
contract: null,
samples: [cliSample()],
...overrides,
summary: {
sampleCount: overrides.samples?.length ?? 1,
durationMs: stats(10),
firstOutputMs: stats(5),
maxRssMb: stats(50),
exitSummary: "code:0x1",
...summary,
},
},
],
};
}
function runBenchmarkCli(args: string[]) {
return spawnSync(testNodeExecPath, [...benchmarkArgs, ...args], {
cwd: repoRoot,
encoding: "utf8",
});
}
function runBenchmarkSample(entry: string, caseId: string, flags: string[] = []) {
return runBenchmarkCli([
"--entry",
entry,
"--case",
caseId,
"--runs",
"1",
"--warmup",
"0",
"--json",
...flags,
]);
}
function configFixture(id: string) {
return testing.buildConfigFixture({ id, name: id, args: [], presets: [] });
}
describe("bench-cli-startup", () => {
const memoryTempDirs = useAutoCleanupTempDirTracker(afterEach);
it("routes synthetic samples and their state through the explicit transport without runner environment", () => {
const tempDirs = createTempDirTracker();
const root = tempDirs.make("openclaw-cli-transport-");
try {
const prefix = join(root, "transport.mjs");
const entry = join(root, "entry.mjs");
const calls = join(root, "calls.jsonl");
const output = join(root, "report.json");
writeFileSync(
prefix,
`import assert from "node:assert/strict";
import fs from "node:fs";
import { spawnSync } from "node:child_process";
const args = process.argv.slice(2);
assert.equal(args.shift(), "/usr/bin/env");
assert.equal(args.shift(), "-C");
const cwd = args.shift();
assert.equal(cwd, ${JSON.stringify(root)});
assert.equal(args.shift(), "-i");
const env = {};
while (args[0]?.includes("=") && !args[0].startsWith("/")) {
const value = args.shift(), index = value.indexOf("=");
env[value.slice(0,index)] = value.slice(index+1);
}
fs.appendFileSync(${JSON.stringify(calls)}, JSON.stringify({args,env})+"\\n");
if (args[0] === "/usr/bin/timeout") {
assert.deepEqual(args.splice(0,4), ["/usr/bin/timeout","--signal=TERM","--kill-after=1s","5s"]);
}
const result = spawnSync(args[0],args.slice(1),{env,cwd,stdio:"inherit"});
process.exit(result.status ?? 99);
`,
);
writeFileSync(
entry,
`import assert from "node:assert/strict";
import fs from "node:fs";
assert.equal(process.env.SUT_FIXTURE,"yes");
assert.equal(process.env.RUNNER_PRIVATE_CANARY,undefined);
assert.equal(process.env.OPENCLAW_BENCH_TRANSPORT_JSON,undefined);
assert.equal(process.cwd(),${JSON.stringify(root)});
fs.writeFileSync(process.env.OPENCLAW_STATE_DIR+"/witness","sample");
console.log("fixture version");
`,
);
const result = spawnSync(
testNodeExecPath,
[
...benchmarkArgs,
"--entry",
entry,
"--case",
"version",
"--runs",
"1",
"--warmup",
"0",
"--timeout-ms",
"5000",
"--json",
"--output",
output,
],
{
cwd: resolve(__dirname, "../.."),
env: {
...process.env,
RUNNER_PRIVATE_CANARY: "must-not-forward",
OPENCLAW_BENCH_TRANSPORT_JSON: JSON.stringify({
prefix: [testNodeExecPath, prefix],
binary: testNodeExecPath,
env: { HOME: root, PATH: process.env.PATH, SUT_FIXTURE: "yes" },
}),
},
encoding: "utf8",
timeout: 15_000,
},
);
expect(result.status, result.stderr).toBe(0);
const report = JSON.parse(readFileSync(output, "utf8"));
expect(report.primary.executionMode).toBe("transport");
expect(report.primary.cases[0].samples).toMatchObject([{ exitCode: 0, signal: null }]);
const invocations = readFileSync(calls, "utf8")
.trim()
.split("\n")
.map((line) => JSON.parse(line));
expect(invocations).toHaveLength(4);
expect(invocations.filter((call) => call.args.includes("/usr/bin/timeout"))).toHaveLength(1);
expect(invocations.every((call) => call.env.RUNNER_PRIVATE_CANARY === undefined)).toBe(true);
} finally {
tempDirs.cleanup();
}
});
it.each(["{}", '{"prefix":["relative"],"binary":"/node","env":{}}'])(
"rejects malformed cross-user transport before candidate execution: %s",
(transport) => {
const result = spawnSync(testNodeExecPath, [...benchmarkArgs, "--entry", "/not-executed"], {
env: { ...process.env, OPENCLAW_BENCH_TRANSPORT_JSON: transport },
encoding: "utf8",
});
expect(result.status).toBe(1);
expect(result.stderr).toContain("Invalid benchmark transport");
expect(result.stdout).toBe("");
},
);
it("rejects transported runtime RSS before launching the SUT filesystem helper", () => {
const root = memoryTempDirs.make("openclaw-cli-rss-transport-");
const prefix = join(root, "transport.mjs");
const witness = join(root, "prefix-launched");
writeFileSync(
prefix,
`import fs from "node:fs";
fs.writeFileSync(${JSON.stringify(witness)}, "launched");
throw new Error("SUT prefix must not launch");`,
);
const result = spawnSync(
testNodeExecPath,
[...benchmarkArgs, "--runtime-rss", "--entry", join(root, "missing-entry.mjs")],
{
cwd: repoRoot,
env: {
...process.env,
OPENCLAW_BENCH_TRANSPORT_JSON: JSON.stringify({
prefix: [testNodeExecPath, prefix],
binary: testNodeExecPath,
env: { HOME: root, PATH: process.env.PATH },
}),
},
encoding: "utf8",
},
);
expect(result.status).toBe(1);
expect(existsSync(witness)).toBe(false);
expect(result.stderr.trim()).toBe("Cross-user runtime RSS sampling is not supported");
expect(result.stdout).toBe("");
});
it.each(["warning", "ca", "windows"])(
"preserves legacy RSS and opts into runtime RSS through the actual %s respawn plan",
(mode) => {
const tmpDir = memoryTempDirs.make("openclaw-cli-rss-respawn-");
const entryPath = join(tmpDir, "entry.mjs");
const caPath = join(tmpDir, "ca.pem");
const respawnUrl = resolveRuntimeWorkerUrl(toolingTsEntrypoints.respawn);
const respawnPreload = resolveRuntimeWorkerArgv(respawnUrl, testNodeExecPath).slice(0, -1);
writeFileSync(caPath, "");
writeFileSync(
entryPath,
`
import { Worker, isMainThread } from "node:worker_threads";
const usage = process.resourceUsage();
const runtime = process.env.FIXTURE_RUNTIME === "1";
process.resourceUsage = () => ({ ...usage, maxRSS: (runtime ? 32 : 64) * 1024 });
if (isMainThread && !runtime) {
${respawnPreload.length > 0 ? `await import(${JSON.stringify(respawnPreload[1])});` : ""}
const { buildCliRespawnPlan, runCliRespawnPlan } = await import(${JSON.stringify(respawnUrl.href)});
const plan = buildCliRespawnPlan({
platform: ${JSON.stringify(mode === "windows" ? "win32" : "linux")},
env: { ...process.env, OPENCLAW_NO_RESPAWN: "0", NODE_EXTRA_CA_CERTS: "",
OPENCLAW_NODE_OPTIONS_READY: ${JSON.stringify(mode === "ca" ? "1" : "")},
OPENCLAW_NODE_EXTRA_CA_CERTS_READY: "" },
autoNodeExtraCaCerts: ${JSON.stringify(mode === "ca" ? caPath : "")}
});
if (!plan) throw new Error("fixture must exercise a real respawn");
plan.env.FIXTURE_RUNTIME = "1";
runCliRespawnPlan(plan);
} else if (isMainThread) {
await new Promise((resolve, reject) => {
const worker = new Worker(new URL(import.meta.url));
worker.once("error", reject);
worker.once("exit", resolve);
});
console.log("runtime ready");
}
`,
);
for (const runtimeRss of [false, true]) {
const result = runBenchmarkSample(entryPath, "health", runtimeRss ? ["--runtime-rss"] : []);
expect(result.status, result.stderr).toBe(0);
const report = JSON.parse(result.stdout);
const sample = report.primary.cases[0].samples[0];
expect(sample.maxRssMb).toBe(runtimeRss ? 32 : 64);
if (!runtimeRss) {
expect(report.primary).not.toHaveProperty("memoryMetric");
expect(sample).not.toHaveProperty("memory");
continue;
}
expect(report.primary.memoryMetric).toBe("cli-runtime-max-rss-v1");
expect(sample.memory.processes).toHaveLength(2);
const runtime = sample.memory.processes.find(
(record: { role: string }) => record.role === "runtime",
);
const launcher = sample.memory.processes.find(
(record: { role: string }) => record.role === "launcher",
);
expect(runtime).toMatchObject({
pid: sample.memory.runtimePid,
parentPid: launcher.pid,
metricKind: "process-high-water-rss",
maxRssBytes: 32 * 1024 * 1024,
});
expect(launcher.maxRssBytes).toBe(64 * 1024 * 1024);
}
},
);
it("excludes silent-entry RSS telemetry from first output only with runtime RSS enabled", () => {
const tmpDir = memoryTempDirs.make("openclaw-cli-rss-silent-");
const entryPath = join(tmpDir, "entry.mjs");
writeFileSync(entryPath, "");
for (const runtimeRss of [false, true]) {
const result = runBenchmarkSample(entryPath, "version", runtimeRss ? ["--runtime-rss"] : []);
expect(result.status, result.stderr).toBe(0);
const report = JSON.parse(result.stdout);
expect(report.primary.executionMode).toBe("native");
const sample = report.primary.cases[0].samples[0];
expect(sample.maxRssMb).toBeGreaterThan(0);
if (runtimeRss) {
expect(sample.firstOutputMs).toBeNull();
} else {
expect(sample.firstOutputMs).toBeGreaterThan(0);
expect(sample).not.toHaveProperty("memory");
}
}
});
it("selects the same runtime when its launcher exits first", () => {
const tmpDir = memoryTempDirs.make("openclaw-cli-rss-parent-first-");
const entryPath = join(tmpDir, "entry.mjs");
writeFileSync(
entryPath,
`
import { fork } from "node:child_process";
const runtime = process.env.FIXTURE_RUNTIME === "1";
const usage = process.resourceUsage();
process.resourceUsage = () => ({ ...usage, maxRSS: (runtime ? 32 : 64) * 1024 });
if (runtime) {
process.once("disconnect", () => console.log("runtime ready"));
process.send("ready");
} else {
const child = fork(process.argv[1], process.argv.slice(2), {
env: { ...process.env, FIXTURE_RUNTIME: "1" },
stdio: ["ignore", "inherit", "inherit", "ipc"]
});
child.once("message", () => process.exit(0));
}
`,
);
const result = runBenchmarkSample(entryPath, "health", ["--runtime-rss"]);
expect(result.status, result.stderr).toBe(0);
const sample = JSON.parse(result.stdout).primary.cases[0].samples[0];
expect(sample.maxRssMb).toBe(32);
expect(
sample.memory.processes.map((record: { role: string }) => record.role).toSorted(),
).toEqual(["launcher", "runtime"]);
});
it.each(["missing", "ambiguous", "auxiliary", "unrecognized"])(
"handles %s descendant identity without falling back to the launcher",
(mode) => {
const tmpDir = memoryTempDirs.make("openclaw-cli-rss-identity-");
const entryPath = join(tmpDir, "entry.mjs");
const otherEntryPath = join(tmpDir, "other.mjs");
writeFileSync(otherEntryPath, 'console.log("other entry");');
writeFileSync(
entryPath,
`
import { fork } from "node:child_process";
const mode = ${JSON.stringify(mode)};
if (process.env.FIXTURE_RUNTIME === "1") {
if (mode === "missing") process.kill(process.pid, "SIGKILL");
else console.log("child ready");
} else {
await Promise.all(Array.from({ length: mode === "ambiguous" ? 2 : 1 }, () =>
new Promise((resolve, reject) => {
const args = [...process.argv.slice(2), ...(mode === "auxiliary" ? ["aux"] : [])];
const child = fork(mode === "unrecognized" ? ${JSON.stringify(otherEntryPath)} : process.argv[1], args, {
env: { ...process.env, FIXTURE_RUNTIME: "1" },
stdio: ["ignore", "inherit", "inherit", "ipc"]
});
child.once("error", reject);
child.once("exit", resolve);
})
));
console.log("parent ready");
}
`,
);
const result = runBenchmarkSample(entryPath, "health", ["--runtime-rss"]);
expect(result.status, result.stderr).toBe(mode === "auxiliary" ? 0 : 1);
const sample = JSON.parse(result.stdout).primary.cases[0].samples[0];
if (mode === "auxiliary") {
expect(sample.maxRssMb).toBeGreaterThan(0);
expect(
sample.memory.processes.map((record: { role: string }) => record.role).toSorted(),
).toEqual(["auxiliary", "runtime"]);
} else {
expect(sample.maxRssMb).toBeNull();
expect(sample.memory.runtimePid).toBeNull();
expect(result.stderr).toContain(
mode === "missing"
? "missing process high-water RSS"
: mode === "unrecognized"
? "unrecognized CLI entry"
: "ambiguous CLI runtime identity",
);
}
},
);
it.runIf(process.platform !== "win32")(
"follows an aliased launcher's direct dist handoff",
() => {
const tmpDir = memoryTempDirs.make("openclaw-cli-rss-alias-");
const launcher = join(tmpDir, "openclaw.mjs");
const alias = join(tmpDir, "cli");
const dist = join(tmpDir, "dist");
mkdirSync(dist);
const entry = join(dist, "entry.mjs");
writeFileSync(
launcher,
`
import { fork } from "node:child_process";
const usage = process.resourceUsage();
process.resourceUsage = () => ({ ...usage, maxRSS: 64 * 1024 });
const child = fork(${JSON.stringify(entry)}, process.argv.slice(2), {
stdio: ["ignore", "inherit", "inherit", "ipc"]
});
child.once("exit", (code) => process.exit(code));
`,
);
writeFileSync(
entry,
`
const usage = process.resourceUsage();
process.resourceUsage = () => ({ ...usage, maxRSS: 32 * 1024 });
console.log("runtime ready");
`,
);
symlinkSync(launcher, alias);
const result = runBenchmarkSample(alias, "health", ["--runtime-rss"]);
expect(result.status, result.stderr).toBe(0);
const sample = JSON.parse(result.stdout).primary.cases[0].samples[0];
expect(sample.maxRssMb).toBe(32);
expect(
sample.memory.processes.map((record: { role: string }) => record.role).toSorted(),
).toEqual(["launcher", "runtime"]);
},
);
it("rejects unknown CLI options before running benchmarks", () => {
expect(() => testing.validateCliArgs(["--wat"])).toThrow("Unknown argument: --wat");
const result = runBenchmarkCli(["--wat", "--help"]);
expect(result.status).toBe(1);
expect(result.stdout).toBe("");
expect(result.stderr.trim()).toBe("Unknown argument: --wat");
expect(result.stderr).not.toContain("Node.js");
expect(result.stderr).not.toContain("\n at ");
});
it("rejects short flag values before running benchmarks", () => {
expect(() => testing.validateCliArgs(["--output", "-h"])).toThrow("--output requires a value");
expect(() => testing.validateCliArgs(["--case", "-h"])).toThrow("--case requires a value");
});
it("rejects duplicate benchmark cases before running benchmarks", () => {
const result = runBenchmarkCli(["--case", "version", "--case", "version"]);
expect(result.status).toBe(1);
expect(result.stdout).toBe("");
expect(result.stderr.trim()).toBe('Duplicate --case "version"');
expect(result.stderr).not.toContain("Node.js");
expect(result.stderr).not.toContain("\n at ");
});
it("rejects duplicate single-value controls before running benchmarks", () => {
expect(() => testing.validateCliArgs(["--output", "one.json", "--output", "two.json"])).toThrow(
"--output was provided more than once",
);
});
it.runIf(process.platform !== "win32")(
"cleans timed-out benchmark process groups when the leader exits first",
async () => {
const tempDirs = createTempDirTracker();
const tmpDir = tempDirs.make("openclaw-cli-startup-timeout-group-");
const entryPath = join(tmpDir, "entry.mjs");
const leaderPidPath = join(tmpDir, "leader.pid");
const childPidPath = join(tmpDir, "child.pid");
const childTermPath = join(tmpDir, "child-term.pid");
try {
writeFileSync(
entryPath,
`
import { spawn } from "node:child_process";
import { writeFileSync } from "node:fs";
process.on("SIGTERM", () => process.exit(0));
writeFileSync(${JSON.stringify(leaderPidPath)}, String(process.pid));
spawn(process.execPath, ["--input-type=module", "-e", ${JSON.stringify(`
import { writeFileSync } from "node:fs";
process.on("SIGTERM", () => writeFileSync(${JSON.stringify(childTermPath)}, String(process.pid)));
writeFileSync(${JSON.stringify(childPidPath)}, String(process.pid));
setInterval(() => {}, 1000);
`)}], { stdio: "ignore" });
setInterval(() => {}, 1000);
`,
"utf8",
);
// Keep real processes, but advance deadlines only after child-owned readiness.
// The driver isolates Node mock timers from Vitest and the fixture processes.
const result = spawnSync(
testNodeExecPath,
[
...benchmarkArgs.slice(0, -1),
"--input-type=module",
"-e",
`
import assert from "node:assert/strict";
import { mock } from "node:test";
import { setTimeout as delay } from "node:timers/promises";
import { pathToFileURL } from "node:url";
import { isProcessAlive, waitForPidFile } from ${JSON.stringify(resolveRuntimeWorkerUrl(toolingTsEntrypoints.processWait).href)};
const realDelay = delay;
mock.timers.enable({ apis: ["setTimeout", "Date"] });
try {
const benchmark = import(pathToFileURL(process.argv[1]).href);
const leader = await waitForPidFile(${JSON.stringify(leaderPidPath)}, 8000, realDelay);
const child = await waitForPidFile(${JSON.stringify(childPidPath)}, 8000, realDelay);
assert(isProcessAlive(leader), "leader must be alive before timeout");
assert(isProcessAlive(child), "descendant must be ready before timeout");
mock.timers.tick(100);
while (isProcessAlive(leader)) await realDelay(5);
assert.equal(await waitForPidFile(${JSON.stringify(childTermPath)}, 8000, realDelay), child);
assert(isProcessAlive(child), "descendant must outlive its leader");
mock.timers.tick(50);
while (isProcessAlive(child)) await realDelay(5);
// Drain cleanup waits only after the OS has consumed SIGKILL.
mock.timers.runAll();
await benchmark;
} catch (error) {
console.error(error);
process.exit(2);
} finally {
mock.timers.reset();
}
`,
fileURLToPath(benchmarkUrl),
"--entry",
entryPath,
"--case",
"version",
"--runs",
"1",
"--warmup",
"0",
"--timeout-ms",
"100",
"--json",
],
{
cwd: repoRoot,
encoding: "utf8",
env: {
...process.env,
HOME: tmpDir,
OPENCLAW_STATE_DIR: join(tmpDir, ".openclaw"),
OPENCLAW_TEST_CLI_STARTUP_TIMEOUT_KILL_GRACE_MS: "50",
VITEST: "1",
},
timeout: 8_000,
},
);
expect(result.error, result.stderr).toBeUndefined();
expect(result.status, result.stderr).toBe(1);
expect(result.signal).toBeNull();
expect(result.stderr).toContain("version sample 1: timed out");
expect(JSON.parse(result.stdout).primary.cases[0].samples).toMatchObject([
{ timedOut: true, exitCode: 0, signal: null },
]);
expect(isProcessAlive(Number(readFileSync(leaderPidPath, "utf8")))).toBe(false);
expect(isProcessAlive(Number(readFileSync(childPidPath, "utf8")))).toBe(false);
} finally {
// The leader registers before spawning: failures before child readiness still
// leave a known group to kill, including an unregistered descendant.
if (existsSync(leaderPidPath)) {
const leader = Number(readFileSync(leaderPidPath, "utf8"));
forceKillVitestProcessGroup({ pid: leader });
await waitForDead(leader, 8_000);
}
if (existsSync(childPidPath)) {
await waitForDead(Number(readFileSync(childPidPath, "utf8")), 8_000);
}
tempDirs.cleanup();
}
},
);
it("writes compare-mode JSON output and creates parent directories", () => {
const tempDirs = createTempDirTracker();
const tmpDir = tempDirs.make("openclaw-cli-startup-compare-output-");
try {
const baselinePath = join(tmpDir, "baseline.json");
const candidatePath = join(tmpDir, "candidate.json");
const outputPath = join(tmpDir, "nested", "comparison.json");
const makeReport = (durationAvg: number, maxRssAvg: number) => ({
primary: {
entry: "openclaw.mjs",
cases: [
{
id: "version",
name: "--version",
args: ["--version"],
contract: null,
samples: [],
summary: {
sampleCount: 1,
durationMs: stats(durationAvg),
firstOutputMs: null,
maxRssMb: stats(maxRssAvg),
exitSummary: "code:0x1",
},
},
],
},
});
writeFileSync(baselinePath, JSON.stringify(makeReport(100, 50)), "utf8");
writeFileSync(candidatePath, JSON.stringify(makeReport(125, 60)), "utf8");
const comparison = {
baseline: baselinePath,
candidate: candidatePath,
deltas: [
{
id: "version",
name: "--version",
durationAvgDeltaMs: 25,
durationAvgDeltaPct: 25,
maxRssAvgDeltaMb: 10,
maxRssAvgDeltaPct: 20,
},
],
};
const result = runBenchmarkCli([
"--runtime-rss",
"--compare-baseline",
baselinePath,
"--compare-candidate",
candidatePath,
"--output",
outputPath,
"--json",
]);
expect(result.status).toBe(0);
expect(result.stderr).toBe("");
expect(JSON.parse(result.stdout)).toEqual(comparison);
expect(JSON.parse(readFileSync(outputPath, "utf8"))).toEqual(comparison);
const attributed = {
primary: { ...makeReport(125, 60).primary, memoryMetric: "cli-runtime-max-rss-v1" },
};
writeFileSync(candidatePath, JSON.stringify(attributed), "utf8");
const incompatible = runBenchmarkCli([
"--runtime-rss",
"--compare-baseline",
baselinePath,
"--compare-candidate",
candidatePath,
"--json",
]);
expect(incompatible.status).toBe(1);
expect(incompatible.stdout).toBe("");
expect(incompatible.stderr).toContain("Incompatible CLI RSS metrics");
writeFileSync(
baselinePath,
JSON.stringify({
primary: { ...makeReport(100, 50).primary, memoryMetric: "cli-runtime-max-rss-v1" },
}),
"utf8",
);
const compatible = runBenchmarkCli([
"--compare-baseline",
baselinePath,
"--compare-candidate",
candidatePath,
"--json",
]);
expect(compatible.status, compatible.stderr).toBe(0);
expect(JSON.parse(compatible.stdout)).toEqual(comparison);
for (const [before, after, error] of [
[undefined, "native", null],
["native", undefined, null],
["native", "native", null],
["transport", "transport", null],
[undefined, "transport", "Incompatible CLI execution modes"],
["transport", "native", "Incompatible CLI execution modes"],
["unknown", "unknown", "Unknown CLI execution mode"],
[null, "native", "Unknown CLI execution mode"],
["native", 1, "Unknown CLI execution mode"],
] satisfies Array<[unknown, unknown, string | null]>) {
writeFileSync(
baselinePath,
JSON.stringify({ primary: { ...makeReport(100, 50).primary, executionMode: before } }),
);
writeFileSync(
candidatePath,
JSON.stringify({ primary: { ...makeReport(125, 60).primary, executionMode: after } }),
);
const modeResult = runBenchmarkCli([
"--compare-baseline",
baselinePath,
"--compare-candidate",
candidatePath,
"--json",
]);
expect(modeResult.status, modeResult.stderr).toBe(error ? 1 : 0);
if (error) {
expect(modeResult.stderr).toContain(error);
expect(modeResult.stdout).toBe("");
} else {
expect(JSON.parse(modeResult.stdout)).toEqual(comparison);
}
}
} finally {
tempDirs.cleanup();
}
});
it("passes generated import hook paths as file URL specifiers", () => {
const hookPath = resolve("measure-rss.mjs");
expect(testing.nodeImportSpecifierForPath(hookPath)).toBe(pathToFileURL(hookPath).href);
});
it("fails reports with no measured samples", () => {
expect(
testing.collectFailedSamples(
suiteResult({
samples: [],
summary: { durationMs: stats(0), firstOutputMs: null, maxRssMb: null, exitSummary: "" },
}),
),
).toEqual(["openclaw.mjs version: no measured samples"]);
});
it("fails reports with nonzero or signaled CLI samples", () => {
expect(
testing.collectFailedSamples(
suiteResult(
{
id: "gatewayStatusJson",
name: "gateway status --json",
args: ["gateway", "status", "--json"],
samples: [
cliSample(),
cliSample({ exitCode: 1 }),
cliSample({ exitCode: null, signal: "SIGTERM" }),
cliSample({ timedOut: true }),
],
summary: { exitSummary: "code:0x1, code:1x1, signal:SIGTERMx1" },
},
"dist/entry.js",
),
),
).toEqual([
"dist/entry.js gatewayStatusJson sample 2: exited with code 1",
"dist/entry.js gatewayStatusJson sample 3: exited via signal SIGTERM",
"dist/entry.js gatewayStatusJson sample 4: timed out",
]);
});
it("retains and validates warmup samples separately from measured samples", () => {
const passingSample = cliSample({
startedAt: "2026-08-01T20:00:00.000Z",
endedAt: "2026-08-01T20:00:00.010Z",
});
expect(
testing.collectFailedSamples(
suiteResult(
{
id: "gatewayHealthJsonWarmState",
name: "gateway health --json (warm state)",
args: ["gateway", "health", "--json"],
warmupSamples: [{ ...passingSample, exitCode: 1 }],
samples: [passingSample],
},
"dist/entry.js",
),
),
).toEqual(["dist/entry.js gatewayHealthJsonWarmState warmup 1: exited with code 1"]);
});
it("fails reports with samples that did not report RSS", () => {
expect(
testing.collectFailedSamples(
suiteResult({
samples: [cliSample({ maxRssMb: null })],
summary: { maxRssMb: null },
}),
),
).toEqual(["openclaw.mjs version sample 1: did not report max RSS"]);
});
it("allows declared nonzero exit codes for clean-state probes", () => {
expect(
testing.collectFailedSamples(
suiteResult({
id: "health",
name: "health",
args: ["health"],
expectedExitCodes: [0, 1],
expectedNonzeroOutputIncludes: ["Gateway target:"],
samples: [
cliSample({
exitCode: 1,
stderrTail:
"Health check failed: gateway closed\n Gateway target: ws://127.0.0.1:18789",
}),
],
summary: { exitSummary: "code:1x1" },
}),
),
).toEqual([]);
});
it("rejects allowed nonzero exits without their expected clean-state output", () => {
expect(
testing.collectFailedSamples(
suiteResult({
id: "health",
name: "health",
args: ["health"],
expectedExitCodes: [0, 1],
expectedNonzeroOutputIncludes: ["Gateway target:"],
samples: [cliSample({ exitCode: 1, stderrTail: "TypeError: crashed before output" })],
summary: { exitSummary: "code:1x1" },
}),
),
).toEqual([
"openclaw.mjs health sample 1: exited with expected code 1 but output did not match expected clean-state markers (Gateway target:)",
]);
});
it("rejects invalid measured run counts", () => {
expect(() => testing.parsePositiveInt("0", 5, "--runs")).toThrow(
"--runs must be an integer >= 1",
);
expect(() => testing.parsePositiveInt("2abc", 5, "--runs")).toThrow(
"--runs must be an integer >= 1",
);
expect(() => testing.parsePositiveInt("1.5", 5, "--runs")).toThrow(
"--runs must be an integer >= 1",
);
expect(() => testing.parsePositiveInt("1e3", 5, "--runs")).toThrow(
"--runs must be an integer >= 1",
);
expect(() => testing.parsePositiveInt("0x10", 5, "--runs")).toThrow(
"--runs must be an integer >= 1",
);
expect(testing.parsePositiveInt("1", 5)).toBe(1);
expect(testing.parseNonNegativeInt("0", 1)).toBe(0);
expect(() => testing.parseNonNegativeInt("-1", 1, "--warmup")).toThrow(
"--warmup must be an integer >= 0",
);
expect(() => testing.parseNonNegativeInt("0b10", 1, "--warmup")).toThrow(
"--warmup must be an integer >= 0",
);
});
it("writes a config fixture for config get benchmarks", () => {
for (const id of ["configGetGatewayPort", "gatewayHealthJson", "health", "healthJson"]) {
expect(withEnv({ OPENCLAW_GATEWAY_PORT: undefined }, () => configFixture(id))).toEqual({
gateway: { auth: { mode: "none" }, bind: "loopback", mode: "local", port: 32123 },
});
}
for (const id of ["gatewayHealthJsonWarmState", "gatewayHealthJsonFreshState"]) {
expect(withEnv({ OPENCLAW_GATEWAY_PORT: undefined }, () => configFixture(id))).toEqual({
gateway: { auth: { mode: "token" }, bind: "loopback", mode: "local", port: 32123 },
});
}
});
it("parses config fixture gateway ports strictly from env", () => {
expect(testing.parseGatewayPortEnv(undefined)).toBe(32123);
expect(testing.parseGatewayPortEnv("127.0.0.1:45678")).toBe(45678);
expect(testing.parseGatewayPortEnv("[::1]:45679")).toBe(45679);
expect(testing.parseGatewayPortEnv("::1")).toBe(32123);
expect(testing.parseGatewayPortEnv("[::1]")).toBe(32123);
for (const id of [
"gatewayHealthJson",
"gatewayHealthJsonWarmState",
"gatewayHealthJsonFreshState",
]) {
expect(withEnv({ OPENCLAW_GATEWAY_PORT: "45678" }, () => configFixture(id))).toMatchObject({
gateway: { port: 45678 },
});
}
for (const invalid of ["45678abc", "127.0.0.1:45678abc"]) {
expect(() =>
withEnv({ OPENCLAW_GATEWAY_PORT: invalid }, () => configFixture("gatewayHealthJson")),
).toThrow("OPENCLAW_GATEWAY_PORT must be an integer >= 1");
}
});
});