mirror of
https://github.com/NanmiCoder/claude-code-haha.git
synced 2026-10-10 03:43:11 +08:00
435e4ccc9f
Route required checks by changed surface, add offline provider and chat contracts, and keep fork PRs independent of live credentials. Layer agent guidance by subtree and enforce a compact instruction budget. Tested: bun run check:policy Confidence: high Scope-risk: broad
182 lines
6.7 KiB
TypeScript
182 lines
6.7 KiB
TypeScript
import { describe, expect, mock, test } from 'bun:test'
|
|
|
|
mock.module('src/utils/http.js', () => ({
|
|
getAuthHeaders: mock(() => ({})),
|
|
getMCPUserAgent: mock(() => 'client-test-agent'),
|
|
getUserAgent: mock(() => 'client-test-agent'),
|
|
getWebFetchUserAgent: mock(() => 'client-test-agent'),
|
|
withOAuth401Retry: mock(async <T>(fn: () => Promise<T>) => fn()),
|
|
}))
|
|
|
|
describe('resolveAnthropicClientApiKey', () => {
|
|
test('does not inherit a local api key when a provider auth token is explicit', async () => {
|
|
const { resolveAnthropicClientApiKey } = await import('./client.js')
|
|
const getFallbackApiKey = mock(() => 'sk-keychain-fallback')
|
|
|
|
const apiKey = resolveAnthropicClientApiKey({
|
|
envAuthToken: 'provider-bearer-token',
|
|
envApiKey: '',
|
|
getFallbackApiKey,
|
|
})
|
|
|
|
expect(apiKey).toBeNull()
|
|
expect(getFallbackApiKey).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('preserves an explicit api key when the caller opts into dual auth', async () => {
|
|
const { resolveAnthropicClientApiKey } = await import('./client.js')
|
|
const getFallbackApiKey = mock(() => 'sk-keychain-fallback')
|
|
|
|
const apiKey = resolveAnthropicClientApiKey({
|
|
explicitApiKey: 'sk-explicit-api-key',
|
|
envAuthToken: 'provider-bearer-token',
|
|
getFallbackApiKey,
|
|
})
|
|
|
|
expect(apiKey).toBe('sk-explicit-api-key')
|
|
expect(getFallbackApiKey).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('falls back to the local api key when no provider auth token is present', async () => {
|
|
const { resolveAnthropicClientApiKey } = await import('./client.js')
|
|
const getFallbackApiKey = mock(() => 'sk-keychain-fallback')
|
|
|
|
const apiKey = resolveAnthropicClientApiKey({
|
|
envAuthToken: '',
|
|
envApiKey: '',
|
|
getFallbackApiKey,
|
|
})
|
|
|
|
expect(apiKey).toBe('sk-keychain-fallback')
|
|
expect(getFallbackApiKey).toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
describe('shouldUseOpenAICodexTransport', () => {
|
|
test('lets ChatGPT Official marker override a saved Claude subscriber login', async () => {
|
|
const { shouldUseOpenAICodexTransport } = await import('./client.js')
|
|
|
|
expect(shouldUseOpenAICodexTransport({
|
|
hasOpenAIAuth: true,
|
|
isClaudeSubscriber: true,
|
|
forceOpenAICodex: true,
|
|
isOpenAIModel: true,
|
|
hasAnthropicAuthToken: false,
|
|
hasExplicitApiKey: false,
|
|
hasFallbackApiKey: false,
|
|
})).toBe(true)
|
|
})
|
|
|
|
test('keeps Claude subscriber transport when ChatGPT Official is not selected', async () => {
|
|
const { shouldUseOpenAICodexTransport } = await import('./client.js')
|
|
|
|
expect(shouldUseOpenAICodexTransport({
|
|
hasOpenAIAuth: true,
|
|
isClaudeSubscriber: true,
|
|
forceOpenAICodex: false,
|
|
isOpenAIModel: true,
|
|
hasAnthropicAuthToken: false,
|
|
hasExplicitApiKey: false,
|
|
hasFallbackApiKey: false,
|
|
})).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('getAnthropicClient', () => {
|
|
test('passes bearer-token provider auth without an SDK api key', async () => {
|
|
const { getAnthropicClient } = await import('./client.js')
|
|
const originalAuthToken = process.env.ANTHROPIC_AUTH_TOKEN
|
|
const originalApiKey = process.env.ANTHROPIC_API_KEY
|
|
const originalSimple = process.env.CLAUDE_CODE_SIMPLE
|
|
|
|
process.env.ANTHROPIC_AUTH_TOKEN = 'provider-bearer-token'
|
|
process.env.CLAUDE_CODE_SIMPLE = '1'
|
|
delete process.env.ANTHROPIC_API_KEY
|
|
|
|
try {
|
|
const client = await getAnthropicClient({
|
|
maxRetries: 0,
|
|
model: 'claude-sonnet-4-6',
|
|
})
|
|
|
|
expect(client.apiKey).toBeNull()
|
|
expect(client._options.defaultHeaders).toMatchObject({
|
|
Authorization: 'Bearer provider-bearer-token',
|
|
})
|
|
} finally {
|
|
if (originalAuthToken === undefined) delete process.env.ANTHROPIC_AUTH_TOKEN
|
|
else process.env.ANTHROPIC_AUTH_TOKEN = originalAuthToken
|
|
|
|
if (originalApiKey === undefined) delete process.env.ANTHROPIC_API_KEY
|
|
else process.env.ANTHROPIC_API_KEY = originalApiKey
|
|
|
|
if (originalSimple === undefined) delete process.env.CLAUDE_CODE_SIMPLE
|
|
else process.env.CLAUDE_CODE_SIMPLE = originalSimple
|
|
}
|
|
})
|
|
|
|
test('bypasses system proxy for local desktop provider proxy base URLs', async () => {
|
|
const { getAnthropicClient } = await import('./client.js')
|
|
const originalAuthToken = process.env.ANTHROPIC_AUTH_TOKEN
|
|
const originalApiKey = process.env.ANTHROPIC_API_KEY
|
|
const originalBaseUrl = process.env.ANTHROPIC_BASE_URL
|
|
const originalHttpProxy = process.env.HTTP_PROXY
|
|
const originalHttpsProxy = process.env.HTTPS_PROXY
|
|
const originalNoProxy = process.env.NO_PROXY
|
|
const originalLowerHttpProxy = process.env.http_proxy
|
|
const originalLowerHttpsProxy = process.env.https_proxy
|
|
const originalLowerNoProxy = process.env.no_proxy
|
|
const originalSimple = process.env.CLAUDE_CODE_SIMPLE
|
|
|
|
process.env.ANTHROPIC_AUTH_TOKEN = 'provider-bearer-token'
|
|
process.env.ANTHROPIC_BASE_URL = 'http://127.0.0.1:3456/proxy/providers/provider-1'
|
|
process.env.HTTP_PROXY = 'http://127.0.0.1:1181'
|
|
process.env.HTTPS_PROXY = 'http://127.0.0.1:1181'
|
|
process.env.NO_PROXY = 'localhost,127.0.0.1,::1'
|
|
process.env.CLAUDE_CODE_SIMPLE = '1'
|
|
delete process.env.ANTHROPIC_API_KEY
|
|
delete process.env.http_proxy
|
|
delete process.env.https_proxy
|
|
delete process.env.no_proxy
|
|
|
|
try {
|
|
const client = await getAnthropicClient({
|
|
maxRetries: 0,
|
|
model: 'deepseek-v4-pro',
|
|
})
|
|
|
|
expect(client._options.fetchOptions?.proxy).toBeUndefined()
|
|
} finally {
|
|
if (originalAuthToken === undefined) delete process.env.ANTHROPIC_AUTH_TOKEN
|
|
else process.env.ANTHROPIC_AUTH_TOKEN = originalAuthToken
|
|
|
|
if (originalApiKey === undefined) delete process.env.ANTHROPIC_API_KEY
|
|
else process.env.ANTHROPIC_API_KEY = originalApiKey
|
|
|
|
if (originalBaseUrl === undefined) delete process.env.ANTHROPIC_BASE_URL
|
|
else process.env.ANTHROPIC_BASE_URL = originalBaseUrl
|
|
|
|
if (originalHttpProxy === undefined) delete process.env.HTTP_PROXY
|
|
else process.env.HTTP_PROXY = originalHttpProxy
|
|
|
|
if (originalHttpsProxy === undefined) delete process.env.HTTPS_PROXY
|
|
else process.env.HTTPS_PROXY = originalHttpsProxy
|
|
|
|
if (originalNoProxy === undefined) delete process.env.NO_PROXY
|
|
else process.env.NO_PROXY = originalNoProxy
|
|
|
|
if (originalLowerHttpProxy === undefined) delete process.env.http_proxy
|
|
else process.env.http_proxy = originalLowerHttpProxy
|
|
|
|
if (originalLowerHttpsProxy === undefined) delete process.env.https_proxy
|
|
else process.env.https_proxy = originalLowerHttpsProxy
|
|
|
|
if (originalLowerNoProxy === undefined) delete process.env.no_proxy
|
|
else process.env.no_proxy = originalLowerNoProxy
|
|
|
|
if (originalSimple === undefined) delete process.env.CLAUDE_CODE_SIMPLE
|
|
else process.env.CLAUDE_CODE_SIMPLE = originalSimple
|
|
}
|
|
})
|
|
})
|