mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 22:16:41 +00:00
Use ComSpec-aware shell selection for escaped wrapper launches. Apply it to the remaining Windows Claude launch paths. Rewrite the Codex exec regression test to use scoped spies so the full suite stays isolated.
107 lines
3.1 KiB
TypeScript
107 lines
3.1 KiB
TypeScript
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<typeof childProcess.spawn>
|
|
);
|
|
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<string, unknown> | 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();
|
|
}
|
|
});
|
|
});
|