Files
16gagent/test/baseline/test-05-tool-exec.test.mjs
T
2026-06-06 10:40:48 +08:00

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");
});
});