test(runtime): cover the Windows agent-prompt submit delay on Linux lanes

AGENT_PROMPT_SUBMIT_DELAY_MS is frozen from process.platform at import, so
no PR lane ever exercised the 1_500 ConPTY path. Add a suite that mocks the
constant to the Windows value and replays both the delay boundary and the
cancellation-during-verification scenario.

Also pin the phase boundary in the existing cancellation test so timing drift
fails at the Enter write rather than as an ambiguous empty post-abort array.

Refs #15498
This commit is contained in:
Neil
2026-08-21 23:02:28 -07:00
parent 3d939ae1b2
commit 1eb70b8ef2
2 changed files with 103 additions and 1 deletions
@@ -676,8 +676,10 @@ describe('agent prompt submission runtime', () => {
})
const rejected = expect(submission).rejects.toThrow('request_aborted')
// Why: the settle delay is platform-dependent (1_500 on Windows); a hardcoded 500 aborts before the Enter there.
// Why: the submit delay is 1_500 on Windows (ConPTY); a hardcoded 500 aborts before the Enter there.
await vi.advanceTimersByTimeAsync(AGENT_PROMPT_SUBMIT_DELAY_MS)
// Why: pin the phase boundary so drift fails here instead of as an empty post-abort array.
expect(writes.filter((data) => data === '\r')).toHaveLength(1)
controller.abort()
await vi.runAllTimersAsync()
@@ -0,0 +1,100 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import type * as AgentPromptInjection from '../../shared/agent-prompt-injection'
import { OrcaRuntimeService } from './orca-runtime'
import { makeStore } from './runtime-rpc-worktree-store-fixtures'
// Why: vi.mock factories are hoisted above module-scope consts, so the delay must be hoisted too.
const { WINDOWS_SUBMIT_DELAY_MS } = vi.hoisted(() => ({ WINDOWS_SUBMIT_DELAY_MS: 1_500 }))
const WORKTREE_PATH = '/tmp/worktree-a'
// Why: AGENT_PROMPT_SUBMIT_DELAY_MS is frozen from process.platform at import, so the 1_500 ConPTY
// path is otherwise unreachable on the Linux/macOS lanes that run this suite.
vi.mock('../../shared/agent-prompt-injection', async (importOriginal) => ({
...(await importOriginal<typeof AgentPromptInjection>()),
AGENT_PROMPT_SUBMIT_DELAY_MS: WINDOWS_SUBMIT_DELAY_MS
}))
vi.mock('../git/worktree', () => ({
listWorktrees: vi.fn().mockResolvedValue([
{
path: '/tmp/worktree-a',
head: 'abc',
branch: 'feature/prompt-verification',
isBare: false,
isMainWorktree: false
}
]),
listWorktreesStrict: vi.fn().mockResolvedValue([
{
path: '/tmp/worktree-a',
head: 'abc',
branch: 'feature/prompt-verification',
isBare: false,
isMainWorktree: false
}
])
}))
// Why: 'aider' is not a settlement agent, so submission takes the fixed-delay branch under test.
async function createPromptRuntime(): Promise<{
runtime: OrcaRuntimeService
handle: string
writes: string[]
}> {
const runtime = new OrcaRuntimeService(makeStore() as never)
const writes: string[] = []
runtime.setPtyController({
spawn: vi.fn().mockResolvedValue({ id: 'pty-prompt' }),
write: (_ptyId, data) => {
writes.push(data)
return true
},
kill: () => true,
getForegroundProcess: async () => null
})
const terminal = await runtime.createTerminal(`path:${WORKTREE_PATH}`, {
launchAgent: 'aider'
})
return { runtime, handle: terminal.handle, writes }
}
function countSubmits(writes: readonly string[]): number {
return writes.filter((data) => data === '\r').length
}
describe('agent prompt submission at the Windows submit delay', () => {
afterEach(() => vi.useRealTimers())
it('holds Enter until the full platform delay elapses', async () => {
vi.useFakeTimers()
const { runtime, handle, writes } = await createPromptRuntime()
const submission = runtime.sendTerminalAgentPrompt(handle, 'review this')
const stalled = expect(submission).rejects.toThrow('agent_prompt_stalled')
await vi.advanceTimersByTimeAsync(WINDOWS_SUBMIT_DELAY_MS - 1)
expect(countSubmits(writes)).toBe(0)
await vi.advanceTimersByTimeAsync(1)
expect(countSubmits(writes)).toBe(1)
await vi.runAllTimersAsync()
await stalled
})
it('does not send another Enter after cancellation during verification', async () => {
vi.useFakeTimers()
const controller = new AbortController()
const { runtime, handle, writes } = await createPromptRuntime()
const submission = runtime.sendTerminalAgentPrompt(handle, 'review this', {
signal: controller.signal
})
const rejected = expect(submission).rejects.toThrow('request_aborted')
await vi.advanceTimersByTimeAsync(WINDOWS_SUBMIT_DELAY_MS)
expect(countSubmits(writes)).toBe(1)
controller.abort()
await vi.runAllTimersAsync()
await rejected
expect(countSubmits(writes)).toBe(1)
})
})