From a0ab5c00a825e7bff592e390e746fae0ade7764d Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 22 Jun 2026 13:44:52 +0800 Subject: [PATCH] test(scripts): harden kitchen sink timeout tests --- .../kitchen-sink-plugin-assertions.test.ts | 2 +- test/scripts/kitchen-sink-rpc-walk.test.ts | 50 +++++++++++++++---- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/test/scripts/kitchen-sink-plugin-assertions.test.ts b/test/scripts/kitchen-sink-plugin-assertions.test.ts index 29195aba2e48..ec79b91cb740 100644 --- a/test/scripts/kitchen-sink-plugin-assertions.test.ts +++ b/test/scripts/kitchen-sink-plugin-assertions.test.ts @@ -834,7 +834,7 @@ set -euo pipefail export PATH="$FAKE_BIN:$PATH" export KITCHEN_SINK_SWEEP_SOURCE_ONLY=1 export KITCHEN_SINK_TMP_DIR="$SCRATCH_ROOT" -export OPENCLAW_CLAWHUB_FIXTURE_WAIT_ATTEMPTS=5 +export OPENCLAW_CLAWHUB_FIXTURE_WAIT_ATTEMPTS=25 export OPENCLAW_DOCKER_E2E_LOG_PRINT_BYTES=64 source scripts/e2e/lib/kitchen-sink-plugin/sweep.sh set +e diff --git a/test/scripts/kitchen-sink-rpc-walk.test.ts b/test/scripts/kitchen-sink-rpc-walk.test.ts index c2050fe5832a..8ce32b5170b6 100644 --- a/test/scripts/kitchen-sink-rpc-walk.test.ts +++ b/test/scripts/kitchen-sink-rpc-walk.test.ts @@ -738,7 +738,14 @@ describe("kitchen-sink RPC command output capture", () => { const root = mkdtempSync(path.join(tmpdir(), "openclaw-kitchen-rpc-timeout-")); const scriptPath = path.join(root, "trap-term.mjs"); const grandchildPidPath = path.join(root, "grandchild.pid"); + const grandchildReadyPath = path.join(root, "grandchild.ready"); let grandchildPid = 0; + const grandchildScript = [ + "const fs = require('node:fs');", + "process.on('SIGTERM', () => {});", + "fs.writeFileSync(process.env.GRANDCHILD_READY_PATH, 'ready');", + "setInterval(() => {}, 1000);", + ].join(" "); writeFileSync( scriptPath, @@ -748,8 +755,8 @@ import fs from "node:fs"; const grandchild = spawn(process.execPath, [ "-e", - "process.on('SIGTERM', () => {}); setInterval(() => {}, 1000);", -], { stdio: "ignore" }); + ${JSON.stringify(grandchildScript)}, +], { env: { ...process.env, GRANDCHILD_READY_PATH: process.argv[3] }, stdio: "ignore" }); fs.writeFileSync(process.argv[2], String(grandchild.pid)); process.on("SIGTERM", () => {}); setInterval(() => {}, 1000); @@ -757,19 +764,28 @@ setInterval(() => {}, 1000); "utf8", ); - const runPromise = runCommand(process.execPath, [scriptPath, grandchildPidPath], { + const runPromise = runCommand(process.execPath, [scriptPath, grandchildPidPath, grandchildReadyPath], { detached: undefined, timeoutKillGraceMs: 25, timeoutMs: 500, }); + const runErrorPromise = runPromise.then( + () => { + throw new Error("expected timed command to reject"); + }, + (error: unknown) => error, + ); try { await waitFor(() => existsSync(grandchildPidPath)); + await waitFor(() => existsSync(grandchildReadyPath)); grandchildPid = Number.parseInt(readText(grandchildPidPath), 10); expect(Number.isInteger(grandchildPid)).toBe(true); expect(isProcessAlive(grandchildPid)).toBe(true); - await expect(runPromise).rejects.toThrow("timed out after 500ms"); + const runError = await runErrorPromise; + expect(runError).toBeInstanceOf(Error); + expect((runError as Error).message).toContain("timed out after 500ms"); await waitFor(() => !isProcessAlive(grandchildPid), 5_000); } finally { await runPromise.catch(() => {}); @@ -1015,7 +1031,14 @@ describe("kitchen-sink RPC caller loading", () => { const root = makeTempDir(tempDirs, "openclaw-kitchen-rpc-timeout-clean-parent-"); const scriptPath = path.join(root, "term-zero-grandchild.mjs"); const grandchildPidPath = path.join(root, "grandchild.pid"); + const grandchildReadyPath = path.join(root, "grandchild.ready"); let grandchildPid = 0; + const grandchildScript = [ + "const fs = require('node:fs');", + "process.on('SIGTERM', () => {});", + "fs.writeFileSync(process.env.GRANDCHILD_READY_PATH, 'ready');", + "setInterval(() => {}, 1000);", + ].join(" "); writeFileSync( scriptPath, @@ -1025,8 +1048,8 @@ import fs from "node:fs"; const grandchild = spawn(process.execPath, [ "-e", - "process.on('SIGTERM', () => {}); setInterval(() => {}, 1000);", -], { stdio: "ignore" }); + ${JSON.stringify(grandchildScript)}, +], { env: { ...process.env, GRANDCHILD_READY_PATH: process.argv[3] }, stdio: "ignore" }); fs.writeFileSync(process.argv[2], String(grandchild.pid)); process.on("SIGTERM", () => process.exit(0)); setInterval(() => {}, 1000); @@ -1034,18 +1057,27 @@ setInterval(() => {}, 1000); "utf8", ); - const runPromise = runCommand(process.execPath, [scriptPath, grandchildPidPath], { + const runPromise = runCommand(process.execPath, [scriptPath, grandchildPidPath, grandchildReadyPath], { timeoutKillGraceMs: 2_000, - timeoutMs: 100, + timeoutMs: 1_500, }); + const runErrorPromise = runPromise.then( + () => { + throw new Error("expected timed command to reject"); + }, + (error: unknown) => error, + ); try { await waitFor(() => existsSync(grandchildPidPath)); + await waitFor(() => existsSync(grandchildReadyPath)); grandchildPid = Number.parseInt(readText(grandchildPidPath), 10); expect(Number.isInteger(grandchildPid)).toBe(true); expect(isProcessAlive(grandchildPid)).toBe(true); - await expect(runPromise).rejects.toThrow("timed out after 100ms"); + const runError = await runErrorPromise; + expect(runError).toBeInstanceOf(Error); + expect((runError as Error).message).toContain("timed out after 1500ms"); await waitFor(() => !isProcessAlive(grandchildPid), 5_000); } finally { await runPromise.catch(() => {});