mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
# Conflicts: # .agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.i18n.yaml # .agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.md # .agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.zh.md # .agents/notes/implemented/architecture/2026-07-25-web-input-machine-and-slash-pipeline.i18n.yaml # .agents/notes/implemented/architecture/2026-07-25-web-input-machine-and-slash-pipeline.md # .agents/notes/implemented/architecture/2026-07-25-web-input-machine-and-slash-pipeline.zh.md # THIRD_PARTY_NOTICES.md # apps/cli/composition.md # apps/cli/config/base.cordis.yml # apps/cli/package.json # apps/cli/src/app-cli-entry.ts # apps/cli/src/bin.ts # apps/cli/tests/args.spec.ts # apps/web/tests/built-boot.snapshot.ts # apps/web/tests/navigation-panes.e2e.ts # docs/architecture.i18n.yaml # docs/architecture.md # docs/architecture.zh.md # docs/config-catalog.md # docs/cordis-catalog/services.md # docs/core-data-structures/core.i18n.yaml # docs/core-data-structures/llm-streaming.i18n.yaml # docs/event-producer-consumer.md # docs/module-graph.md # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl # packages/README.i18n.yaml # packages/bundle/README.i18n.yaml # packages/client/connection/README.i18n.yaml # packages/client/connection/README.md # packages/client/connection/README.zh.md # packages/client/connection/src/client/fixture.ts # packages/client/connection/src/http-bridge.ts # packages/client/connection/src/index.ts # packages/client/connection/tests/fixture.spec.ts # packages/client/connection/tests/node-half.spec.ts # packages/client/runtime/README.i18n.yaml # packages/client/runtime/README.md # packages/client/runtime/README.zh.md # packages/client/runtime/src/client/contract/session.ts # packages/client/runtime/src/client/sessions/session.ts # packages/client/ui-conversation/README.i18n.yaml # packages/client/ui-conversation/README.md # packages/client/ui-conversation/README.zh.md # packages/client/ui-conversation/src/client/apply.ts # packages/client/ui-conversation/src/client/chat/AssistantMarkdown.tsx # packages/client/ui-conversation/src/client/chat/ChatView.tsx # packages/client/ui-conversation/src/client/chat/MessageItem.module.css # packages/client/ui-conversation/src/client/chat/MessageItem.tsx # packages/client/ui-conversation/src/client/contract/slots.ts # packages/client/ui-conversation/src/client/index.ts # packages/client/ui-conversation/src/client/input/contract.ts # packages/client/ui-conversation/src/client/input/facade.ts # packages/client/ui-conversation/src/client/input/hub.ts # packages/client/ui-conversation/src/client/locales.ts # packages/client/ui-conversation/src/client/service.ts # packages/client/ui-conversation/src/client/skeleton/ConversationSession.tsx # packages/client/ui-conversation/src/client/skeleton/InputBar.tsx # packages/client/ui-conversation/tests/apply-inject.spec.tsx # packages/client/ui-conversation/tests/input-bar.spec.tsx # packages/client/ui-conversation/tests/input-matrix.spec.tsx # packages/client/ui-conversation/tests/input-scenarios.spec.tsx # packages/client/ui-conversation/tests/service-orchestration.spec.ts # packages/client/ui-conversation/tests/skeleton.spec.tsx # 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/README.i18n.yaml # packages/host/apiproxy/README.md # packages/host/apiproxy/README.zh.md # packages/host/apiproxy/src/api-proxy.ts # packages/host/apiproxy/src/api/rpc.ts # packages/host/apiproxy/src/api/sessions.ts # packages/host/apiproxy/src/index.ts # packages/host/apiproxy/tests/api-proxy-models.spec.ts # packages/host/apiproxy/tests/rpc-schemas.spec.ts # packages/llm/llm-pi-ai/README.i18n.yaml # packages/llm/llm-pi-ai/README.md # packages/llm/llm-pi-ai/README.zh.md # packages/llm/llm-pi-ai/src/adapter.ts # packages/llm/llm/README.i18n.yaml # packages/ui/tui/README.md # packages/ui/tui/README.zh.md # packages/ui/tui/src/components/content.ts # packages/ui/tui/src/components/transcript.ts # packages/ui/tui/tests/tui.spec.ts # pnpm-lock.yaml
76 lines
2.9 KiB
TypeScript
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
|
|
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)
|
|
})
|
|
})
|