Files
deepseek-harness/packages/client/connection/tests/http-bridge.spec.ts
creatixchu f73bf425ec fix: address ds-review-bot v8 findings
- llm-deepseek: the uncatalogued resolveModel fallback declares text-only
  modalities — the wire route is text-only regardless of catalog
  membership, so "unknown" must not let the host persist-then-fail images.
- session.selectModel also consults the pending-inbox mirror: a queued
  image prompt enters the log only when claimed, after a switch would land.
- attachment store: ensureDurableDirectory syncs every ancestor entry up to
  a caller-vouched boundary regardless of what mkdir reports — a raced
  "already existed" is not "already durable".
- One image walker (imageBlockIn/imageInEvent) now serves both attachment
  authorization and the selection gate; referencedImage therefore also
  authorizes references inside wrapped message content.
- InputHub: the scope disposer resolves the conversation service optionally
  (teardown/HMR must reach quiescence), and a send failing after its scope
  died releases the in-flight drafts instead of restoring them onto a
  disposed shell.
- http-bridge destroys declared-oversize requests with connection: close
  instead of draining a body the client can trickle indefinitely.
- LlmService validates AND detaches modality arrays identically on the
  advisory and exact routes; READMEs record the fourth INVALID_MODEL_INFO
  rejection reason.
- CLI provider docs (JSDoc, README pair, Agent Note pair) describe the
  reuse behavior; llm-route.spec now parses the SHIPPED cordis.yml through
  the production extraction, pinning the row coupling.
- image-display lane pins gallery/rail shape in inline snapshots and the
  object-URL scheme this environment must take; stale host.schema comment
  dropped.
2026-07-29 19:40:17 +08:00

76 lines
2.9 KiB
TypeScript

import { EventEmitter } from 'node:events'
import { Readable } from 'node:stream'
import type { IncomingMessage, ServerResponse } from 'node:http'
import { describe, expect, it } from 'vitest'
import { bridge } from '../src/http-bridge.ts'
describe('HTTP bridge abort', () => {
it('destroys a declared-oversize request instead of draining it', async () => {
const destroyed: true[] = []
const request = Readable.from([]) as unknown as IncomingMessage
Object.assign(request, {
url: '/api/session.prompt',
method: 'POST',
headers: { 'content-type': 'application/json', 'content-length': '999999' },
destroy: () => { destroyed.push(true) },
})
let status: number | undefined
let headers: unknown
const response = Object.assign(new EventEmitter(), {
writableEnded: false,
writeHead(code: number, values?: unknown) { status = code; headers = values; return this },
write() { return true },
end(this: { writableEnded: boolean }) { this.writableEnded = true; return this },
}) as unknown as ServerResponse
await bridge(request, response, {
fetch: () => { throw new Error('a rejected request must never reach the handler') },
}, 1000)
// The socket must not stay parked draining a body the client can trickle
// at will after the rejection — same discipline as the chunked overrun.
expect(status).toBe(413)
expect(headers).toMatchObject({ connection: 'close' })
expect(destroyed).toHaveLength(1)
})
it('aborts a pending native picker request when the browser disconnects', async () => {
const body = JSON.stringify({
type: 'client-request', rpcId: 'picker-1', method: 'host.pickDirectory', payload: {},
})
const request = Readable.from([Buffer.from(body)]) as unknown as IncomingMessage
Object.assign(request, {
url: '/api/host.pickDirectory',
method: 'POST',
headers: { 'content-type': 'application/json' },
})
const response = Object.assign(new EventEmitter(), {
writableEnded: false,
writeHead() { return this },
write() { return true },
end() { this.writableEnded = true; return this },
}) as unknown as ServerResponse
let resolveStarted!: () => void
const started = new Promise<void>((resolve) => { resolveStarted = resolve })
let carrierSignal: AbortSignal | undefined
const pending = bridge(request, response, {
fetch: async (input) => {
const fetchRequest = input as Request
carrierSignal = fetchRequest.signal
resolveStarted()
if (!fetchRequest.signal.aborted) {
await new Promise<void>((resolve) => {
fetchRequest.signal.addEventListener('abort', () => { resolve() }, { once: true })
})
}
return Response.json({ aborted: fetchRequest.signal.aborted })
},
}, Number.MAX_SAFE_INTEGER)
await started
response.emit('close')
await pending
expect(carrierSignal?.aborted).toBe(true)
})
})