codev / src /server /__tests__ /proxy-network-settings.test.ts
chenbhao's picture
feat: desktop
1f21206
Raw
History Blame Contribute Delete
7.55 kB
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test'
import * as fs from 'fs/promises'
import * as os from 'os'
import * as path from 'path'
import { handleProxyRequest } from '../proxy/handler.js'
import { ProviderService } from '../services/providerService.js'
import { resetSettingsCache } from '../../utils/settings/settingsCache.js'
let tmpDir: string
let originalConfigDir: string | undefined
async function setup() {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'proxy-network-test-'))
originalConfigDir = process.env.CLAUDE_CONFIG_DIR
process.env.CLAUDE_CONFIG_DIR = tmpDir
resetSettingsCache()
}
async function teardown() {
if (originalConfigDir !== undefined) {
process.env.CLAUDE_CONFIG_DIR = originalConfigDir
} else {
delete process.env.CLAUDE_CONFIG_DIR
}
resetSettingsCache()
await fs.rm(tmpDir, { recursive: true, force: true })
}
describe('proxy network settings', () => {
beforeEach(setup)
afterEach(teardown)
test('uses configured AI request timeout for non-stream upstream requests', async () => {
await fs.writeFile(
path.join(tmpDir, 'settings.json'),
JSON.stringify({
network: {
aiRequestTimeoutMs: 45_000,
proxy: { mode: 'system', url: '' },
},
}),
'utf-8',
)
const svc = new ProviderService()
const provider = await svc.addProvider({
presetId: 'custom',
name: 'OpenAI Proxy',
baseUrl: 'https://api.example.com',
apiKey: 'sk-test',
apiFormat: 'openai_chat',
models: {
main: 'model-main',
haiku: 'model-main',
sonnet: 'model-main',
opus: 'model-main',
},
})
const originalFetch = globalThis.fetch
const originalTimeout = AbortSignal.timeout
const timeoutCalls: number[] = []
globalThis.fetch = mock(async (_url: string | URL | Request, _init?: RequestInit) => {
return new Response(JSON.stringify({
id: 'chatcmpl-network-timeout',
object: 'chat.completion',
created: 0,
model: 'model-main',
choices: [{
index: 0,
message: { role: 'assistant', content: 'ok' },
finish_reason: 'stop',
}],
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
}), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
}) as typeof fetch
AbortSignal.timeout = ((ms: number) => {
timeoutCalls.push(ms)
return originalTimeout(ms)
}) as typeof AbortSignal.timeout
try {
const body = {
model: 'model-main',
max_tokens: 64,
messages: [{ role: 'user', content: 'hello' }],
}
const req = new Request(
`http://localhost:3456/proxy/providers/${provider.id}/v1/messages`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
},
)
const res = await handleProxyRequest(req, new URL(req.url))
expect(res.status).toBe(200)
expect(timeoutCalls).toEqual([45_000])
} finally {
AbortSignal.timeout = originalTimeout
globalThis.fetch = originalFetch
}
})
test('uses configured AI request timeout for non-stream Responses upstream requests', async () => {
await fs.writeFile(
path.join(tmpDir, 'settings.json'),
JSON.stringify({
network: {
aiRequestTimeoutMs: 45_000,
proxy: { mode: 'system', url: '' },
},
}),
'utf-8',
)
const svc = new ProviderService()
const provider = await svc.addProvider({
presetId: 'custom',
name: 'OpenAI Responses Proxy',
baseUrl: 'https://api.example.com',
apiKey: 'sk-test',
apiFormat: 'openai_responses',
models: {
main: 'model-main',
haiku: 'model-main',
sonnet: 'model-main',
opus: 'model-main',
},
})
const originalFetch = globalThis.fetch
const originalTimeout = AbortSignal.timeout
const timeoutCalls: number[] = []
globalThis.fetch = mock(async (_url: string | URL | Request, _init?: RequestInit) => {
return new Response(JSON.stringify({
id: 'resp-network-timeout',
status: 'completed',
model: 'model-main',
output: [{
type: 'message',
content: [{ type: 'output_text', text: 'ok' }],
}],
usage: { input_tokens: 1, output_tokens: 1 },
}), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
}) as typeof fetch
AbortSignal.timeout = ((ms: number) => {
timeoutCalls.push(ms)
return originalTimeout(ms)
}) as typeof AbortSignal.timeout
try {
const body = {
model: 'model-main',
max_tokens: 64,
messages: [{ role: 'user', content: 'hello' }],
}
const req = new Request(
`http://localhost:3456/proxy/providers/${provider.id}/v1/messages`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
},
)
const res = await handleProxyRequest(req, new URL(req.url))
expect(res.status).toBe(200)
expect(timeoutCalls).toEqual([45_000])
} finally {
AbortSignal.timeout = originalTimeout
globalThis.fetch = originalFetch
}
})
test('uses configured AI request timeout for streaming upstream requests', async () => {
await fs.writeFile(
path.join(tmpDir, 'settings.json'),
JSON.stringify({
network: {
aiRequestTimeoutMs: 180_000,
proxy: { mode: 'system', url: '' },
},
}),
'utf-8',
)
const svc = new ProviderService()
const provider = await svc.addProvider({
presetId: 'custom',
name: 'OpenAI Proxy',
baseUrl: 'https://api.example.com',
apiKey: 'sk-test',
apiFormat: 'openai_chat',
models: {
main: 'model-main',
haiku: 'model-main',
sonnet: 'model-main',
opus: 'model-main',
},
})
const originalFetch = globalThis.fetch
const originalTimeout = AbortSignal.timeout
const timeoutCalls: number[] = []
globalThis.fetch = mock(async (_url: string | URL | Request, _init?: RequestInit) => {
return new Response(
new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode('data: [DONE]\n\n'))
controller.close()
},
}),
{
status: 200,
headers: { 'Content-Type': 'text/event-stream' },
},
)
}) as typeof fetch
AbortSignal.timeout = ((ms: number) => {
timeoutCalls.push(ms)
return originalTimeout(ms)
}) as typeof AbortSignal.timeout
try {
const body = {
model: 'model-main',
max_tokens: 64,
stream: true,
messages: [{ role: 'user', content: 'hello' }],
}
const req = new Request(
`http://localhost:3456/proxy/providers/${provider.id}/v1/messages`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
},
)
const res = await handleProxyRequest(req, new URL(req.url))
expect(res.status).toBe(200)
expect(timeoutCalls).toEqual([180_000])
} finally {
AbortSignal.timeout = originalTimeout
globalThis.fetch = originalFetch
}
})
})