95 lines
3.7 KiB
JavaScript
95 lines
3.7 KiB
JavaScript
/**
|
|
* 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");
|
|
});
|
|
});
|