test(desktop): leftover 4001 rebind must not revive a deleted session

Cover the delete-transition race and the tombstone early-return so the Resume failed toast cannot come back through those paths.
This commit is contained in:
xxxigm
2026-09-02 22:26:46 +09:00
committed by brooklyn!
parent 990879d688
commit 66109d3bbd
2 changed files with 70 additions and 0 deletions

View File

@@ -101,6 +101,60 @@ describe('useRouteResume', () => {
expect(resumeSession).not.toHaveBeenCalled()
})
it('does not honor a leftover 4001 rebind during a /:sid -> /new delete transition', () => {
const resumeSession = vi.fn(async () => undefined)
const startFreshSessionDraft = vi.fn()
const activeSessionIdRef: MutableRefObject<null | string> = { current: 'runtime-1' }
const creatingSessionRef = { current: false }
const runtimeIdByStoredSessionIdRef = { current: new Map([['session-1', 'runtime-1']]) }
const selectedStoredSessionIdRef: MutableRefObject<null | string> = { current: 'session-1' }
const { rerender } = render(
<RouteResumeHarness
activeSessionId="runtime-1"
activeSessionIdRef={activeSessionIdRef}
creatingSessionRef={creatingSessionRef}
currentView="chat"
freshDraftReady={false}
gatewayState="open"
locationPathname="/session-1"
resumeSession={resumeSession}
routedSessionId="session-1"
runtimeIdByStoredSessionIdRef={runtimeIdByStoredSessionIdRef}
selectedStoredSessionId="session-1"
selectedStoredSessionIdRef={selectedStoredSessionIdRef}
startFreshSessionDraft={startFreshSessionDraft}
/>
)
expect(resumeSession).not.toHaveBeenCalled()
// Idle reap queued requestSessionResume, then the user deleted the chat.
// Draft state lands before React Router flips /:sid -> /new.
activeSessionIdRef.current = null
selectedStoredSessionIdRef.current = null
rerender(
<RouteResumeHarness
activeSessionId={null}
activeSessionIdRef={activeSessionIdRef}
creatingSessionRef={creatingSessionRef}
currentView="chat"
freshDraftReady
gatewayState="open"
locationPathname="/session-1"
resumeSession={resumeSession}
routedSessionId="session-1"
runtimeIdByStoredSessionIdRef={runtimeIdByStoredSessionIdRef}
selectedStoredSessionId={null}
selectedStoredSessionIdRef={selectedStoredSessionIdRef}
sessionResumeRequest={{ sequence: 1, sessionId: 'session-1' }}
startFreshSessionDraft={startFreshSessionDraft}
/>
)
expect(resumeSession).not.toHaveBeenCalled()
})
it('self-heals a stranded routed session (null selected/active, same pathname, not a fresh draft)', () => {
const resumeSession = vi.fn(async () => undefined)
const startFreshSessionDraft = vi.fn()

View File

@@ -1077,6 +1077,8 @@ describe('resumeSession failure recovery', () => {
setResumeFailedSessionId(null)
setMessages([])
setSessions([])
$removedSessionIds.set(new Set())
$sessionMutationsInFlight.set(new Set())
clearClarifyRequest()
vi.restoreAllMocks()
})
@@ -1094,6 +1096,20 @@ describe('resumeSession failure recovery', () => {
await resume!('stored-1', true)
}
it('does not resume a tombstoned session after delete', async () => {
$removedSessionIds.set(new Set(['stored-1']))
const requestGateway = vi.fn(async () => {
throw new Error('404: Session not found')
})
await runResume(requestGateway)
expect(requestGateway).not.toHaveBeenCalled()
expect($resumeFailedSessionId.get()).toBeNull()
expect($selectedStoredSessionId.get()).toBeNull()
})
it.each([
['Codex tool-only', ''],
['DeepSeek text-plus-tool', 'I found two paths; choose one.']