diff --git a/src/shared/bun-spawn-shim.test.ts b/src/shared/bun-spawn-shim.test.ts index 06905bd76..238cfb1b1 100644 --- a/src/shared/bun-spawn-shim.test.ts +++ b/src/shared/bun-spawn-shim.test.ts @@ -59,6 +59,33 @@ describe("bun-spawn-shim", () => { expect(result.exitCode).toBe(0) expect(result.success).toBe(true) - expect(Buffer.from(result.stdout).toString().trim()).toBe("sync-ok") + expect(result.stdout).toBeDefined() + expect(Buffer.from(result.stdout!).toString().trim()).toBe("sync-ok") + }) + + test("#given spawnSync command #when it completes #then result.pid is a positive number", () => { + const result = spawnSync(["bun", "--version"], { stdout: "pipe", stderr: "pipe" }) + + expect(result.pid).toBeGreaterThan(0) + }) + + test("#given default stdio #when child reads stdin #then it does not hang waiting for input", async () => { + const proc = spawn(["cat"], { stdout: "pipe", stderr: "pipe" }) + + const exitCode = await proc.exited + + expect(exitCode).toBe(0) + }) + + test("#given missing executable #when spawn invoked #then the error is surfaced to the caller", async () => { + let observedError: unknown + try { + const proc = spawn(["__omo-shim-missing-binary__"], { stdout: "pipe", stderr: "pipe" }) + await proc.exited + } catch (error) { + observedError = error + } + + expect(observedError).toBeDefined() }) }) diff --git a/src/shared/bun-spawn-shim.ts b/src/shared/bun-spawn-shim.ts index 158894f2d..de756793d 100644 --- a/src/shared/bun-spawn-shim.ts +++ b/src/shared/bun-spawn-shim.ts @@ -30,8 +30,8 @@ export interface SpawnedProcess { export interface SpawnSyncResult { readonly exitCode: number - readonly stdout: Buffer - readonly stderr: Buffer + readonly stdout: Buffer | undefined + readonly stderr: Buffer | undefined readonly success: boolean readonly pid: number } @@ -81,20 +81,20 @@ function resolveCommand(cmdOrOpts: unknown, optsArg?: unknown): { cmd: string[]; function resolveStdio(options: SpawnOptions): StdioTuple { if (options.stdio) return options.stdio - return [options.stdin ?? "pipe", options.stdout ?? "pipe", options.stderr ?? "pipe"] + return [options.stdin ?? "ignore", options.stdout ?? "pipe", options.stderr ?? "inherit"] } function wrapNodeProcess(proc: ReturnType): SpawnedProcess { let exitCode: number | null = null - const exited = new Promise((resolve) => { + const exited = new Promise((resolve, reject) => { proc.on("exit", (code) => { exitCode = code ?? 1 resolve(exitCode) }) - proc.on("error", () => { + proc.on("error", (error) => { if (exitCode === null) { exitCode = 1 - resolve(1) + reject(error) } }) }) @@ -158,9 +158,9 @@ export function spawnSync(cmdOrOpts: unknown, opts?: unknown): SpawnSyncResult { return { exitCode: result.status ?? 1, - stdout: result.stdout, - stderr: result.stderr, + stdout: result.stdout ?? undefined, + stderr: result.stderr ?? undefined, success: (result.status ?? 1) === 0, - pid: -1, + pid: result.pid ?? -1, } } diff --git a/src/shared/spawn-with-windows-hide.ts b/src/shared/spawn-with-windows-hide.ts index 872c8deeb..f6fec2a7e 100644 --- a/src/shared/spawn-with-windows-hide.ts +++ b/src/shared/spawn-with-windows-hide.ts @@ -75,7 +75,7 @@ export function spawnWithWindowsHide(command: string[], options: SpawnOptions): const proc = nodeSpawn(cmd, args, { cwd: options.cwd, env: options.env, - stdio: [options.stdin ?? "pipe", options.stdout ?? "pipe", options.stderr ?? "pipe"], + stdio: [options.stdin ?? "ignore", options.stdout ?? "pipe", options.stderr ?? "inherit"], windowsHide: true, shell: true, })