describe("Child Process", () => {
describe("exec", () => {
it("should execute a shell command and return stdout", async () => {
const result = await child_process.exec("echo hello");
assert.ok(
result.stdout.includes("hello"),
"stdout should contain 'hello'",
);
assert.equal(result.code, 0, "exit code should be 0");
});
it("should capture stderr", async () => {
const result = await child_process.exec("echo error >&2");
assert.ok(
result.stderr.includes("error"),
"stderr should contain 'error'",
);
});
it("should return non-zero exit code for failing command", async () => {
const result = await child_process.exec("exit 1");
assert.equal(result.code, 1, "exit code should be 1");
});
it("should support cwd option", async () => {
const result = await child_process.exec("pwd", { cwd: "/tmp" });
assert.ok(
result.stdout.includes("/tmp") ||
result.stdout.includes("/private/tmp"),
"should execute in /tmp directory",
);
});
it("should support env option", async () => {
const result = await child_process.exec("echo $TEST_CHILD_VAR", {
env: { TEST_CHILD_VAR: "hello_from_env", PATH: process.env.PATH },
});
assert.ok(
result.stdout.includes("hello_from_env"),
"should use custom env variable",
);
});
it("should inherit process.env modifications", async () => {
process.env.CHILD_PROCESS_TEST_VAR = "modified_value_123";
const result = await child_process.exec(
"echo $CHILD_PROCESS_TEST_VAR",
);
assert.ok(
result.stdout.includes("modified_value_123"),
"child should receive modified process.env",
);
delete process.env.CHILD_PROCESS_TEST_VAR;
});
it("should handle command not found", async () => {
const result = await child_process.exec(
"nonexistent_cmd_xyz_12345 2>/dev/null || echo 'failed'",
);
assert.ok(
result.code !== 0 || result.stdout.includes("failed"),
"should indicate failure for nonexistent command",
);
});
it("should support timeout option", async () => {
const start = Date.now();
try {
await child_process.exec("sleep 10", { timeout: 100 });
assert.fail("should have timed out");
} catch (e) {
const elapsed = Date.now() - start;
assert.ok(elapsed < 1000, "should timeout quickly");
assert.ok(
e.message.includes("timed out") || e.code === "ETIMEDOUT",
"should have timeout error",
);
}
});
it("should complete before timeout", async () => {
const result = await child_process.exec("echo fast", { timeout: 5000 });
assert.ok(result.stdout.includes("fast"), "should complete successfully");
});
});
describe("execFile", () => {
it("should execute a file directly", async () => {
const result = await child_process.execFile("/bin/echo", [
"hello",
"world",
]);
assert.ok(
result.stdout.includes("hello world"),
"stdout should contain 'hello world'",
);
assert.equal(result.code, 0, "exit code should be 0");
});
it("should pass arguments correctly", async () => {
const result = await child_process.execFile("/bin/echo", [
"-n",
"test",
]);
assert.ok(result.stdout.includes("test"), "stdout should contain 'test'");
});
it("should support cwd option", async () => {
const result = await child_process.execFile("/bin/pwd", [], {
cwd: "/tmp",
});
assert.ok(
result.stdout.includes("/tmp") ||
result.stdout.includes("/private/tmp"),
"should execute in /tmp directory",
);
});
it("should handle special characters in arguments", async () => {
const result = await child_process.execFile("/bin/echo", [
"hello world",
"foo'bar",
'baz"qux',
]);
assert.ok(
result.stdout.includes("hello world"),
"should handle spaces in args",
);
assert.ok(
result.stdout.includes("foo'bar"),
"should handle single quotes in args",
);
assert.ok(
result.stdout.includes('baz"qux'),
"should handle double quotes in args",
);
});
it("should support timeout option", async () => {
const start = Date.now();
try {
await child_process.execFile("/bin/sleep", ["10"], { timeout: 100 });
assert.fail("should have timed out");
} catch (e) {
const elapsed = Date.now() - start;
assert.ok(elapsed < 1000, "should timeout quickly");
assert.ok(
e.message.includes("timed out") || e.code === "ETIMEDOUT",
"should have timeout error",
);
}
});
});
describe("spawn", () => {
it("should spawn a process and return ChildProcess object", () => {
const child = child_process.spawn("echo", ["hello"]);
assert.ok(child !== null, "should return a ChildProcess object");
assert.ok(typeof child.pid === "number", "should have pid property");
});
it("should have stdin, stdout and stderr streams", () => {
const child = child_process.spawn("cat", []);
assert.ok(child.stdin !== null, "should have stdin");
assert.ok(child.stdout !== null, "should have stdout");
assert.ok(child.stderr !== null, "should have stderr");
assert.ok(
typeof child.stdin.getWriter === "function",
"stdin should be a WritableStream",
);
assert.ok(
typeof child.stdout.getReader === "function",
"stdout should be a ReadableStream",
);
assert.ok(
typeof child.stderr.getReader === "function",
"stderr should be a ReadableStream",
);
child.kill();
});
it("should write to stdin and read from stdout", async () => {
const child = child_process.spawn("cat", []);
const writer = child.stdin.getWriter();
const encoder = new TextEncoder();
await writer.write(encoder.encode("hello from stdin"));
await writer.close();
const reader = child.stdout.getReader();
let output = "";
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
output += decoder.decode(value);
}
assert.ok(
output.includes("hello from stdin"),
"should read what was written to stdin",
);
});
it("should read stdout data via stream", async () => {
const child = child_process.spawn("echo", ["hello streaming"]);
const reader = child.stdout.getReader();
let output = "";
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
output += decoder.decode(value);
}
assert.ok(
output.includes("hello streaming"),
"should read 'hello streaming' from stdout",
);
});
it("should support shell option", async () => {
const child = child_process.spawn("echo $HOME", [], { shell: true });
const reader = child.stdout.getReader();
let output = "";
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
output += decoder.decode(value);
}
assert.ok(output.length > 0, "should receive HOME directory");
});
it("should support cwd option", async () => {
const child = child_process.spawn("pwd", [], { cwd: "/tmp" });
const reader = child.stdout.getReader();
let output = "";
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
output += decoder.decode(value);
}
assert.ok(
output.includes("/tmp") || output.includes("/private/tmp"),
"should execute in /tmp directory",
);
});
it("should get exitCode via wait()", async () => {
const child = child_process.spawn("echo", ["test"]);
const code = await child.wait();
assert.equal(code, 0, "exitCode should be 0");
});
it("should capture non-zero exit code via wait()", async () => {
const child = child_process.spawn("exit", ["42"], { shell: true });
const code = await child.wait();
assert.equal(code, 42, "exitCode should be 42");
});
it("should emit exit event", async () => {
const child = child_process.spawn("echo", ["test"]);
const code = await new Promise((resolve) => {
child.once("exit", resolve);
});
assert.equal(code, 0, "exit event should provide exit code");
});
});
describe("ChildProcess properties", () => {
it("should have pid after spawn", () => {
const child = child_process.spawn("echo", ["test"]);
assert.ok(typeof child.pid === "number", "pid should be a number");
assert.ok(child.pid > 0, "pid should be positive");
});
it("should have kill method", () => {
const child = child_process.spawn("sleep", ["10"]);
assert.ok(typeof child.kill === "function", "should have kill method");
const result = child.kill();
assert.equal(result, true, "kill should return true for running process");
});
it("should support kill with signal name", () => {
const child = child_process.spawn("sleep", ["10"]);
const result = child.kill("SIGKILL");
assert.equal(result, true, "kill with SIGKILL should return true");
});
it("should support kill with signal number", () => {
const child = child_process.spawn("sleep", ["10"]);
const result = child.kill("9"); assert.equal(result, true, "kill with signal number should return true");
});
it("should have wait method", () => {
const child = child_process.spawn("echo", ["test"]);
assert.ok(typeof child.wait === "function", "should have wait method");
});
});
describe("EventEmitter interface", () => {
it("should have on method", () => {
const child = child_process.spawn("echo", ["test"]);
assert.ok(typeof child.on === "function", "should have on method");
});
it("should have once method", () => {
const child = child_process.spawn("echo", ["test"]);
assert.ok(typeof child.once === "function", "should have once method");
});
it("should have off method", () => {
const child = child_process.spawn("echo", ["test"]);
assert.ok(typeof child.off === "function", "should have off method");
});
it("should have emit method", () => {
const child = child_process.spawn("echo", ["test"]);
assert.ok(typeof child.emit === "function", "should have emit method");
});
});
});