Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { createClientSessionState } from '@/lib/chat-runtime'
|
||||
import { $activeSessionId, $selectedStoredSessionId, $unreadFinishedSessionIds } from '@/store/session'
|
||||
import {
|
||||
$attentionSessionIds,
|
||||
$sessionStates,
|
||||
$workingSessionIds,
|
||||
clearAllSessionStates,
|
||||
publishSessionState
|
||||
} from '@/store/session-states'
|
||||
|
||||
import { rehydrateLiveSessionStatuses } from './use-background-sync'
|
||||
|
||||
/**
|
||||
* `session.active_list` is the authoritative snapshot of what is RUNNING in the
|
||||
* polled gateway process. A session that finished while Desktop was looking
|
||||
* elsewhere — or whose runtime id was recycled by a backend respawn — simply
|
||||
* stops appearing in the response. Absence is therefore a completion signal,
|
||||
* not "no news": if nothing reaps it, the row spins forever and the
|
||||
* busy→idle edge that paints the green "your turn" dot never fires.
|
||||
*/
|
||||
describe('rehydrateLiveSessionStatuses — reaping vanished runtimes', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
$selectedStoredSessionId.set(null)
|
||||
$unreadFinishedSessionIds.set([])
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllTimers()
|
||||
vi.useRealTimers()
|
||||
clearAllSessionStates()
|
||||
$unreadFinishedSessionIds.set([])
|
||||
$activeSessionId.set(null)
|
||||
})
|
||||
|
||||
it('clears a working session that disappears from the live snapshot', () => {
|
||||
rehydrateLiveSessionStatuses({
|
||||
sessions: [{ id: 'runtime-a', session_key: 'stored-a', status: 'working' }]
|
||||
})
|
||||
|
||||
expect($workingSessionIds.get()).toEqual(['stored-a'])
|
||||
|
||||
// The turn finished and the gateway reaped the session between polls.
|
||||
rehydrateLiveSessionStatuses({ sessions: [] })
|
||||
|
||||
expect($workingSessionIds.get()).toEqual([])
|
||||
})
|
||||
|
||||
it('fires the unread "your turn" marker for a vanished background session', () => {
|
||||
rehydrateLiveSessionStatuses({
|
||||
sessions: [{ id: 'runtime-b', session_key: 'stored-b', status: 'working' }]
|
||||
})
|
||||
|
||||
rehydrateLiveSessionStatuses({ sessions: [] })
|
||||
|
||||
expect($unreadFinishedSessionIds.get()).toEqual(['stored-b'])
|
||||
})
|
||||
|
||||
it('clears a blocked session that disappears from the live snapshot', () => {
|
||||
rehydrateLiveSessionStatuses({
|
||||
sessions: [{ id: 'runtime-c', session_key: 'stored-c', status: 'waiting' }]
|
||||
})
|
||||
|
||||
expect($attentionSessionIds.get()).toEqual(['stored-c'])
|
||||
|
||||
rehydrateLiveSessionStatuses({ sessions: [] })
|
||||
|
||||
expect($attentionSessionIds.get()).toEqual([])
|
||||
})
|
||||
|
||||
it('leaves runtimes this poll never seeded alone', () => {
|
||||
// A background PROFILE's sessions are served by a different gateway and
|
||||
// never appear in this profile's active_list. Reaping them would dark out
|
||||
// every other profile's running rows.
|
||||
rehydrateLiveSessionStatuses(
|
||||
{ sessions: [{ id: 'runtime-other', session_key: 'stored-other', status: 'working' }] },
|
||||
Date.now(),
|
||||
'other'
|
||||
)
|
||||
|
||||
rehydrateLiveSessionStatuses({ sessions: [] }, Date.now(), 'default')
|
||||
|
||||
expect($workingSessionIds.get()).toEqual(['stored-other'])
|
||||
})
|
||||
|
||||
it('seals open tool parts and clears awaitingResponse when a session vanishes', () => {
|
||||
const openTool = {
|
||||
type: 'tool-call',
|
||||
toolCallId: 'call-1',
|
||||
toolName: 'patch',
|
||||
args: {},
|
||||
argsText: '{}'
|
||||
} as never
|
||||
|
||||
publishSessionState('runtime-tools', {
|
||||
...createClientSessionState('stored-tools'),
|
||||
busy: true,
|
||||
awaitingResponse: true,
|
||||
messages: [{ id: 'a1', role: 'assistant', parts: [openTool], pending: false } as never]
|
||||
})
|
||||
|
||||
// Keep the runtime referenced so the settled state stays in the store
|
||||
// instead of being evicted as no-longer-needed.
|
||||
$activeSessionId.set('runtime-tools')
|
||||
|
||||
rehydrateLiveSessionStatuses({
|
||||
sessions: [{ id: 'runtime-tools', session_key: 'stored-tools', status: 'working' }]
|
||||
})
|
||||
rehydrateLiveSessionStatuses({ sessions: [] })
|
||||
|
||||
const state = $sessionStates.get()['runtime-tools']
|
||||
|
||||
expect(state.busy).toBe(false)
|
||||
expect(state.awaitingResponse).toBe(false)
|
||||
expect((state.messages[0].parts[0] as { result?: unknown }).result).toBeDefined()
|
||||
})
|
||||
|
||||
it('clears a session stuck awaiting a response without the busy flag', () => {
|
||||
publishSessionState('runtime-await', {
|
||||
...createClientSessionState('stored-await'),
|
||||
awaitingResponse: true,
|
||||
busy: false
|
||||
})
|
||||
|
||||
$activeSessionId.set('runtime-await')
|
||||
|
||||
rehydrateLiveSessionStatuses({
|
||||
sessions: [{ id: 'runtime-await', session_key: 'stored-await', status: 'working' }]
|
||||
})
|
||||
rehydrateLiveSessionStatuses({ sessions: [] })
|
||||
|
||||
expect($sessionStates.get()['runtime-await'].awaitingResponse).toBe(false)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user