mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
fix(client): satisfy stream lifecycle contracts
This commit is contained in:
@@ -261,7 +261,7 @@ describe('RemoteJournalStream', () => {
|
||||
],
|
||||
[
|
||||
signal => new Promise<Page>((_resolve, reject) => {
|
||||
const aborted = (): void => { reject(signal.reason) }
|
||||
const aborted = (): void => { reject(new Error('page aborted')) }
|
||||
signal.addEventListener('abort', aborted, { once: true })
|
||||
if (signal.aborted) aborted()
|
||||
}),
|
||||
|
||||
@@ -120,10 +120,10 @@ describe('session.history projections block', () => {
|
||||
if (!response.ok) throw new Error('history failed')
|
||||
|
||||
expect(response.value.events.map(entry => entry.event.seq)).toEqual([0])
|
||||
expect(response.value.projections).toEqual({
|
||||
asOfSeq: 0,
|
||||
values: expect.objectContaining({ 'test/last-user': { text: 'm0' } }),
|
||||
})
|
||||
expect(response.value.projections?.asOfSeq).toBe(0)
|
||||
expect(response.value.projections?.values).toEqual(
|
||||
expect.objectContaining({ 'test/last-user': { text: 'm0' } }),
|
||||
)
|
||||
})
|
||||
|
||||
it('projects an empty log at cursor -1', async () => {
|
||||
@@ -134,10 +134,10 @@ describe('session.history projections block', () => {
|
||||
if (!response.ok) throw new Error('history failed')
|
||||
|
||||
expect(response.value.events).toEqual([])
|
||||
expect(response.value.projections).toEqual({
|
||||
asOfSeq: -1,
|
||||
values: expect.objectContaining({ 'test/last-user': null }),
|
||||
})
|
||||
expect(response.value.projections?.asOfSeq).toBe(-1)
|
||||
expect(response.value.projections?.values).toEqual(
|
||||
expect.objectContaining({ 'test/last-user': null }),
|
||||
)
|
||||
})
|
||||
|
||||
it('publishes the attachments imageLimits as a constant unit while both seams are composed', async () => {
|
||||
|
||||
@@ -205,22 +205,23 @@ export function apply(ctx: Context): void {
|
||||
const source = generationSource
|
||||
if (source === undefined) throw new Error('connection: no generation source is registered')
|
||||
const token = {}
|
||||
const ownsGeneration = (): boolean => owner?.token === token
|
||||
const controller = new ConnectionController(api, source, {
|
||||
...sinks,
|
||||
onConnected: (next) => {
|
||||
if (owner?.token !== token) return
|
||||
if (!ownsGeneration()) return
|
||||
publishDescription(next)
|
||||
// A description subscriber may synchronously stop the loop. In that
|
||||
// case publishDescription(undefined) has already retracted this
|
||||
// generation, so do not leak its stale connected notification to
|
||||
// the consumer sink afterward.
|
||||
if (owner?.token !== token || !Object.is(description, next)) return
|
||||
if (!ownsGeneration() || !Object.is(description, next)) return
|
||||
sinks.onConnected?.(next)
|
||||
},
|
||||
onStateChange: (state) => {
|
||||
if (owner?.token !== token) return
|
||||
if (!ownsGeneration()) return
|
||||
if (state === 'reconnecting') publishDescription(undefined)
|
||||
if (owner?.token !== token) return
|
||||
if (!ownsGeneration()) return
|
||||
sinks.onStateChange?.(state)
|
||||
},
|
||||
}, config ?? {})
|
||||
|
||||
@@ -816,9 +816,9 @@ describe('remaining branches', () => {
|
||||
expect(session.getSnapshot().promptError).toBeNull()
|
||||
})
|
||||
|
||||
it('dispose is a reserved no-op on resident instances', () => {
|
||||
it('dispose is a reserved no-op on resident instances', async () => {
|
||||
const { session } = makeSession()
|
||||
expect(() => { session.dispose() }).not.toThrow()
|
||||
await expect(session.dispose()).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
it('carries history-entry and follow-frame views into the business-neutral Event input', async () => {
|
||||
|
||||
@@ -1342,7 +1342,7 @@ describe('ChatView', () => {
|
||||
const h = makeHarness({
|
||||
pending: [
|
||||
new PendingWait('approval', 'r1', SID,
|
||||
{ approvalId: 'ap1', toolName: 'bash' } as PendingWait<'approval'>['payload'], vi.fn()),
|
||||
{ approvalId: 'ap1', toolName: 'bash' }, vi.fn()),
|
||||
new PendingWait('question', 'r2', SID,
|
||||
{ questions: [{ id: 'q1', question: '选择' }] }, vi.fn()),
|
||||
],
|
||||
|
||||
@@ -88,18 +88,19 @@ describe('UserQuestionService', () => {
|
||||
const pending = Promise.withResolvers<never>()
|
||||
ctx.userQuestions.registerProvider({ ask: () => pending.promise })
|
||||
const controller = new AbortController()
|
||||
const abortReason = new DOMException('This operation was aborted', 'AbortError')
|
||||
|
||||
const answer = ctx.userQuestions.ask({
|
||||
questions: [{ id: 'confirm', question: 'Proceed?' }],
|
||||
signal: controller.signal,
|
||||
})
|
||||
controller.abort()
|
||||
pending.reject(controller.signal.reason)
|
||||
controller.abort(abortReason)
|
||||
pending.reject(abortReason)
|
||||
|
||||
await expect(answer).rejects.toMatchObject({
|
||||
name: 'UserQuestionError',
|
||||
code: 'ASK_ABORTED',
|
||||
cause: controller.signal.reason,
|
||||
cause: abortReason,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user