169 lines
4.7 KiB
TypeScript
169 lines
4.7 KiB
TypeScript
import { EventEmitter } from 'node:events'
|
|
import { mkdir, mkdtemp, realpath, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { PassThrough } from 'node:stream'
|
|
import { describe, expect, it, vi } from 'vitest'
|
|
import {
|
|
DEEPSEEK_HARNESS_CONTROL_PROTOCOL,
|
|
DEEPSEEK_HARNESS_CONTROL_VERSION,
|
|
DEEPSEEK_HARNESS_CREDENTIAL_REF,
|
|
createDeepSeekHarnessUtilityLauncher,
|
|
parseHarnessControlMessage
|
|
} from './deepseek-harness-utility-launcher'
|
|
|
|
class FakeUtility extends EventEmitter {
|
|
readonly messages: unknown[] = []
|
|
readonly stderr = new PassThrough()
|
|
readonly pid = 123
|
|
killed = false
|
|
|
|
postMessage(message: unknown): void {
|
|
this.messages.push(message)
|
|
}
|
|
|
|
kill(): boolean {
|
|
this.killed = true
|
|
return true
|
|
}
|
|
}
|
|
|
|
async function fixture() {
|
|
const root = await realpath(
|
|
await mkdtemp(join(tmpdir(), 'goodbuddy-harness-launcher-'))
|
|
)
|
|
const workspace = join(root, 'workspace')
|
|
const dshHome = join(root, 'home')
|
|
const hostPath = join(
|
|
root,
|
|
'deepseek-harness-host-bootstrap.js'
|
|
)
|
|
await Promise.all([
|
|
mkdir(workspace),
|
|
mkdir(dshHome),
|
|
writeFile(hostPath, '', 'utf8')
|
|
])
|
|
return {
|
|
dshHome,
|
|
hostPath,
|
|
launchOptions: {
|
|
cwd: workspace,
|
|
signal: new AbortController().signal,
|
|
baseUrl: 'https://gateway.example/openai/v1',
|
|
model: 'qwen-plus',
|
|
credentialRefs: [DEEPSEEK_HARNESS_CREDENTIAL_REF],
|
|
skillPackages: []
|
|
}
|
|
}
|
|
}
|
|
|
|
describe('DeepSeek Harness utility launcher', () => {
|
|
it('accepts only strict control messages and secret-free config', () => {
|
|
expect(
|
|
parseHarnessControlMessage({
|
|
protocol: DEEPSEEK_HARNESS_CONTROL_PROTOCOL,
|
|
version: DEEPSEEK_HARNESS_CONTROL_VERSION,
|
|
type: 'ready'
|
|
})
|
|
).toMatchObject({ type: 'ready' })
|
|
expect(
|
|
parseHarnessControlMessage({
|
|
protocol: DEEPSEEK_HARNESS_CONTROL_PROTOCOL,
|
|
version: DEEPSEEK_HARNESS_CONTROL_VERSION,
|
|
type: 'ready',
|
|
apiKey: 'must-not-pass'
|
|
})
|
|
).toBeUndefined()
|
|
})
|
|
|
|
it('waits for Host readiness and sends no credential value', async () => {
|
|
const { dshHome, hostPath, launchOptions } = await fixture()
|
|
const utility = new FakeUtility()
|
|
const fork = vi.fn(() => utility as never)
|
|
const launcher = createDeepSeekHarnessUtilityLauncher({
|
|
bundledHostPath: hostPath,
|
|
dshHome,
|
|
environment: { PATH: 'C:\\Tools' },
|
|
fork
|
|
})
|
|
|
|
const launching = launcher(launchOptions)
|
|
await vi.waitFor(() =>
|
|
expect(utility.messages).toHaveLength(1)
|
|
)
|
|
expect(JSON.stringify(utility.messages[0])).not.toContain(
|
|
'secret'
|
|
)
|
|
expect(utility.messages[0]).toMatchObject({
|
|
type: 'start',
|
|
config: {
|
|
baseUrl: 'https://gateway.example/openai/v1',
|
|
model: 'qwen-plus',
|
|
credentialRefs: [DEEPSEEK_HARNESS_CREDENTIAL_REF]
|
|
}
|
|
})
|
|
utility.emit('message', {
|
|
protocol: DEEPSEEK_HARNESS_CONTROL_PROTOCOL,
|
|
version: DEEPSEEK_HARNESS_CONTROL_VERSION,
|
|
type: 'ready'
|
|
})
|
|
|
|
await expect(launching).resolves.toMatchObject({
|
|
stdin: expect.any(WritableStream),
|
|
stdout: expect.any(ReadableStream)
|
|
})
|
|
expect(fork).toHaveBeenCalledWith(
|
|
hostPath,
|
|
[],
|
|
expect.objectContaining({
|
|
cwd: launchOptions.cwd,
|
|
stdio: ['ignore', 'ignore', 'pipe']
|
|
})
|
|
)
|
|
})
|
|
|
|
it('fails closed on an invalid Host startup message', async () => {
|
|
const { dshHome, hostPath, launchOptions } = await fixture()
|
|
const utility = new FakeUtility()
|
|
const terminateProcess = vi.fn(() => {
|
|
utility.killed = true
|
|
})
|
|
const launcher = createDeepSeekHarnessUtilityLauncher({
|
|
bundledHostPath: hostPath,
|
|
dshHome,
|
|
environment: {},
|
|
fork: () => utility as never,
|
|
terminateProcess
|
|
})
|
|
|
|
const launching = launcher(launchOptions)
|
|
await vi.waitFor(() =>
|
|
expect(utility.messages).toHaveLength(1)
|
|
)
|
|
utility.emit('message', { type: 'ready' })
|
|
|
|
await expect(launching).rejects.toThrow('启动协议无效')
|
|
expect(terminateProcess).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it.each([
|
|
'http://gateway.example/v1',
|
|
'https://user:secret@gateway.example/v1',
|
|
'https://gateway.example/v1?api-version=2025-01-01'
|
|
])('rejects unsafe endpoint %s before forking', async (baseUrl) => {
|
|
const { dshHome, hostPath, launchOptions } = await fixture()
|
|
const fork = vi.fn()
|
|
const launcher = createDeepSeekHarnessUtilityLauncher({
|
|
bundledHostPath: hostPath,
|
|
dshHome,
|
|
environment: {},
|
|
fork
|
|
})
|
|
|
|
await expect(
|
|
launcher({ ...launchOptions, baseUrl })
|
|
).rejects.toThrow('HTTPS')
|
|
expect(fork).not.toHaveBeenCalled()
|
|
})
|
|
})
|