# Conflicts: # docs/architecture.i18n.yaml # docs/architecture.md # docs/architecture.zh.md # docs/config-catalog.md # docs/core-data-structures/core.i18n.yaml # docs/core-data-structures/llm-streaming.i18n.yaml # docs/module-graph.md # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl # packages/README.i18n.yaml # packages/client/connection/src/client/fixture.ts # packages/client/connection/src/index.ts # packages/client/runtime/README.i18n.yaml # packages/client/runtime/README.md # packages/client/runtime/README.zh.md # packages/client/runtime/src/client/sessions/conversation.ts # packages/client/ui-conversation/README.i18n.yaml # packages/client/ui-conversation/src/client/apply.ts # packages/client/ui-conversation/src/client/chat/ChatView.tsx # packages/client/ui-conversation/src/client/chat/MessageItem.tsx # packages/client/ui-conversation/src/client/contract/slots.ts # packages/client/ui-trajectory/tests/views.spec.tsx # packages/compact/compact-basic/README.i18n.yaml # packages/cordis/tool-cordis/src/api-catalog.ts # packages/host/apiproxy/src/api-proxy.ts # packages/host/apiproxy/src/api/index.ts # packages/host/apiproxy/src/api/sessions.ts # packages/host/apiproxy/src/index.ts # packages/host/apiproxy/tests/fetch-carrier.spec.ts # packages/llm/llm-deepseek/src/adapter.ts # packages/llm/llm-deepseek/tests/adapter.spec.ts # packages/llm/llm-deepseek/tests/serialize.spec.ts # packages/llm/llm-pi-ai/README.i18n.yaml # packages/llm/llm-pi-ai/src/adapter.ts # packages/llm/llm-pi-ai/src/index.ts # packages/llm/llm-pi-ai/tests/adapter.spec.ts # packages/llm/llm/src/types.ts # packages/ui/tui/README.i18n.yaml # packages/ui/tui/src/index.ts # packages/ui/tui/tests/tui.spec.ts
56 lines
2.2 KiB
TypeScript
56 lines
2.2 KiB
TypeScript
/** Node half: registers the /api prefix route bridging to the api gateway. */
|
|
import { Context } from 'cordis'
|
|
import { describe, expect, it } from 'vitest'
|
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
import type { ApiProxy } from '@deepseek-ai/dsh-host-apiproxy/api'
|
|
import type { HttpServerService, WebRoute } from '@deepseek-ai/dsh-host-webserver'
|
|
import type { AttachmentStore } from '@deepseek-ai/dsh-attachment'
|
|
import { API_PATH, apply, inject } from '../src/index.ts'
|
|
|
|
describe('connection node half', () => {
|
|
it('registers the /api prefix route and removes it with the fiber', async () => {
|
|
const ctx = new Context()
|
|
const routes: WebRoute[] = []
|
|
// Structural fake: the plugin only touches register(); the service class
|
|
// carries private state a literal cannot (and need not) reproduce.
|
|
const httpServer: Pick<HttpServerService, 'register' | 'tapIndex' | 'port'> = {
|
|
register(route) {
|
|
routes.push(route)
|
|
return () => { routes.splice(routes.indexOf(route), 1) }
|
|
},
|
|
tapIndex: () => () => {},
|
|
port: 0,
|
|
}
|
|
ctx.provide('httpServer', httpServer as HttpServerService)
|
|
ctx.provide('apiProxy', {} as unknown as ApiProxy)
|
|
ctx.provide('attachments', {
|
|
imageLimits: { maxMessageImageBytes: 20 * 1024 * 1024 },
|
|
} as AttachmentStore)
|
|
|
|
const fiber = ctx.plugin({ inject: [...inject], apply })
|
|
await fiber.await()
|
|
expect(routes).toHaveLength(1)
|
|
expect(routes[0]).toMatchObject({ kind: 'prefix', path: API_PATH })
|
|
|
|
let status: number | undefined
|
|
let body: unknown
|
|
const deniedRequest = {
|
|
url: '/api/host.pickDirectory',
|
|
headers: {
|
|
host: 'harness.example', origin: 'http://harness.example', 'sec-fetch-site': 'same-origin',
|
|
},
|
|
socket: { remoteAddress: '192.168.1.8' },
|
|
} as unknown as IncomingMessage
|
|
const deniedResponse = {
|
|
writeHead(value: number) { status = value; return this },
|
|
end(value?: unknown) { body = value; return this },
|
|
} as unknown as ServerResponse
|
|
await routes[0]!.handler(deniedRequest, deniedResponse)
|
|
expect(status).toBe(403)
|
|
expect(body).toBe('forbidden')
|
|
|
|
await fiber.dispose()
|
|
expect(routes).toHaveLength(0)
|
|
})
|
|
})
|