/ src / tests / shell-execution.test.ts
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  });