import { describe, expect, it } from "vitest"; import { captureEnv } from "../test-utils/env.js"; import { runCommandWithTimeout, shouldSpawnWithShell } from "./exec.js"; describe("runCommandWithTimeout", () => { it("never enables shell execution (Windows cmd.exe injection hardening)", () => { expect( shouldSpawnWithShell({ resolvedCommand: "npm.cmd", platform: "win32", }), ).toBe(false); }); it("merges custom env with process.env", async () => { const envSnapshot = captureEnv(["OPENCLAW_BASE_ENV"]); process.env.OPENCLAW_BASE_ENV = "base"; try { const result = await runCommandWithTimeout( [ process.execPath, "-e", 'process.stdout.write((process.env.OPENCLAW_BASE_ENV ?? "") + "|" + (process.env.OPENCLAW_TEST_ENV ?? ""))', ], { timeoutMs: 5_000, env: { OPENCLAW_TEST_ENV: "ok" }, }, ); expect(result.code).toBe(0); expect(result.stdout).toBe("base|ok"); expect(result.termination).toBe("exit"); } finally { envSnapshot.restore(); } }); it("kills command when no output timeout elapses", async () => { const result = await runCommandWithTimeout( [process.execPath, "-e", "setTimeout(() => {}, 1_000)"], { timeoutMs: 1_000, noOutputTimeoutMs: 35, }, ); expect(result.termination).toBe("no-output-timeout"); expect(result.noOutputTimedOut).toBe(true); expect(result.code).not.toBe(0); }); it("resets no output timer when command keeps emitting output", async () => { const result = await runCommandWithTimeout( [ process.execPath, "-e", 'let i=0; const t=setInterval(() => { process.stdout.write("."); i += 1; if (i >= 2) { clearInterval(t); process.exit(0); } }, 5);', ], { timeoutMs: 1_000, noOutputTimeoutMs: 120, }, ); expect(result.signal).toBeNull(); expect(result.code ?? 0).toBe(0); expect(result.termination).toBe("exit"); expect(result.noOutputTimedOut).toBe(false); expect(result.stdout.length).toBeGreaterThanOrEqual(2); }); it("reports global timeout termination when overall timeout elapses", async () => { const result = await runCommandWithTimeout( [process.execPath, "-e", "setTimeout(() => {}, 1_000)"], { timeoutMs: 15, }, ); expect(result.termination).toBe("timeout"); expect(result.noOutputTimedOut).toBe(false); expect(result.code).not.toBe(0); }); });