Files
orca/src/shared/agent-session-command-menu-query.test.ts
T
Brennan Benson e08cb10cc6 Prevent resting chats from delaying native command menus (#35069)
* Scope native chat command discovery to menu demand

* Fix command discovery CI imports and test timing

* fix: recover demanded command menus after context retirement

* test: load command host fixture before mocked consumers

* test: retain eager cache reads across command expiry

* test: cover command recovery after pending deadline

* test: count demanded command renders and query isolation

* test: load mobile command host fixture during collection

* test: type command batch fields from subscribe events

* test: check command event arrays without assertion

* Fix command discovery fixture project boundaries

* Name command fixture mocks with public Vitest types

* Assert opening metadata on resumed session snapshots

* test: assert passive command population boundaries

* test: cover mobile command capability handoff

* test: correct capability proof scope adapters
2026-10-29 13:16:35 -07:00

101 lines
3.5 KiB
TypeScript

import { afterEach, expect, it, vi } from 'vitest'
import { startAgentSessionCommandMenuQuery } from './agent-session-command-menu-query'
afterEach(() => vi.useRealTimers())
it.each(['completion', 'lost-hint', 'lost-reply'])(
'recovers slow commands after a %s without closed-menu demand',
async (mode) => {
vi.useFakeTimers()
const commands = [{ name: 'fast', kind: 'command' as const }]
let ready = false
const read = vi.fn(async () => {
if (mode === 'lost-reply' && read.mock.calls.length === 1) {
throw new Error('lost reply')
}
return ready ? { commands, commandsPending: false } : { commandsPending: true }
})
const accept = vi.fn()
const query = startAgentSessionCommandMenuQuery(read, accept)
try {
await vi.advanceTimersByTimeAsync(0)
await vi.advanceTimersByTimeAsync(2_000)
ready = true
if (mode === 'completion') {
query.invalidate()
}
await vi.advanceTimersByTimeAsync(mode === 'completion' ? 0 : 8_000)
expect(accept).toHaveBeenLastCalledWith({ commands, commandsPending: false })
expect(read).toHaveBeenCalledTimes(2)
query.dispose()
query.invalidate()
await vi.advanceTimersByTimeAsync(330_000)
expect(read).toHaveBeenCalledTimes(2)
} finally {
query.dispose()
}
}
)
it('coalesces invalidation, discards late answers and bounds failed recovery', async () => {
vi.useFakeTimers()
const pending = Promise.withResolvers<{ commandsPending: boolean }>()
const read = vi.fn(() => pending.promise)
const accept = vi.fn()
const query = startAgentSessionCommandMenuQuery(read, accept)
query.invalidate()
query.invalidate()
query.dispose()
pending.resolve({ commandsPending: false })
await vi.advanceTimersByTimeAsync(330_000)
expect(read).toHaveBeenCalledTimes(1)
expect(accept).not.toHaveBeenCalled()
const fail = vi.fn(async () => {
throw new Error('transport lost')
})
const failed = startAgentSessionCommandMenuQuery(fail, accept)
try {
await vi.advanceTimersByTimeAsync(330_000)
expect(fail).toHaveBeenCalledTimes(3)
} finally {
failed.dispose()
}
})
it.each([undefined, [], [{ name: 'fast', kind: 'command' as const }]])(
'leaves settled %j demanded without a timer, then accepts a real wake',
async (commands) => {
vi.useFakeTimers()
const read = vi.fn(async () => ({ commands, commandsPending: false }))
const query = startAgentSessionCommandMenuQuery(read, vi.fn())
await vi.advanceTimersByTimeAsync(600_001)
expect(read).toHaveBeenCalledTimes(1)
expect(vi.getTimerCount()).toBe(0)
query.invalidate()
await vi.advanceTimersByTimeAsync(0)
expect(read).toHaveBeenCalledTimes(2)
expect(vi.getTimerCount()).toBe(0)
query.dispose()
}
)
it('parks actual pending work at the deadline with one outstanding read and one dirty follow-up', async () => {
vi.useFakeTimers()
const held = Promise.withResolvers<{ commandsPending: boolean }>()
const read = vi.fn(() =>
read.mock.calls.length === 1 ? held.promise : Promise.resolve({ commandsPending: true })
)
const query = startAgentSessionCommandMenuQuery(read, vi.fn())
query.invalidate()
query.invalidate()
await vi.advanceTimersByTimeAsync(20_000)
expect(read).toHaveBeenCalledTimes(1)
held.resolve({ commandsPending: true })
await vi.advanceTimersByTimeAsync(0)
expect(read).toHaveBeenCalledTimes(2)
expect(vi.getTimerCount()).toBe(1)
await vi.advanceTimersByTimeAsync(600_000)
expect(read).toHaveBeenCalledTimes(32)
expect(vi.getTimerCount()).toBe(0)
query.dispose()
})