import { test, expect, vi } from "vitest"; import { EventEmitter } from "node:events"; // Mock node:child_process so the PRODUCTION spawnFn (no injected spawnFn) is exercised // without launching a real pi. Per-file mock — kept out of pi-process-manager.test.ts, // which deliberately spawns the real fake_pi. vi.mock("node:child_process", () => ({ spawn: vi.fn(() => { const ch: any = new EventEmitter(); ch.stdout = new EventEmitter(); ch.stderr = new EventEmitter(); ch.stderr.resume = () => {}; ch.stdin = { write: () => true }; ch.kill = () => {}; return ch; }), })); import { spawn as nodeSpawn } from "node:child_process"; import { PiProcessManager } from "../src/pi/pi-process-manager.js"; import { loadConfig } from "../src/config.js"; test("production spawnFn launches `pi --mode rpc` with no --approve (pi 0.73 dropped it)", async () => { (nodeSpawn as any).mockClear(); // loadConfig({}) leaves provider/model/thinking unset → spawnFor skips the rpc // handshake and just spawns + sends the prompt, so the mocked child never hangs. const cfg = loadConfig({}); const mgr = new PiProcessManager(cfg); // NO injected spawnFn → uses the production spawnFn await mgr.spawnFor("s1", {}); const [bin, args] = (nodeSpawn as any).mock.calls[0]; expect(bin).toBe(cfg.piBin); expect(args).toEqual(["--mode", "rpc"]); expect(args).not.toContain("--approve"); mgr.teardown("s1"); });