shell-execution.test.ts
1 import { describe, test, expect } from "@jest/globals"; 2 import { executeShellCommand } from "../utils/shell-execution.js"; 3 import os from "os"; 4 5 describe("Shell Execution", () => { 6 test("executes simple echo command", async () => { 7 const command = "echo hello"; 8 const result = await executeShellCommand(command); 9 10 expect(result.exitCode).toBe(0); 11 expect(result.stdout).toContain("hello"); 12 expect(result.timedOut).toBe(false); 13 expect(result.error).toBeUndefined(); 14 }); 15 16 test("captures standard output", async () => { 17 const command = 18 os.platform() === "win32" ? "echo testoutput" : "echo testoutput"; 19 const result = await executeShellCommand(command); 20 21 expect(result.exitCode).toBe(0); 22 expect(result.stdout).toContain("testoutput"); 23 }); 24 25 test("captures error output", async () => { 26 const command = 27 os.platform() === "win32" 28 ? "Write-Error 'test error'" 29 : "echo 'test error' >&2"; 30 const result = await executeShellCommand(command); 31 32 expect(result.stderr).toContain("test error"); 33 }); 34 35 test("handles command with non-zero exit code", async () => { 36 const command = os.platform() === "win32" ? "exit 1" : "exit 1"; 37 const result = await executeShellCommand(command); 38 39 expect(result.exitCode).toBe(1); 40 expect(result.timedOut).toBe(false); 41 }); 42 43 test("handles timeout", async () => { 44 const command = 45 os.platform() === "win32" ? "Start-Sleep -Seconds 5" : "sleep 5"; 46 const result = await executeShellCommand(command, { timeout: 1000 }); 47 48 expect(result.timedOut).toBe(true); 49 }, 10000); 50 51 test("respects working directory", async () => { 52 const testDir = os.tmpdir(); 53 const command = os.platform() === "win32" ? "pwd" : "pwd"; 54 const result = await executeShellCommand(command, { workdir: testDir }); 55 56 expect(result.exitCode).toBe(0); 57 expect(result.stdout.toLowerCase()).toContain(testDir.toLowerCase()); 58 }); 59 60 test("handles command that doesn't exist", async () => { 61 const command = "nonexistentcommand12345"; 62 const result = await executeShellCommand(command); 63 64 // Either error is set or exit code is non-zero 65 expect(result.error || result.exitCode !== 0).toBeTruthy(); 66 }, 15000); 67 68 test("handles chained commands", async () => { 69 const command = 70 os.platform() === "win32" 71 ? "echo first; echo second" 72 : "echo first && echo second"; 73 const result = await executeShellCommand(command); 74 75 expect(result.exitCode).toBe(0); 76 expect(result.stdout).toContain("first"); 77 expect(result.stdout).toContain("second"); 78 }); 79 80 test("respects environment variables", async () => { 81 const command = 82 os.platform() === "win32" ? "echo $env:TEST_VAR" : "echo $TEST_VAR"; 83 const result = await executeShellCommand(command, { 84 env: { TEST_VAR: "test_value" }, 85 }); 86 87 expect(result.stdout).toContain("test_value"); 88 }); 89 90 test("handles empty output", async () => { 91 const command = os.platform() === "win32" ? "exit 0" : "true"; 92 const result = await executeShellCommand(command); 93 94 expect(result.exitCode).toBe(0); 95 // stdout may be empty or contain minimal output 96 expect(result.stderr).toBe(""); 97 }); 98 99 test("handles multiline output", async () => { 100 const command = 101 os.platform() === "win32" 102 ? "echo line1; echo line2; echo line3" 103 : 'echo "line1\nline2\nline3"'; 104 const result = await executeShellCommand(command); 105 106 expect(result.exitCode).toBe(0); 107 expect(result.stdout).toContain("line1"); 108 expect(result.stdout).toContain("line2"); 109 expect(result.stdout).toContain("line3"); 110 }); 111 112 test("trims output whitespace", async () => { 113 const command = "echo test"; 114 const result = await executeShellCommand(command); 115 116 // Output should be trimmed (no leading/trailing whitespace) 117 expect(result.stdout).toBe(result.stdout.trim()); 118 expect(result.stderr).toBe(result.stderr.trim()); 119 }); 120 });