/** Node half: registers the /api prefix route bridging to the api gateway. */ import { EventEmitter } from 'node:events' import { Readable } from 'node:stream' 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 { API_PATH, apply, inject } from '../src/index.ts' /** Structural httpServer fake: the plugin only touches register(). */ function fakeHttpServer(routes: WebRoute[]): Pick { return { register(route) { routes.push(route) return () => { routes.splice(routes.indexOf(route), 1) } }, tapIndex: () => () => {}, port: 0, } } /** Bodyless GET carrying the given headers (enough for the trust fence + bridge). */ function fakeRequest(headers: Record): IncomingMessage { const request = Readable.from([]) as unknown as IncomingMessage Object.assign(request, { url: `${API_PATH}/session.list`, method: 'GET', headers }) return request } /** Response recorder compatible with both the fence's short-circuit and the bridge. */ function fakeResponse(): { response: ServerResponse; state: { status?: number; body?: unknown } } { const state: { status?: number; body?: unknown } = {} const response = Object.assign(new EventEmitter(), { writableEnded: false, writeHead(value: number) { state.status = value; return this }, write() { return true }, end(this: { writableEnded: boolean }, value?: unknown) { if (value !== undefined) state.body = value this.writableEnded = true return this }, }) as unknown as ServerResponse return { response, state } } async function mounted(config?: { trustedHosts?: string[] }): Promise<{ routes: WebRoute[]; dispose: () => Promise }> { const ctx = new Context() const routes: WebRoute[] = [] ctx.provide('httpServer', fakeHttpServer(routes) as HttpServerService) ctx.provide('apiProxy', {} as unknown as ApiProxy) const fiber = ctx.plugin({ inject: [...inject], apply }, config) await fiber.await() return { routes, dispose: () => fiber.dispose() } } describe('connection node half', () => { it('registers the /api prefix route and removes it with the fiber', async () => { const { routes, dispose } = await mounted() expect(routes).toHaveLength(1) expect(routes[0]).toMatchObject({ kind: 'prefix', path: API_PATH }) await dispose() expect(routes).toHaveLength(0) }) it('refuses an untrusted Host on any /api path before the bridge runs', async () => { const { routes, dispose } = await mounted() const { response, state } = fakeResponse() await routes[0]!.handler(fakeRequest({ host: 'harness.example', origin: 'http://harness.example', 'sec-fetch-site': 'same-origin', }), response) expect(state.status).toBe(403) expect(state.body).toBe('forbidden') await dispose() }) it('passes loopback and declared-authority requests through to the bridge', async () => { const { routes, dispose } = await mounted({ trustedHosts: ['harness.example:3080'] }) // Loopback, no browser markers (curl shape): the fence passes; the carrier // answers 404 for a GET unary path — proof the bridge ran. const loopback = fakeResponse() await routes[0]!.handler(fakeRequest({ host: '127.0.0.1:3080' }), loopback.response) expect(loopback.state.status).toBe(404) // Declared public authority, same-origin browser shape. const declared = fakeResponse() await routes[0]!.handler(fakeRequest({ host: 'harness.example:3080', origin: 'http://harness.example:3080', 'sec-fetch-site': 'same-origin', }), declared.response) expect(declared.state.status).toBe(404) await dispose() }) })