| 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(() => {}, 10_000)"], |
| { |
| timeoutMs: 5_000, |
| noOutputTimeoutMs: 50, |
| }, |
| ); |
|
|
| 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); } }, 10);', |
| ], |
| { |
| timeoutMs: 5_000, |
| noOutputTimeoutMs: 160, |
| }, |
| ); |
|
|
| expect(result.code).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(() => {}, 10_000)"], |
| { |
| timeoutMs: 15, |
| }, |
| ); |
|
|
| expect(result.termination).toBe("timeout"); |
| expect(result.noOutputTimedOut).toBe(false); |
| expect(result.code).not.toBe(0); |
| }); |
| }); |
|
|