|
|
@@ -0,0 +1,69 @@
|
|
|
+import { createServer, type Server } from 'node:http'
|
|
|
+import type { AddressInfo } from 'node:net'
|
|
|
+import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
|
|
+import { installGlobalProxy, type ProxyPolicy } from '@deepseek-ai/dsh-http-proxy'
|
|
|
+
|
|
|
+let seen: string[] = []
|
|
|
+let proxy: Server
|
|
|
+let proxyUrl: string
|
|
|
+
|
|
|
+beforeAll(async () => {
|
|
|
+ proxy = createServer((request, response) => {
|
|
|
+ seen.push(`REQ ${request.url ?? ''}`)
|
|
|
+ response.writeHead(502); response.end('fake-proxy')
|
|
|
+ })
|
|
|
+ proxy.on('connect', (request, socket) => {
|
|
|
+ seen.push(`CONNECT ${request.url ?? ''}`)
|
|
|
+ socket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n'); socket.end()
|
|
|
+ })
|
|
|
+ const a = await new Promise<AddressInfo>((r) => { proxy.listen(0, '127.0.0.1', () => { r(proxy.address() as AddressInfo) }) })
|
|
|
+ proxyUrl = `http://127.0.0.1:${String(a.port)}`
|
|
|
+})
|
|
|
+afterAll(async () => { await new Promise<void>((r) => { proxy.close(() => { r() }) }) })
|
|
|
+
|
|
|
+function policy(): ProxyPolicy {
|
|
|
+ return { httpProxy: proxyUrl, httpsProxy: proxyUrl, noProxy: '', source: 'env' }
|
|
|
+}
|
|
|
+async function observe(run: () => Promise<unknown>): Promise<string[]> {
|
|
|
+ seen = []
|
|
|
+ const dispose = await installGlobalProxy(policy())
|
|
|
+ try { await run().catch(() => undefined) } finally { await dispose() }
|
|
|
+ return seen
|
|
|
+}
|
|
|
+import { mkdtempSync, rmSync } from 'node:fs'
|
|
|
+import { tmpdir } from 'node:os'
|
|
|
+import { join } from 'node:path'
|
|
|
+import { vi } from 'vitest'
|
|
|
+import { Context } from '@deepseek-ai/cordis'
|
|
|
+import LlmRuntime from '@deepseek-ai/dsh-llm'
|
|
|
+import DeepSeekLlmApiExtensionRegistry from '@deepseek-ai/dsh-deepseek-llm-api-extensions'
|
|
|
+import * as LlmDeepSeek from '../src/index.ts'
|
|
|
+
|
|
|
+let home: string
|
|
|
+beforeAll(() => {
|
|
|
+ home = mkdtempSync(join(tmpdir(), 'dsh-deepseek-egress-'))
|
|
|
+ vi.stubEnv('DSH_HOME', home)
|
|
|
+ vi.stubEnv('DEEPSEEK_API_KEY', 'probe-key')
|
|
|
+})
|
|
|
+afterAll(() => {
|
|
|
+ vi.unstubAllEnvs()
|
|
|
+ rmSync(home, { recursive: true, force: true })
|
|
|
+})
|
|
|
+
|
|
|
+/** Drive the shipping adapter's chat-completions request at an unresolvable endpoint. */
|
|
|
+async function streamOnce(): Promise<void> {
|
|
|
+ const ctx = new Context()
|
|
|
+ await ctx.plugin(LlmRuntime)
|
|
|
+ await ctx.plugin(DeepSeekLlmApiExtensionRegistry)
|
|
|
+ await ctx.plugin(LlmDeepSeek, { baseURL: 'http://deepseek-probe.invalid/v1', models: [{ id: 'm' }] })
|
|
|
+ for await (const _chunk of ctx.llm.stream({ provider: 'deepseek-official', model: 'm', messages: [] })) {
|
|
|
+ // The endpoint never answers; the proxy record is the assertion.
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+describe('llm-deepseek egress', () => {
|
|
|
+ it('sends the chat-completions request through the proxy', async () => {
|
|
|
+ const observed = await observe(streamOnce)
|
|
|
+ expect(observed.join('|')).toContain('deepseek-probe.invalid')
|
|
|
+ })
|
|
|
+})
|