/** * PR-A Baseline Test 05: exec Tool Current Behavior * Verifies: shell command execution returns stdout/stderr/exitCode correctly * Freezes: exec tool contract */ import { describe, it } from "node:test"; import assert from "node:assert"; import { execSync, spawnSync } from "node:child_process"; import { homedir } from "node:os"; import { join } from "node:path"; const GATEWAY_URL = "http://127.0.0.1:18789"; describe("test-05: exec Tool Baseline", () => { it("05.01 - local shell execution works (node child_process)", () => { const result = spawnSync("echo", ["hello"], { encoding: "utf8", timeout: 5000, }); console.log("[BASELINE] echo hello stdout:", JSON.stringify(result.stdout.trim())); console.log("[BASELINE] echo hello stderr:", JSON.stringify(result.stderr)); console.log("[BASELINE] echo hello status:", result.status); assert.equal(result.stdout.trim(), "hello", "echo must output hello"); assert.equal(result.status, 0, "exit code must be 0"); }); it("05.02 - shell error exit codes are preserved", () => { const result = spawnSync("sh", ["-c", "exit 42"], { encoding: "utf8", timeout: 5000, }); console.log("[BASELINE] exit 42 status:", result.status); assert.equal(result.status, 42, "exit code must be preserved exactly"); }); it("05.03 - shell stderr is captured separately", () => { const result = spawnSync("sh", ["-c", "echo stdout-text && echo stderr-text >&2"], { encoding: "utf8", timeout: 5000, }); console.log("[BASELINE] stdout:", JSON.stringify(result.stdout.trim())); console.log("[BASELINE] stderr:", JSON.stringify(result.stderr.trim())); assert.ok(result.stdout.includes("stdout-text"), "stdout must contain message"); assert.ok(result.stderr.includes("stderr-text"), "stderr must contain message"); }); it("05.04 - shell output encoding is UTF-8", () => { const result = spawnSync("sh", ["-c", "echo 你好世界"], { encoding: "utf8", timeout: 5000, }); console.log("[BASELINE] UTF-8 output:", JSON.stringify(result.stdout.trim())); assert.ok(result.stdout.includes("你好世界"), "UTF-8 must be preserved"); }); it("05.05 - shell timeout kills process", () => { const start = Date.now(); const result = spawnSync("sleep", ["10"], { encoding: "utf8", timeout: 2000, // 2 second timeout }); const elapsed = Date.now() - start; console.log("[BASELINE] sleep 10 timeout: elapsed=%dms, killed=%s", elapsed, result.signal || "none"); // Process should be killed by timeout assert.ok(result.signal === "SIGTERM" || result.status !== 0 || elapsed < 10000, "long-running process must be terminated by timeout"); }); it("05.06 - exec tool: binary output handling", () => { // Create a null-byte test const result = spawnSync("printf", ["\\x00hello\\x00"], { encoding: "buffer", timeout: 5000, }); console.log("[BASELINE] binary stdout length:", result.stdout.length); console.log("[BASELINE] binary stdout bytes:", Array.from(result.stdout.slice(0, 10)).map(b => '0x' + b.toString(16)).join(' ')); assert.ok(result.stdout.includes(0x68), "output must contain 'h' (0x68)"); }); it("05.07 - exec tool: large output handled", () => { // Generate 10KB of output const result = spawnSync("sh", ["-c", "yes head | head -1000"], { encoding: "utf8", timeout: 5000, maxBuffer: 1024 * 1024, }); const lines = result.stdout.trim().split("\n").length; console.log("[BASELINE] large output: lines=%d, bytes=%d", lines, result.stdout.length); assert.ok(lines > 100, "large output must be captured"); }); });