mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 02:27:43 +00:00
* 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>
93 lines
4.0 KiB
TypeScript
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)
|
|
})
|
|
})
|