Files
orca/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-errors.test.ts
T
Neilandm4air 8167ddaa7f Distinguish remote browser failures from server connection failures (#26573)
* Distinguish remote browser service failures from server outages

* Use the shared browser_unavailable code in the stream failure classifier

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-08 21:07:48 -07:00

93 lines
4.0 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
isPermanentRemoteBrowserStreamFailure,
remoteBrowserStreamUnsupportedError,
resolveRemoteBrowserStreamFailure
} from './remote-browser-stream-errors'
function rpcError(code: string, message = 'boom'): Error {
return Object.assign(new Error(message), { code })
}
// Why this file exists: the permanent set decides whether the pane ever retries on its own, and it
// was enforced by nothing — re-adding `selector_not_found` (the exact code 08260a54bf had to walk
// back out) left the whole suite green, as did deleting two of the three members.
describe('remote browser stream failure classification', () => {
it.each(['opening', 'restart'] as const)(
'distinguishes browser setup from transport during %s',
(phase) => {
const failure = resolveRemoteBrowserStreamFailure(
rpcError('browser_unavailable', 'unconfigured backend'),
phase
)
expect(failure.message).toBe(
'The remote browser is unavailable. Check its setup on the server.'
)
expect(failure.shouldRetry).toBe(true)
expect(failure.logRawError).toBe(true)
expect(isPermanentRemoteBrowserStreamFailure(rpcError('browser_unavailable'))).toBe(false)
}
)
it('keeps the initial transport failure distinct from a lost stream', () => {
expect(resolveRemoteBrowserStreamFailure(rpcError('runtime_timeout'), 'opening').message).toBe(
'Cannot reach the remote server.'
)
})
it('treats only codes that prove the target is gone as permanent', () => {
for (const code of [
'worktree_not_found_on_server',
'repo_not_found',
'capability_unsupported'
]) {
expect(isPermanentRemoteBrowserStreamFailure(rpcError(code))).toBe(true)
}
expect(isPermanentRemoteBrowserStreamFailure(remoteBrowserStreamUnsupportedError())).toBe(true)
})
// The regression guard. `selector_not_found` means "I could not resolve this right now" — its
// producer is a live worktree scan behind a 1s TTL cache, and the connectionId-gated fallback that
// shields SSH repos from scan lag does not cover purely local ones. Treating it as permanent
// stopped retries on a blip and stranded the pane, which is the bug this area exists to prevent.
it('does not treat an unresolved selector as proof the target is gone', () => {
expect(isPermanentRemoteBrowserStreamFailure(rpcError('selector_not_found'))).toBe(false)
expect(resolveRemoteBrowserStreamFailure(rpcError('selector_not_found')).shouldRetry).toBe(true)
})
it('keeps unknown and transport failures retryable', () => {
for (const code of ['runtime_unavailable', 'runtime_timeout', 'socket_closed']) {
expect(isPermanentRemoteBrowserStreamFailure(rpcError(code))).toBe(false)
}
expect(isPermanentRemoteBrowserStreamFailure(new Error('no code at all'))).toBe(false)
expect(isPermanentRemoteBrowserStreamFailure(null)).toBe(false)
})
it('keeps a permanent failure specific and replaces only raw transient text', () => {
const permanent = resolveRemoteBrowserStreamFailure(
rpcError('worktree_not_found_on_server', 'worktree is gone')
)
// Its own message says something true that a generic "lost connection" would not.
expect(permanent.message).toBe('worktree is gone')
expect(permanent.shouldRetry).toBe(false)
expect(permanent.logRawError).toBe(false)
const transient = resolveRemoteBrowserStreamFailure(
rpcError('runtime_unavailable', 'Runtime environment is manually disconnected.')
)
expect(transient.message).toBe('Lost connection to the remote server.')
expect(transient.shouldRetry).toBe(true)
// Dropped from the UI but not lost: nothing else records it.
expect(transient.logRawError).toBe(true)
})
it('uses localized copy for permanent failures without an Error message', () => {
const failure = resolveRemoteBrowserStreamFailure({
code: 'worktree_not_found_on_server'
})
expect(failure.message).toBe('Failed to restart remote browser stream.')
expect(failure.shouldRetry).toBe(false)
})
})