Files
orca/src/main/runtime/rpc/errors.test.ts
T

149 lines
5.0 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { mapRuntimeError } from './errors'
class LineageError extends Error {
code = 'LINEAGE_PARENT_NOT_FOUND'
data = {
nextSteps: ['Run `orca worktree list`.', 'Retry with --no-parent.']
}
}
describe('mapRuntimeError', () => {
it.each(['terminal_tab_close_timeout', 'terminal_tab_not_found', 'terminal_tab_pinned'])(
'preserves the durable terminal tab close failure %s',
(code) => {
expect(mapRuntimeError('req_1', { runtimeId: 'runtime-1' }, new Error(code))).toMatchObject({
ok: false,
error: { code, message: code }
})
}
)
it.each([
'remote_update_manual_required',
'remote_update_not_available',
'remote_update_not_downloaded'
])('preserves remote updater failure %s', (code) => {
expect(mapRuntimeError('req_1', { runtimeId: 'runtime-1' }, new Error(code))).toMatchObject({
ok: false,
error: { code, message: code }
})
})
it.each([
['window_not_focused', 'keyboard input requires focus', 'restore-window'],
['permission_denied', 'missing DBUS_SESSION_BUS_ADDRESS', 'permissions'],
['element_not_found', 'fresh element index required', 'get-app-state'],
['unsupported_capability', 'hotkey combinations require xdotool', 'capabilities'],
[
'action_not_supported',
'Raise is not a valid secondary action',
'advertised secondary actions'
],
['value_not_settable', 'element value is not settable', 'settable text element'],
['element_not_clickable', 'element has no actionable frame', 'actionable frame'],
['invalid_argument', 'click_count must be a positive integer', 'Do not retry'],
['action_timeout', 'computer sidecar click timed out', 'do not repeat'],
['screenshot_failed', 'screenshot capture returned no image', '--no-screenshot'],
['accessibility_error', 'desktop script provider is not available', 'capabilities']
])('adds recovery steps for computer-use %s errors', (code, message, recoveryFragment) => {
const error = new Error(message)
Object.assign(error, { code })
const response = mapRuntimeError('req_1', { runtimeId: 'runtime-1' }, error)
expect(response.error).toMatchObject({
code,
message,
data: {
nextSteps: expect.arrayContaining([expect.stringContaining(recoveryFragment)])
}
})
})
it('adds computer-use startup recovery steps for missing desktop apps', () => {
const error = new Error('app not found: Gmail')
Object.assign(error, { code: 'app_not_found' })
const response = mapRuntimeError('req_1', { runtimeId: 'runtime-1' }, error)
expect(response.error).toMatchObject({
code: 'app_not_found',
message: 'app not found: Gmail',
data: {
nextSteps: [
expect.stringContaining('list-apps'),
expect.stringContaining('desktop browser app/window'),
expect.stringContaining('--app <web app>'),
expect.stringContaining('list-windows --app <browser>')
]
}
})
})
it('adds computer-use recovery steps for missing desktop windows', () => {
const error = new Error('No top-level window found')
Object.assign(error, { code: 'window_not_found' })
const response = mapRuntimeError('req_1', { runtimeId: 'runtime-1' }, error)
expect(response.error).toMatchObject({
code: 'window_not_found',
data: {
nextSteps: [
expect.stringContaining('list-windows'),
expect.stringContaining('--restore-window'),
expect.stringContaining('does not launch closed desktop apps')
]
}
})
})
it('preserves structured computer-use focus error codes for CLI recovery hints', () => {
const error = new Error(
'keyboard input requires the target window to be focused; retry with --restore-window'
)
Object.assign(error, { code: 'window_not_focused' })
const response = mapRuntimeError('req_1', { runtimeId: 'runtime-1' }, error)
expect(response).toEqual({
id: 'req_1',
ok: false,
error: {
code: 'window_not_focused',
message:
'keyboard input requires the target window to be focused; retry with --restore-window',
data: {
nextSteps: [
'Retry once with `--restore-window`.',
'If `--restore-window` was already used, stop retrying restore; bring the app forward manually, check permissions, or prefer `set-value` for editable fields.'
]
}
},
_meta: { runtimeId: 'runtime-1' }
})
})
it('preserves structured lineage error codes and data for CLI recovery hints', () => {
const response = mapRuntimeError(
'req_1',
{ runtimeId: 'runtime-1' },
new LineageError('Parent selector was not found.')
)
expect(response).toEqual({
id: 'req_1',
ok: false,
error: {
code: 'LINEAGE_PARENT_NOT_FOUND',
message: 'Parent selector was not found.',
data: {
nextSteps: ['Run `orca worktree list`.', 'Retry with --no-parent.']
}
},
_meta: { runtimeId: 'runtime-1' }
})
})
})