fix(cli): close shared config review gaps

This commit is contained in:
Turtle
2026-07-30 14:56:39 +08:00
parent 5a490553bc
commit a51143bded
51 changed files with 277 additions and 171 deletions
+78
View File
@@ -29,6 +29,7 @@ import SessionReferenceService, { formatSessionReferenceMention } from '@deepsee
import type {} from '@deepseek-ai/dsh-llm-retry'
import {
createTuiChat,
disposeRootAndExit,
FILE_REFERENCE_PROMPT,
mountTui,
renderSkillInvocation,
@@ -502,6 +503,30 @@ describe('goodbye message and /resume', () => {
await dispose(result)
})
it('treats a closed session-query provider as unavailable', async () => {
let queryCtx: Context | undefined
const result = await setup({
cwd: '/workspace',
async configureContext(ctx) {
await ctx.plugin({
apply(child: Context) {
queryCtx = child
child.provide('sessionQuery', {
listSessions: () => Promise.reject(new Error('closed database must not be called')),
} as never)
},
})
},
})
await queryCtx!.fiber.dispose()
result.terminal.send('/resume')
result.terminal.send('\r')
await tick()
expect(result.terminal.output).toContain('session query is not mounted')
expect(result.terminal.output).not.toContain('closed database')
await dispose(result)
})
it('keeps persisted query records readable without a persistence service', async () => {
const target = header('query-only-persisted', 10, '/workspace')
const result = await setup({
@@ -4962,6 +4987,59 @@ describe('TUI extension service', () => {
})
})
describe('application exit', () => {
it('disposes the root fiber rather than only the TUI child before exiting', async () => {
const rootDispose = vi.fn(() => Promise.resolve())
const childDispose = vi.fn(() => Promise.resolve())
const ctx = {
root: { fiber: { dispose: rootDispose } },
fiber: { dispose: childDispose },
} as unknown as Context
const exit = vi.fn()
disposeRootAndExit(ctx, 7, exit)
await Promise.resolve()
expect(rootDispose).toHaveBeenCalledOnce()
expect(childDispose).not.toHaveBeenCalled()
expect(exit).toHaveBeenCalledOnce()
expect(exit).toHaveBeenCalledWith(7)
})
it('forces exit when root disposal does not settle', async () => {
vi.useFakeTimers()
try {
let settle!: () => void
const disposal = new Promise<void>((resolve) => { settle = resolve })
const ctx = {
root: { fiber: { dispose: () => disposal } },
} as unknown as Context
const exit = vi.fn()
disposeRootAndExit(ctx, 9, exit)
await vi.advanceTimersByTimeAsync(4_999)
expect(exit).not.toHaveBeenCalled()
await vi.advanceTimersByTimeAsync(1)
expect(exit).toHaveBeenCalledOnce()
expect(exit).toHaveBeenCalledWith(9)
settle()
await disposal
await Promise.resolve()
expect(exit).toHaveBeenCalledOnce()
} finally {
vi.useRealTimers()
}
})
it('exits after a rejected root disposal without an unhandled rejection', async () => {
const ctx = {
root: { fiber: { dispose: () => Promise.reject(new Error('cleanup failed')) } },
} as unknown as Context
const exit = vi.fn()
disposeRootAndExit(ctx, 5, exit)
await Promise.resolve()
await Promise.resolve()
expect(exit).toHaveBeenCalledWith(5)
})
})
describe('terminal mounting', () => {
it('starts immediately when the configured agent already exists', async () => {
const ctx = new Context()