fix(cli): close shared config review gaps
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user