fix: guard rawBaseUrl against the literal string "undefined" from env vars (#340)
On Windows, shells can set OPENAI_BASE_URL to the literal string "undefined" when the variable is referenced without quotes while unset. The nullish-coalescing operator (??) does not catch this because "undefined" is a truthy string, causing resolveProviderRequest() to treat it as a real base URL. This broke the Codex transport check: (!rawBaseUrl && isCodexAlias(model)) evaluated as (false || true) = false so the transport was incorrectly set to chat_completions (issue #336). Fix: introduce asEnvUrl() which trims the value and rejects both empty strings and the sentinel string "undefined". Use it for all three rawBaseUrl sources (options.baseUrl, OPENAI_BASE_URL, OPENAI_API_BASE). Tests: add three new cases to the 'Codex provider config' describe block covering the empty-string, "undefined"-string, and options-override scenarios. Also add beforeEach/afterEach guards so individual tests cannot contaminate each other via env var state. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { afterEach, describe, expect, test } from 'bun:test'
|
||||
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
|
||||
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { tmpdir } from 'node:os'
|
||||
@@ -46,6 +46,21 @@ async function collectStreamEventTypes(responseText: string): Promise<string[]>
|
||||
}
|
||||
|
||||
describe('Codex provider config', () => {
|
||||
const originalOpenaiBaseUrl = process.env.OPENAI_BASE_URL
|
||||
const originalOpenaiApiBase = process.env.OPENAI_API_BASE
|
||||
|
||||
beforeEach(() => {
|
||||
delete process.env.OPENAI_BASE_URL
|
||||
delete process.env.OPENAI_API_BASE
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
if (originalOpenaiBaseUrl === undefined) delete process.env.OPENAI_BASE_URL
|
||||
else process.env.OPENAI_BASE_URL = originalOpenaiBaseUrl
|
||||
if (originalOpenaiApiBase === undefined) delete process.env.OPENAI_API_BASE
|
||||
else process.env.OPENAI_API_BASE = originalOpenaiApiBase
|
||||
})
|
||||
|
||||
test('resolves codexplan alias to Codex transport with reasoning', () => {
|
||||
const resolved = resolveProviderRequest({ model: 'codexplan' })
|
||||
expect(resolved.transport).toBe('codex_responses')
|
||||
@@ -53,6 +68,27 @@ describe('Codex provider config', () => {
|
||||
expect(resolved.reasoning).toEqual({ effort: 'high' })
|
||||
})
|
||||
|
||||
test('resolves codexplan to Codex transport even when OPENAI_BASE_URL is the string "undefined"', () => {
|
||||
// On Windows, env vars can leak as the literal string "undefined" instead of
|
||||
// the JS value undefined when not properly unset (issue #336).
|
||||
process.env.OPENAI_BASE_URL = 'undefined'
|
||||
const resolved = resolveProviderRequest({ model: 'codexplan' })
|
||||
expect(resolved.transport).toBe('codex_responses')
|
||||
})
|
||||
|
||||
test('resolves codexplan to Codex transport even when OPENAI_BASE_URL is an empty string', () => {
|
||||
process.env.OPENAI_BASE_URL = ''
|
||||
const resolved = resolveProviderRequest({ model: 'codexplan' })
|
||||
expect(resolved.transport).toBe('codex_responses')
|
||||
})
|
||||
|
||||
test('prefers explicit baseUrl option over env var', () => {
|
||||
process.env.OPENAI_BASE_URL = 'https://example.com/v1'
|
||||
const resolved = resolveProviderRequest({ model: 'codexplan', baseUrl: 'https://chatgpt.com/backend-api/codex' })
|
||||
expect(resolved.transport).toBe('codex_responses')
|
||||
expect(resolved.baseUrl).toBe('https://chatgpt.com/backend-api/codex')
|
||||
})
|
||||
|
||||
test('loads Codex credentials from auth.json fallback', () => {
|
||||
const authPath = createTempAuthJson({
|
||||
tokens: {
|
||||
|
||||
Reference in New Issue
Block a user