cc-haha/src/server/__tests__/provider-runtime-env.test.ts
程序员阿江(Relakkes) 3a42ca2067 Prevent stale provider proxy env in direct TUI launches
Direct TUI launches read cc-haha managed settings without the desktop host's session-env injection, so a stale proxy URL could survive after switching to an Anthropic-compatible provider. The provider runtime env calculation now has one shared source of truth and CLI config loading recomputes managed provider env from the active provider index before applying it.

Constraint: Direct source-based TUI usage must keep working with .env when no desktop provider is configured
Rejected: Inject desktop runtime only from the embedded terminal shell | native terminals and source TUI launches would still read stale managed settings
Confidence: high
Scope-risk: moderate
Directive: Keep provider env derivation shared between ProviderService and CLI managedEnv loading
Tested: bun test src/server/__tests__/provider-runtime-env.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/conversation-service.test.ts
Tested: bun run check:server
Not-tested: Live provider request from embedded terminal TUI
2026-05-20 00:11:22 +08:00

106 lines
3.1 KiB
TypeScript

import { describe, test, expect, beforeEach, afterEach } from 'bun:test'
import * as fs from 'fs/promises'
import * as os from 'os'
import * as path from 'path'
import {
mergeActiveProviderManagedEnv,
readActiveProviderManagedEnv,
} from '../services/providerRuntimeEnv.js'
let tmpDir: string
async function writeJson(filePath: string, value: unknown): Promise<void> {
await fs.mkdir(path.dirname(filePath), { recursive: true })
await fs.writeFile(filePath, JSON.stringify(value, null, 2), 'utf-8')
}
describe('providerRuntimeEnv', () => {
beforeEach(async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'provider-runtime-env-'))
})
afterEach(async () => {
await fs.rm(tmpDir, { recursive: true, force: true })
})
test('derives native Anthropic provider env from the active provider index', async () => {
await writeJson(path.join(tmpDir, 'cc-haha', 'providers.json'), {
activeId: 'provider-1',
providers: [
{
id: 'provider-1',
presetId: 'custom',
name: 'Active Provider',
apiKey: 'sk-active',
authStrategy: 'auth_token',
baseUrl: 'https://api.example.com/anthropic',
apiFormat: 'anthropic',
models: {
main: 'active-main',
haiku: '',
sonnet: 'active-sonnet',
opus: '',
},
},
],
})
const env = readActiveProviderManagedEnv(tmpDir)
expect(env).toMatchObject({
ANTHROPIC_BASE_URL: 'https://api.example.com/anthropic',
ANTHROPIC_API_KEY: '',
ANTHROPIC_AUTH_TOKEN: 'sk-active',
ANTHROPIC_MODEL: 'active-main',
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'active-main',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'active-sonnet',
ANTHROPIC_DEFAULT_OPUS_MODEL: 'active-main',
})
})
test('active provider env overrides stale proxy settings while preserving unrelated env', async () => {
await writeJson(path.join(tmpDir, 'cc-haha', 'providers.json'), {
activeId: 'provider-1',
providers: [
{
id: 'provider-1',
presetId: 'custom',
name: 'Sub2API',
apiKey: 'sk-sub2api',
authStrategy: 'auth_token',
baseUrl: 'https://sub2api.example.com',
apiFormat: 'anthropic',
models: {
main: 'gpt-5.5',
haiku: 'gpt-5.5',
sonnet: 'gpt-5.5',
opus: 'gpt-5.5',
},
},
],
})
const env = mergeActiveProviderManagedEnv(
{
ANTHROPIC_BASE_URL: 'http://127.0.0.1:3456/proxy',
ANTHROPIC_API_KEY: 'proxy-managed',
ANTHROPIC_MODEL: 'deepseek-v4-pro',
DISABLE_AUTOUPDATER: '1',
},
tmpDir,
)
expect(env).toMatchObject({
ANTHROPIC_BASE_URL: 'https://sub2api.example.com',
ANTHROPIC_API_KEY: '',
ANTHROPIC_AUTH_TOKEN: 'sk-sub2api',
ANTHROPIC_MODEL: 'gpt-5.5',
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'gpt-5.5',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'gpt-5.5',
ANTHROPIC_DEFAULT_OPUS_MODEL: 'gpt-5.5',
DISABLE_AUTOUPDATER: '1',
})
})
})