Files
orca/src/shared/remote-runtime-client-error-classification.test.ts
T
Jinwoo Hong 15ef69a814 refactor(runtime): preserve transport error codes over IPC (#12667)
Errors thrown across Electron's `ipcMain.handle` lose their structured error code — only the message survives. So the renderer classified transport failures by matching substrings of English message text. That is how a queue-overload rejection escaped classification during a remote outage and surfaced as a raw error wall: the code was stripped in transit and its message fragment was not in the recoverable list.

This converts `RemoteRuntimeClientError` and `RuntimeRpcCallQueueOverloadError` rejections from `runtimeEnvironments:call` into the existing structured `{ok:false, error:{code,message}}` response, which the preload already passes through unchanged and `unwrapRuntimeRpcResult` already reconstructs with the code intact. Classification now treats a present code as authoritative and consults message fragments only when there is no code.

The fragment list is deliberately RETAINED as a backstop, not deleted: untyped main-handler rejections, subscription-start failures, and older code-less paths still rely on it.

Proven real rather than cosmetic: a test-only patch applied to unmodified main fails (4 failed / 66 passed) because the code does not survive the boundary today, and passes on this branch.

Independent review specifically chased the risk that a present-but-unrecognized code would now short-circuit to fatal where a message fragment previously rescued it — the shape that dead-ends a pane. It enumerated all 34 reachable code/message pairs and confirmed no pair flips recoverable to fatal, that the newly-serialized code set is closed and client-local, and that host-forwarded codes preserve recoverable classification by design. A differential harness over that corpus was verified non-vacuous by injecting the bad shape.

Nothing crosses the paired-runtime wire: desktop main -> IPC -> preload -> renderer only, reusing an existing response shape, no new fields or opcodes.

The connection-level offline state with a single reconnect affordance remains as STA-3456 follow-up work.
2026-08-05 00:44:05 -07:00

63 lines
2.1 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
isRecoverableRemoteRuntimeConnectionError,
isRuntimeRpcQueueOverloadError,
toRemoteRuntimeClientErrorLike
} from './remote-runtime-client-error-classification'
describe('remote runtime client error classification', () => {
it.each([
'remote_runtime_unavailable',
'runtime_rpc_queue_overloaded',
'runtime_timeout',
'runtime_unavailable',
'reconnecting'
])('treats %s as recoverable', (code) => {
expect(isRecoverableRemoteRuntimeConnectionError({ code, message: 'transport failed' })).toBe(
true
)
})
it('does not retry authentication or protocol failures', () => {
expect(
isRecoverableRemoteRuntimeConnectionError({ code: 'unauthorized', message: 'bad token' })
).toBe(false)
expect(
isRecoverableRemoteRuntimeConnectionError({
code: 'invalid_runtime_response',
message: 'bad frame'
})
).toBe(false)
})
it('trusts a structured recovery code before legacy message fragments', () => {
expect(
isRecoverableRemoteRuntimeConnectionError({
code: 'unauthorized',
message: 'Remote Orca runtime closed the connection.'
})
).toBe(false)
})
it('trusts a structured queue code before legacy message fragments', () => {
expect(
isRuntimeRpcQueueOverloadError({
code: 'remote_runtime_unavailable',
message: 'Remote runtime call queue is full; retry after current calls finish.'
})
).toBe(false)
})
it.each([
'Could not connect to the remote Orca runtime.',
'Remote Orca runtime closed the connection.',
'Remote Orca runtime connection closed.',
'Remote Orca runtime is not connected.',
"Error invoking remote method 'runtimeEnvironments:call': RuntimeRpcCallQueueOverloadError: Remote runtime call queue is full; retry after current calls finish.",
'Remote runtime subscription closed before it started.'
])('normalizes unstructured connection failure: %s', (message) => {
const error = toRemoteRuntimeClientErrorLike(new Error(message))
expect(isRecoverableRemoteRuntimeConnectionError(error)).toBe(true)
})
})