fix(desktop): OAuth REST preflight gets its own dial budget
Sharing the remainder of SWITCH_DIAL_TIMEOUT_MS meant a slow-but-successful socket dial left the preflight 0 ms and the switch failed as "Timed out connecting" although the socket had just opened.
This commit is contained in:
@@ -631,33 +631,6 @@ describe('selectConnection', () => {
|
||||
|
||||
expect(ensureGatewayAgent).toHaveBeenCalledWith('homelab', 'default', expect.anything())
|
||||
expect($connection.get()?.connectionId).toBe('homelab')
|
||||
|
||||
// REST readiness shares the dial budget, rather than adding a second
|
||||
// full timeout. A late response has no activation rights after failure.
|
||||
setConnectionsRegistry({
|
||||
...registry,
|
||||
connections: registry.connections.map(connection =>
|
||||
connection.id === 'work-vps' ? { ...connection, authMode: 'oauth' } : connection
|
||||
)
|
||||
})
|
||||
const dial = deferred()
|
||||
const rest = deferred<{ profiles: never[] }>()
|
||||
openGatewayAgent.mockImplementationOnce(() => dial.promise)
|
||||
api.mockImplementationOnce(() => rest.promise)
|
||||
$activeSessionId.set('homelab-runtime')
|
||||
const restOutcome = selectConnection('work-vps').catch((error: Error) => error.message)
|
||||
await vi.advanceTimersByTimeAsync(10_000)
|
||||
dial.resolve()
|
||||
await vi.advanceTimersByTimeAsync(0)
|
||||
expect(api).toHaveBeenCalledTimes(1)
|
||||
await vi.advanceTimersByTimeAsync(10_000)
|
||||
expect(await restOutcome).toMatch(/Timed out connecting to "Work VPS"/)
|
||||
expect($activeSessionId.get()).toBe('homelab-runtime')
|
||||
expect($pendingConnectionId.get()).toBeNull()
|
||||
rest.resolve({ profiles: [] })
|
||||
await vi.advanceTimersByTimeAsync(0)
|
||||
expect(beginGatewaySwitch).toHaveBeenCalledTimes(1)
|
||||
expect($activeConnectionId.get()).toBe('homelab')
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
|
||||
@@ -335,7 +335,6 @@ export async function selectConnection(connectionId: string, options: SelectConn
|
||||
let token = null as GatewaySwitchToken | null
|
||||
|
||||
try {
|
||||
const preflightDeadline = Date.now() + SWITCH_DIAL_TIMEOUT_MS
|
||||
// Phase 1 — open the target's socket; the active route is untouched.
|
||||
// Always use the explicit registry route. `local` must mean This device,
|
||||
// and a registry primary can differ from a legacy per-profile override.
|
||||
@@ -357,12 +356,11 @@ export async function selectConnection(connectionId: string, options: SelectConn
|
||||
(targetConnection.kind === 'remote' || targetConnection.kind === 'cloud')
|
||||
) {
|
||||
// Retained sockets can outlive cookie/native OAuth REST auth. Prove a
|
||||
// protected read on the destination before wiping; a socket alone is
|
||||
// enough for the unchanged local/long-lived-token path. Keep the exact
|
||||
// protected read on the destination before wiping. Keep the exact
|
||||
// failure for caller UX (network failures must not become sign-in errors).
|
||||
await withTimeout(
|
||||
getProfiles({ connectionId, profile: targetProfile }),
|
||||
Math.max(0, preflightDeadline - Date.now()),
|
||||
SWITCH_DIAL_TIMEOUT_MS,
|
||||
`Timed out connecting to "${targetConnection.label}".`
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user