import { describe, expect, test } from 'bun:test'; import { CodexAdapter } from '../../../src/targets/codex-adapter'; import { buildCodexBrowserMcpOverrides, getCodexBrowserMcpServerName, } from '../../../src/utils/browser-codex-overrides'; describe('CodexAdapter', () => { const adapter = new CodexAdapter(); test('supports only adapter-level default and cliproxy profile types', () => { expect(adapter.supportsProfileType('default')).toBe(true); expect(adapter.supportsProfileType('cliproxy')).toBe(true); expect(adapter.supportsProfileType('settings')).toBe(false); expect(adapter.supportsProfileType('account')).toBe(false); expect(adapter.supportsProfileType('copilot')).toBe(false); }); test('passes default-mode args through unchanged', () => { expect( adapter.buildArgs('default', ['--search'], { profileType: 'default', }) ).toEqual(['--search']); }); test('builds browser MCP overrides with Codex-safe defaults', () => { const serverName = getCodexBrowserMcpServerName(); expect(buildCodexBrowserMcpOverrides()).toEqual([ `mcp_servers.${serverName}.command=${JSON.stringify(process.platform === 'win32' ? 'npx.cmd' : 'npx')}`, `mcp_servers.${serverName}.args=${JSON.stringify(['-y', '@playwright/mcp@0.0.70'])}`, `mcp_servers.${serverName}.enabled=true`, `mcp_servers.${serverName}.tool_timeout_sec=30`, ]); }); test('translates default-mode reasoning overrides into transient codex config', () => { const args = adapter.buildArgs('default', ['--search'], { profileType: 'default', creds: { profile: 'default', baseUrl: '', apiKey: '', reasoningOverride: 'medium', }, binaryInfo: { path: '/tmp/codex', needsShell: false, features: ['config-overrides'], }, }); expect(args).toEqual(['-c', 'model_reasoning_effort="medium"', '--search']); }); test('injects runtime config overrides for native Codex default launches', () => { const runtimeConfigOverrides = buildCodexBrowserMcpOverrides(); const args = adapter.buildArgs('default', ['--search'], { profileType: 'default', creds: { profile: 'default', baseUrl: '', apiKey: '', runtimeConfigOverrides, }, binaryInfo: { path: '/tmp/codex', needsShell: false, features: ['config-overrides'], }, }); expect(args).toEqual([ ...runtimeConfigOverrides.flatMap((override) => ['-c', override]), '--search', ]); }); test('rejects default-mode reasoning overrides when codex lacks config override support', () => { expect(() => adapter.buildArgs('default', ['--search'], { profileType: 'default', creds: { profile: 'default', baseUrl: '', apiKey: '', reasoningOverride: 'high', }, binaryInfo: { path: '/tmp/codex', needsShell: false, version: 'codex-cli 0.1.0', features: [], }, }) ).toThrow(/does not advertise --config overrides/); }); test('injects transient config overrides for CCS-backed launches', () => { const runtimeConfigOverrides = buildCodexBrowserMcpOverrides(); const args = adapter.buildArgs('codex', ['--search'], { profileType: 'cliproxy', creds: { profile: 'codex', baseUrl: 'http://127.0.0.1:8317/api/provider/codex', apiKey: 'cliproxy-token', model: 'gpt-5.4', reasoningOverride: 'high', runtimeConfigOverrides, }, binaryInfo: { path: '/tmp/codex', needsShell: false, features: ['config-overrides'], }, }); expect(args).toContain('-c'); expect(args).toContain('model_provider="ccs_runtime"'); expect(args).toContain('model_providers.ccs_runtime.env_key="CCS_CODEX_API_KEY"'); expect(args).toContain('model="gpt-5.4"'); expect(args).toContain(`mcp_servers.${getCodexBrowserMcpServerName()}.command=${JSON.stringify(process.platform === 'win32' ? 'npx.cmd' : 'npx')}`); expect(args).toContain('model_reasoning_effort="high"'); expect(args[args.length - 1]).toBe('--search'); }); test('fails fast when Codex binary lacks config override support', () => { expect(() => adapter.buildArgs('codex', [], { profileType: 'cliproxy', creds: { profile: 'codex', baseUrl: 'http://127.0.0.1:8317/api/provider/codex', apiKey: 'cliproxy-token', }, binaryInfo: { path: '/tmp/codex', needsShell: false, version: 'codex-cli 0.1.0', features: [], }, }) ).toThrow(/does not advertise --config overrides/); }); test('rejects native Codex provider-selection flags for CCS-backed launches', () => { expect(() => adapter.buildArgs('codex', ['--profile', 'other', '--search'], { profileType: 'cliproxy', creds: { profile: 'codex', baseUrl: 'http://127.0.0.1:8317/api/provider/codex', apiKey: 'cliproxy-token', }, binaryInfo: { path: '/tmp/codex', needsShell: false, features: ['config-overrides'], }, }) ).toThrow(/does not allow --profile\/-p/); }); test('rejects user-supplied --config overrides for CCS-backed launches', () => { const options = { profileType: 'cliproxy' as const, creds: { profile: 'codex', baseUrl: 'http://127.0.0.1:8317/api/provider/codex', apiKey: 'cliproxy-token', }, binaryInfo: { path: '/tmp/codex', needsShell: false, features: ['config-overrides'], }, }; expect(() => adapter.buildArgs('codex', ['-c', 'model="other"', '--search'], options)).toThrow( /does not allow --config\/-c/ ); expect(() => adapter.buildArgs('codex', ['--config=model="other"', '--search'], options) ).toThrow(/does not allow --config\/-c/); }); test('rejects unsupported reasoning override values for CCS-backed launches', () => { expect(() => adapter.buildArgs('codex', ['--search'], { profileType: 'cliproxy', creds: { profile: 'codex', baseUrl: 'http://127.0.0.1:8317/api/provider/codex', apiKey: 'cliproxy-token', reasoningOverride: 8192, }, binaryInfo: { path: '/tmp/codex', needsShell: false, features: ['config-overrides'], }, }) ).toThrow(/supports reasoning levels only/); }); test('injects CCS_CODEX_API_KEY for CCS-backed launches only', () => { const originalCodeXHome = process.env.CODEX_HOME; const originalCodeXCi = process.env.CODEX_CI; const originalCodeXManagedByBun = process.env.CODEX_MANAGED_BY_BUN; const originalCodeXThreadId = process.env.CODEX_THREAD_ID; const originalAnthropicBaseUrl = process.env.ANTHROPIC_BASE_URL; try { process.env.CODEX_HOME = '/tmp/codex-home'; process.env.CODEX_CI = '1'; process.env.CODEX_MANAGED_BY_BUN = '1'; process.env.CODEX_THREAD_ID = 'thread-123'; process.env.ANTHROPIC_BASE_URL = 'https://stale-proxy.invalid'; const settingsEnv = adapter.buildEnv( { profile: 'codex', baseUrl: 'http://127.0.0.1:8317/api/provider/codex', apiKey: 'cliproxy-token', }, 'cliproxy' ); expect(settingsEnv.CCS_CODEX_API_KEY).toBe('cliproxy-token'); expect(settingsEnv.CODEX_HOME).toBe('/tmp/codex-home'); expect(settingsEnv.CODEX_CI).toBeUndefined(); expect(settingsEnv.CODEX_MANAGED_BY_BUN).toBeUndefined(); expect(settingsEnv.CODEX_THREAD_ID).toBeUndefined(); expect(settingsEnv.ANTHROPIC_BASE_URL).toBeUndefined(); const defaultEnv = adapter.buildEnv( { profile: 'default', baseUrl: '', apiKey: '', }, 'default' ); expect(defaultEnv.CCS_CODEX_API_KEY).toBeUndefined(); expect(defaultEnv.CODEX_HOME).toBe('/tmp/codex-home'); expect(defaultEnv.CODEX_CI).toBeUndefined(); expect(defaultEnv.CODEX_MANAGED_BY_BUN).toBeUndefined(); expect(defaultEnv.CODEX_THREAD_ID).toBeUndefined(); expect(defaultEnv.ANTHROPIC_BASE_URL).toBeUndefined(); } finally { if (originalCodeXHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodeXHome; } if (originalCodeXCi === undefined) { delete process.env.CODEX_CI; } else { process.env.CODEX_CI = originalCodeXCi; } if (originalCodeXManagedByBun === undefined) { delete process.env.CODEX_MANAGED_BY_BUN; } else { process.env.CODEX_MANAGED_BY_BUN = originalCodeXManagedByBun; } if (originalCodeXThreadId === undefined) { delete process.env.CODEX_THREAD_ID; } else { process.env.CODEX_THREAD_ID = originalCodeXThreadId; } if (originalAnthropicBaseUrl === undefined) { delete process.env.ANTHROPIC_BASE_URL; } else { process.env.ANTHROPIC_BASE_URL = originalAnthropicBaseUrl; } } }); });