test: strengthen RPC cancellation, release rejection, and mobile prompt coverage (#33146)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-24 05:05:06 -07:00
committed by GitHub
co-authored by OrcaWin
parent 38ffe31706
commit 5a2d2f49bc
10 changed files with 118 additions and 53 deletions
@@ -3,7 +3,7 @@ import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { afterEach, expect, it } from 'vitest'
import { resolveCli } from './runtime-serve-terminal-smoke.mjs'
import { packagedNodeRuntimePath } from './build-orcad-node.mjs'
import { NODE_RUNTIME_ASSETS } from '../../src/shared/node-runtime-pin.ts'
import { currentTarget } from './server-build-target.mjs'
import { ORCAD_CLI_ENTRY_FILENAME } from '../../src/shared/orcad-artifacts.ts'
@@ -30,7 +30,13 @@ it('drives Orcad with its packaged CLI and pinned Node even when a desktop CLI e
mkdirSync(dirname(bundled), { recursive: true })
writeFileSync(bundled, '')
expect(resolveCli('orcad', root)).toEqual({
command: packagedNodeRuntimePath(directory, currentTarget()),
command: join(
root,
'out',
'runtimes',
`node-${NODE_RUNTIME_ASSETS[currentTarget()].executableSha256}`,
...(process.platform === 'win32' ? ['node.exe'] : ['bin', 'node'])
),
prefix: [bundled]
})
})
@@ -78,12 +78,14 @@ describe('antivirus detection report', () => {
known: true,
scanned: 70,
flagged: [
{ engine: 'TrendMicro', category: 'malicious', detection: 'Trojan.MSIL.MSILHERACLES' }
{ engine: 'TrendMicro', category: 'malicious', detection: 'Trojan.MSIL.MSILHERACLES' },
{ engine: 'Microsoft', category: 'suspicious', detection: 'Behavior:Win32/Test' }
]
})
expect(report).toContain('1 of 70 engines flag this build')
expect(report).toContain('2 of 70 engines flag this build')
expect(report).toContain('TrendMicro: Trojan.MSIL.MSILHERACLES')
expect(report).toContain('Microsoft: Behavior:Win32/Test')
})
it('flags the release when any single artifact carries a verdict', () => {
@@ -121,15 +121,19 @@ describe('verify-windows-inner-signature', () => {
})
it('rejects invalid status and unexpected signer subjects', () => {
expect(classifySignature({ ...validSignature, status: 'NotSigned' }).message).toMatch(
/status is NotSigned/
)
expect(classifySignature({ ...validSignature, status: 'NotSigned' })).toMatchObject({
ok: false,
message: expect.stringMatching(/status is NotSigned/)
})
expect(
classifySignature({
...validSignature,
signerSubject: 'CN=SignPath Foundation Test, O=SignPath Foundation, L=Vienna, C=AT'
}).message
).toMatch(/Unexpected Windows inner executable signer/)
})
).toMatchObject({
ok: false,
message: expect.stringMatching(/Unexpected Windows inner executable signer/)
})
})
it('accepts an expected thumbprint as an alternate explicit allowlist', () => {
@@ -150,8 +154,11 @@ describe('verify-windows-inner-signature', () => {
expectedSigners: [DEFAULT_EXPECTED_SIGNER],
expectedThumbprints: ['001122']
}
).message
).toMatch(/Unexpected Windows inner executable signer/)
)
).toMatchObject({
ok: false,
message: expect.stringMatching(/Unexpected Windows inner executable signer/)
})
expect(
classifySignature(validSignature, {
expectedSigners: [DEFAULT_EXPECTED_SIGNER],
@@ -109,7 +109,7 @@ describe('detectAgentPermission', () => {
const long = `Do you want to proceed? ${'x'.repeat(300)}`
const result = detectAgentPermission({ state: 'waiting', lastAssistantMessage: long })
expect(result).not.toBeNull()
expect((result?.detail ?? '').length).toBeLessThanOrEqual(160)
expect(result?.detail).toBe(`Do you want to proceed? ${'x'.repeat(135)}…`)
})
it('shortens long numbered option labels', () => {
@@ -120,7 +120,7 @@ describe('detectAgentPermission', () => {
})
const second = result?.options[1]
expect(second?.send).toBe('2')
expect((second?.label ?? '').length).toBeLessThanOrEqual(40)
expect(second?.label).toBe('No, and explain in great detail exactly…')
})
})
@@ -72,7 +72,20 @@ describe('mobile question rejection', () => {
expect(f.cancelPending).toHaveBeenCalledOnce()
const sends = f.sendRequest.mock.calls.filter(([method]) => method === 'terminal.send')
expect(sends).toHaveLength(1)
expect(sends[0]?.[1]).toMatchObject({ terminal: 'terminal', text: '\x1b', enter: false })
expect(f.sendRequest).toHaveBeenCalledWith(
'terminal.send',
{
terminal: 'terminal',
text: '\x1b',
enter: false,
requireWriteSettlement: true,
client: { id: 'device', type: 'mobile' }
},
{ timeoutMs: 15_000, budgetSpansConnect: true }
)
expect(f.cancelPending.mock.invocationCallOrder[0]).toBeLessThan(
f.sendRequest.mock.invocationCallOrder[0]!
)
expect(f.onSendError).not.toHaveBeenCalled()
})
@@ -1,11 +1,21 @@
import { createElement } from 'react'
import TestRenderer from 'react-test-renderer'
import { describe, expect, it, vi } from 'vitest'
import TestRenderer, { type ReactTestRenderer } from 'react-test-renderer'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { AgentStatusEntry } from '../../../src/shared/agent-status-types'
import type { NativeChatMessage } from '../../../src/shared/native-chat-types'
import { buildOmpPlanReviewAnswer } from '../../../src/shared/omp-plan-review-prompt'
import { useMobileNativeChatPrompts } from './use-mobile-native-chat-prompts'
const renderers: ReactTestRenderer[] = []
afterEach(() => {
TestRenderer.act(() => {
for (const renderer of renderers) {
renderer.unmount()
}
})
renderers.length = 0
})
const APPROVAL = JSON.stringify({
approval: { tool: 'Bash', summary: 'pnpm build > build.log 2>&1' }
})
@@ -30,7 +40,7 @@ function promptsFor(
return null
}
TestRenderer.act(() => {
TestRenderer.create(createElement(Probe))
renderers.push(TestRenderer.create(createElement(Probe)))
})
return captured!
}
@@ -216,37 +226,47 @@ describe('useMobileNativeChatPrompts ask state gate', () => {
})
describe('useMobileNativeChatPrompts in a structured chat', () => {
it('shows a Codex async question and answers it with an ordinary send', async () => {
const sendWithOutcome = vi.fn(async () => 'accepted' as const)
const questions = [{ title: 'Which color?', options: ['Red', 'Blue'] }]
const message: NativeChatMessage = {
id: 'ask',
role: 'assistant',
blocks: [
{ type: 'text', text: 'Which color?\n- Red\n- Blue' },
{ type: 'tool-call', name: 'request_user_input_async', input: { questions } }
],
timestamp: null,
source: 'transcript'
}
let captured: ReturnType<typeof useMobileNativeChatPrompts> | undefined
function Probe(): null {
captured = useMobileNativeChatPrompts({
enabled: true,
structured: { permission: null, question: null, sendWithOutcome },
status: null,
messages: [message],
transcriptLoading: false
it.each([
['accepted', true],
['queued', true],
['unknown', true],
['rejected', false]
] as const)(
'answers a Codex async question through an ordinary %s send',
async (outcome, accepted) => {
const sendWithOutcome = vi.fn(async () => outcome)
const questions = [{ title: 'Which color?', options: ['Red', 'Blue'] }]
const message: NativeChatMessage = {
id: 'ask',
role: 'assistant',
blocks: [
{ type: 'text', text: 'Which color?\n- Red\n- Blue' },
{ type: 'tool-call', name: 'request_user_input_async', input: { questions } }
],
timestamp: null,
source: 'transcript'
}
let captured: ReturnType<typeof useMobileNativeChatPrompts> | undefined
function Probe(): null {
captured = useMobileNativeChatPrompts({
enabled: true,
structured: { permission: null, question: null, sendWithOutcome },
status: null,
messages: [message],
transcriptLoading: false
})
return null
}
TestRenderer.act(() => {
renderers.push(TestRenderer.create(createElement(Probe)))
})
return null
}
TestRenderer.act(() => {
TestRenderer.create(createElement(Probe))
})
const ask = captured?.ask
expect(ask?.questions[0]?.question).toBe('Which color?')
await expect(ask && captured?.structuredAnswer?.(ask, [{ indices: [1] }])).resolves.toBe(true)
expect(sendWithOutcome).toHaveBeenCalledWith('> Which color?\n\nBlue')
})
const ask = captured?.ask
expect(ask?.questions[0]?.question).toBe('Which color?')
await expect(ask && captured?.structuredAnswer?.(ask, [{ indices: [1] }])).resolves.toBe(
accepted
)
expect(sendWithOutcome).toHaveBeenCalledWith('> Which color?\n\nBlue')
}
)
})
@@ -39,7 +39,7 @@ it('cancels setup using the preselected id and unsubscribes a late handle', asyn
it('keeps the response deadline while setup is pending', async () => {
vi.useFakeTimers()
const subscribe = vi.fn(() => new Promise(() => {}))
const subscribe = vi.fn((_args: { subscriptionId?: string }) => new Promise(() => {}))
const cancelSubscription = vi.fn().mockResolvedValue(undefined)
stubRuntimeEnvironmentBridge({ subscribe, cancelSubscription })
const pending = callAbortableRuntimeEnvironment(
@@ -50,7 +50,12 @@ it('keeps the response deadline while setup is pending', async () => {
new AbortController().signal
)
const rejection = expect(pending).rejects.toThrow('timed out')
await vi.advanceTimersByTimeAsync(15_000)
await vi.advanceTimersByTimeAsync(14_999)
expect(cancelSubscription).not.toHaveBeenCalled()
await vi.advanceTimersByTimeAsync(1)
await rejection
expect(cancelSubscription).toHaveBeenCalledOnce()
expect(cancelSubscription).toHaveBeenCalledWith({
subscriptionId: subscribe.mock.calls[0]?.[0].subscriptionId
})
})
@@ -21,15 +21,26 @@ beforeEach(() => {
describe('callHostRoute', () => {
it('calls the route target with the pairing revision captured before the call', async () => {
callRuntimeRpc.mockResolvedValue({ ok: true })
const signal = new AbortController().signal
await expect(
callHostRoute(route, 'folderWorkspace.update', { id: 'f' }, { timeoutMs: 5 })
callHostRoute(
route,
'folderWorkspace.update',
{ id: 'f' },
{ timeoutMs: 5, signal, expectedEnvironmentRuntimeId: 'runtime-a' }
)
).resolves.toEqual({ ok: true })
expect(callRuntimeRpc).toHaveBeenCalledWith(
route.target,
'folderWorkspace.update',
{ id: 'f' },
{ timeoutMs: 5, expectedEnvironmentPairingRevision: 7 }
{
timeoutMs: 5,
signal,
expectedEnvironmentRuntimeId: 'runtime-a',
expectedEnvironmentPairingRevision: 7
}
)
})
@@ -49,6 +49,7 @@ it('captures the pairing revision before awaiting the compatibility probe', asyn
})
await expect(request).resolves.toEqual({ ok: true })
expect(runtimeEnvironmentCall).toHaveBeenCalledTimes(2)
expect(runtimeEnvironmentCall).toHaveBeenNthCalledWith(1, {
selector: 'env-cas',
method: 'status.get',
@@ -7,7 +7,7 @@ import { brandEphemeralSetupTerminalWorktreeId } from '../../../shared/ephemeral
describe('toRuntimeWorktreeSelector', () => {
it('addresses raw worktree IDs as runtime ID selectors', () => {
expect(toRuntimeWorktreeSelector('wt-1')).toBe('id:wt-1')
expect(toRuntimeWorktreeSelector(' wt-1 ')).toBe('id:wt-1')
expect(toRuntimeWorktreeSelector('repo-1::C:/Users/me/orca/workspaces/orca/new-worktree')).toBe(
'id:repo-1::C:/Users/me/orca/workspaces/orca/new-worktree'
)