import { afterEach, beforeEach, describe, expect, it, spyOn } from 'bun:test'; import * as childProcess from 'child_process'; import { EventEmitter } from 'events'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import { CodexAdapter } from '../../../src/targets/codex-adapter'; import { buildCodexBrowserMcpOverrides } from '../../../src/utils/browser-codex-overrides'; import * as signalForwarder from '../../../src/utils/signal-forwarder'; function createMockChild(): EventEmitter & { stdout: EventEmitter; stderr: EventEmitter; exitCode: number | null; killed: boolean; pid: number; unref: () => EventEmitter; kill: () => boolean; } { const child = new EventEmitter() as EventEmitter & { stdout: EventEmitter; stderr: EventEmitter; exitCode: number | null; killed: boolean; pid: number; unref: () => EventEmitter; kill: () => boolean; }; child.stdout = new EventEmitter(); child.stderr = new EventEmitter(); child.exitCode = null; child.killed = false; child.pid = process.pid; child.unref = () => child; child.kill = () => { child.killed = true; child.exitCode = 1; return true; }; return child; } describe('codex-adapter exec', () => { const originalPlatform = process.platform; let tmpDir: string; beforeEach(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-codex-adapter-exec-')); }); afterEach(() => { Object.defineProperty(process, 'platform', { value: originalPlatform }); fs.rmSync(tmpDir, { recursive: true, force: true }); }); it('launches Windows cmd wrappers via cmd.exe when runtime overrides include browser MCP args', () => { Object.defineProperty(process, 'platform', { value: 'win32' }); const fakeCodex = path.join(tmpDir, 'codex.cmd'); fs.writeFileSync(fakeCodex, ''); const spawnSpy = spyOn(childProcess, 'spawn').mockImplementation( () => createMockChild() as unknown as ReturnType ); const signalSpy = spyOn(signalForwarder, 'wireChildProcessSignals').mockImplementation( () => undefined ); try { const adapter = new CodexAdapter(); const binaryInfo = { path: fakeCodex, needsShell: true, features: ['config-overrides'], }; const args = adapter.buildArgs('default', ['--version'], { profileType: 'default', creds: { profile: 'default', baseUrl: '', apiKey: '', runtimeConfigOverrides: buildCodexBrowserMcpOverrides(), }, binaryInfo, }); adapter.exec(args, {}, { binaryInfo }); expect(spawnSpy).toHaveBeenCalledTimes(1); const [command, options] = spawnSpy.mock.calls[0] as [ string, Record | undefined, ]; expect(options?.shell).toBe('cmd.exe'); expect(command).toContain(fakeCodex); expect(command).toContain('mcp_servers.ccs_browser.args='); expect(command).toContain('@playwright/mcp@0.0.70'); } finally { spawnSpy.mockRestore(); signalSpy.mockRestore(); } }); });