test: remove low-value browser, hook and SSH cases (#40423)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-11-09 09:04:22 -08:00
committed by GitHub
co-authored by OrcaWin
parent 9a45e8bdfb
commit da8396395c
18 changed files with 28 additions and 1705 deletions
@@ -15,7 +15,6 @@ src/main/managed-server/migration/cutover/orcad-migration-delta-move.ts
src/main/managed-server/migration/linking/orcad-migration-repository-link.test.ts
src/main/ssh/migration-source/orcad-migration-terminal-gate.test.ts
src/main/ssh/migration-source/orcad-migration-terminal-gate.ts
src/main/ssh/host-server/ssh-host-server-on-connect.test.ts
src/main/ssh/host-server/ssh-host-server-on-connect.ts
src/main/ssh/targets/ssh-target-orcad-preflight.ts
src/preload/api-types.ts
@@ -927,12 +927,10 @@
"src/main/browser/identity/mode/browser-identity-mode-store.test.ts": 95,
"src/main/browser/guest-lifecycle/prompts/browser-javascript-dialogs.test.ts": 54,
"src/main/browser/guest-lifecycle/manager/viewport/browser-manager-annotation-bridge.test.ts": 603,
"src/main/browser/guest-lifecycle/manager/navigation/browser-manager-auth-user-agent.test.ts": 555,
"src/main/browser/guest-lifecycle/manager/downloads/browser-manager-client-hosted-downloads.test.ts": 670,
"src/main/browser/guest-lifecycle/manager/registration/browser-manager-destroyed-guest.test.ts": 633,
"src/main/browser/guest-lifecycle/manager/devtools/browser-manager-devtools-retirement.test.ts": 551,
"src/main/browser/guest-lifecycle/manager/downloads/browser-manager-downloads.test.ts": 592,
"src/main/browser/guest-lifecycle/manager/grab/browser-manager-grab-capture.test.ts": 516,
"src/main/browser/guest-lifecycle/manager/grab/browser-manager-grab-mode.test.ts": 567,
"src/main/browser/guest-lifecycle/manager/grab/browser-manager-grab-selection.test.ts": 584,
"src/main/browser/guest-lifecycle/manager/browser-manager-guest-visibility.test.ts": 543,
@@ -942,7 +940,6 @@
"src/main/browser/guest-lifecycle/manager/popups/browser-manager-popup-routing.test.ts": 575,
"src/main/browser/guest-lifecycle/manager/viewport/browser-manager-tab-identity.test.ts": 619,
"src/main/browser/guest-lifecycle/manager/viewport/browser-manager-viewport-override.test.ts": 597,
"src/main/browser/guest-lifecycle/manager/viewport/browser-manager-viewport-ownership.test.ts": 550,
"src/main/browser/network/execution-routes/browser-network-deferred-socket.test.ts": 101,
"src/main/browser/network/execution-routes/browser-network-execution-route.test.ts": 272,
"src/main/browser/network/tunnels/browser-network-tunnel-client.test.ts": 127,
@@ -9,7 +9,6 @@ import { AgentHookServer } from '../../agent-hook-server'
import { buildBody, PANE, postHookEvent } from '../server.test-fixtures'
import { ClaudeHelperKillPoller } from '../../../../../shared/agent-hook-listener/providers/claude-helper-kill-poller'
import '../../../../../shared/agent-hook-listener/providers/claude-transcript-decoder'
import * as cursor from '../../../../../shared/agent-hook-listener/providers/claude-transcript-cursor'
import '../../../../../shared/agent-hook-listener/transcripts/agent-transcript-poll-scheduler'
// Complete captured lifecycle records, with runtime replay seeded by real persisted HTTP state.
@@ -369,38 +368,6 @@ it('a real failed bind parks without resources and explicit retry delivers its f
expect(p.row()?.workingMode).toBe('monitoring')
})
it.each(['relay-spool', 'desktop-spool'])(
'stop during %s recovery invalidates its parked owner',
async (kind) => {
const read = vi.spyOn(cursor, 'readClaudeTranscriptChunk')
const p = await setup(kind, 'accepted-before-bind', {
stopBeforeBind: true,
beforeStart: () => read.mockClear()
})
expect(p.entries.size).toBe(0)
expect(read).not.toHaveBeenCalled()
expect(p.poller['scheduler']['entries'].size).toBe(0)
}
)
it.each(['desktop-http', 'relay-http', 'desktop-spool', 'relay-spool'])(
'refused %s admission creates no observation or wake',
async (kind) => {
const capture = vi.spyOn(ClaudeHelperKillPoller.prototype, 'schedule')
const p = await setup(kind, 'before-start', {
retired: true,
beforeStart: () => capture.mockClear()
})
expect(p.entries.size).toBe(0)
expect(capture).not.toHaveBeenCalled()
if (kind === 'desktop-spool') {
expect(p.row()?.restoredUnconfirmed).toBe(true)
} else {
expect(p.row()).toBeUndefined()
}
}
)
it('a held local child permission cannot reset the admitted owner', async () => {
const p = await setup('desktop-http', 'after-start')
await p.post({
@@ -27,105 +27,6 @@ afterEach(() => {
})
describe('AgentHookServer listener replay', () => {
it('does not infer an interrupt while a subagent child is still working', () => {
vi.useFakeTimers()
vi.setSystemTime(1_000)
try {
const server = new AgentHookServer()
server.ingestRemote(
{
paneKey: PANE,
tabId: 'tab-1',
worktreeId: 'wt-1',
payload: {
state: 'working',
prompt: 'review loop',
agentType: 'claude',
// Child-driven working status cannot prove Ctrl+C ended the lead's turn.
subagents: [{ id: 'a1', state: 'working', startedAt: 900 }]
}
},
'conn-1'
)
const baseline = server.getStatusSnapshot()[0]
vi.setSystemTime(1_500)
const applied = server.inferInterrupt({
paneKey: PANE,
baselineUpdatedAt: baseline.receivedAt,
baselineStateStartedAt: baseline.stateStartedAt,
baselinePrompt: 'review loop',
baselineAgentType: 'claude',
intent: 'ctrl-c'
})
expect(applied).toBe(false)
expect(server.getStatusSnapshot()[0]).toMatchObject({ state: 'working' })
} finally {
vi.useRealTimers()
}
})
it('does not infer an interrupt while Claude reports a background shell and the row carries no main agent fact', () => {
// Legacy hosts omit mainAgent, so working may mean an idle lead held open by live shell work.
vi.useFakeTimers()
vi.setSystemTime(1_000)
try {
const server = new AgentHookServer()
server.ingestRemote(
{
paneKey: PANE,
tabId: 'tab-1',
worktreeId: 'wt-1',
claudeRunningNonAgentTask: true,
payload: { state: 'working', prompt: 'run in background', agentType: 'claude' }
},
'conn-1'
)
let baseline = server.getStatusSnapshot()[0]
vi.setSystemTime(1_500)
expect(
server.inferInterrupt({
paneKey: PANE,
baselineUpdatedAt: baseline.receivedAt,
baselineStateStartedAt: baseline.stateStartedAt,
baselinePrompt: 'run in background',
baselineAgentType: 'claude',
intent: 'ctrl-c'
})
).toBe(false)
expect(server.getStatusSnapshot()[0]).toMatchObject({ state: 'working' })
vi.setSystemTime(2_000)
server.ingestRemote(
{
paneKey: PANE,
tabId: 'tab-1',
worktreeId: 'wt-1',
claudeRunningNonAgentTask: false,
payload: { state: 'working', prompt: 'run in background', agentType: 'claude' }
},
'conn-1'
)
baseline = server.getStatusSnapshot()[0]
vi.setSystemTime(2_500)
expect(
server.inferInterrupt({
paneKey: PANE,
baselineUpdatedAt: baseline.receivedAt,
baselineStateStartedAt: baseline.stateStartedAt,
baselinePrompt: 'run in background',
baselineAgentType: 'claude',
intent: 'ctrl-c'
})
).toBe(true)
} finally {
vi.useRealTimers()
}
})
it('infers a cancel of a live Claude main agent turn and keeps the shell it leaves running as monitoring', () => {
vi.useFakeTimers()
vi.setSystemTime(1_000)
@@ -443,43 +344,6 @@ describe('AgentHookServer listener replay', () => {
}
})
it('does not apply Claude background metadata from a rejected remote status', () => {
const server = new AgentHookServer()
server.ingestRemote(
{
paneKey: PANE,
hookEventName: 'PermissionRequest',
claudeRunningNonAgentTask: true,
payload: {
state: 'waiting',
prompt: 'approve shell',
agentType: 'claude',
toolName: 'Bash'
}
},
'conn-1'
)
const waiting = server.getStatusSnapshot()[0]
server.ingestRemote(
{
paneKey: PANE,
hookEventName: 'PreToolUse',
claudeRunningNonAgentTask: false,
payload: {
state: 'working',
prompt: 'approve shell',
agentType: 'claude',
toolName: 'OtherTool'
}
},
'conn-1'
)
expect(server.getStatusSnapshot()[0]).toEqual(waiting)
expect(server._getStateForTests().claudeRunningNonAgentTaskPaneKeys.has(PANE)).toBe(true)
})
it('preserves an inferred interrupted row when OpenCode immediately reports SessionIdle', () => {
vi.useFakeTimers()
vi.setSystemTime(1_000)
@@ -543,90 +407,4 @@ describe('AgentHookServer listener replay', () => {
vi.useRealTimers()
}
})
it.each(['opencode', 'opencode2', 'copilot'] as const)(
'rejects single plain Escape inference for %s',
(agentType) => {
vi.useFakeTimers()
vi.setSystemTime(1_000)
try {
const server = new AgentHookServer()
server.ingestRemote(
{
paneKey: PANE,
tabId: 'tab-1',
worktreeId: 'wt-1',
payload: { state: 'working', prompt: 'long task', agentType }
},
'conn-1'
)
const baseline = server.getStatusSnapshot()[0]
vi.setSystemTime(1_500)
const applied = server.inferInterrupt({
paneKey: PANE,
baselineUpdatedAt: baseline.receivedAt,
baselineStateStartedAt: baseline.stateStartedAt,
baselinePrompt: 'long task',
baselineAgentType: agentType,
intent: 'plain-escape'
})
expect(applied).toBe(false)
expect(server.getStatusSnapshot()).toEqual([
expect.objectContaining({
state: 'working',
prompt: 'long task',
agentType
})
])
} finally {
vi.useRealTimers()
}
}
)
it.each(['opencode', 'opencode2', 'copilot'] as const)(
'accepts double plain Escape inference for %s',
(agentType) => {
vi.useFakeTimers()
vi.setSystemTime(1_000)
try {
const server = new AgentHookServer()
server.ingestRemote(
{
paneKey: PANE,
tabId: 'tab-1',
worktreeId: 'wt-1',
payload: { state: 'working', prompt: 'long task', agentType }
},
'conn-1'
)
const baseline = server.getStatusSnapshot()[0]
vi.setSystemTime(1_500)
const applied = server.inferInterrupt({
paneKey: PANE,
baselineUpdatedAt: baseline.receivedAt,
baselineStateStartedAt: baseline.stateStartedAt,
baselinePrompt: 'long task',
baselineAgentType: agentType,
intent: 'plain-escape',
inputCount: 2
})
expect(applied).toBe(true)
expect(server.getStatusSnapshot()).toEqual([
expect.objectContaining({
state: 'done',
prompt: 'long task',
agentType,
interrupted: true
})
])
} finally {
vi.useRealTimers()
}
}
)
})
@@ -85,20 +85,6 @@ describe('a pane without a proven owner', () => {
expect(attempts()).toBe(1)
})
it('acquires a Claude started through a shell script named claude', async () => {
const server = await createServer()
runningPane(
49960,
[49960, 101, 49960, 'bash /Users/u/bin/claude', 'S+'],
[49969, 49960, 49960, 'node /opt/homebrew/bin/claude', 'S+']
)
observe(server, 'waiting')
await hook(server, 'SessionStart', { session: 'session-a' })
await hook(server, 'UserPromptSubmit', { session: 'session-a', prompt: 'Wrapped task' })
expect(server.row()).toMatchObject({ state: 'working', prompt: 'Wrapped task' })
expect(server.presence()).toMatchObject({ process: capture(49969), session: 'session-a' })
})
it.each([['parent'], [undefined]])(
'keeps a detached nested run out of an unproven owner in session %s, through its exit',
async (session) => {
@@ -259,25 +245,6 @@ describe('terminal reports and Orca restatements', () => {
expect(server.row()).toMatchObject({ agentType: 'codex', prompt: 'Next task' })
expect(server.presence()).toEqual({ agent: 'codex', session: 'cx' })
})
it('keeps a suspended owner through a terminal report', async () => {
const server = await createServer()
runningPane(49969, CLAUDE)
await hook(server, 'SessionStart', { session: 'session-a' })
probe.mockImplementationOnce(async (_identity, _read, _platform, onSuspension) => {
onSuspension?.(true)
return 'unverifiable'
})
await server.checkAgentPresence(PANE)
observe(server, 'working')
expect(server.presence()).toEqual({
agent: 'claude',
process: capture(49969),
session: 'session-a',
sessionOwner: true,
suspended: true
})
})
})
describe('a retired owner', () => {
@@ -249,30 +249,6 @@ describe('startup spool replay of panes without a proven owner', () => {
release()
}
)
it('answers past 64 unfinished reads as unknown, and reads that pane on its next turn', async () => {
const panes = [...Array.from({ length: 69 }, (_, index) => paneOf(index)), PANE]
const userDataPath = spoolDir(
panes.map((paneKey) =>
spooled(paneKey, 49969, { hook_event_name: 'UserPromptSubmit', prompt: 'task' })
)
)
runningPane(49969, CLAUDE)
const { release, requested } = rootAnswers(() => true)
const { server, started } = launchServer(userDataPath)
await requested
await vi.advanceTimersByTimeAsync(500)
await started
expect(attempts()).toBe(64)
vi.useRealTimers()
release()
await settled(64)
expect(server.presence(panes[0])?.fallback).toBeUndefined()
expect(server.presence()).toMatchObject({ process: capture(49969), fallback: true })
await hook(server, 'UserPromptSubmit', { prompt: 'next' })
expect(attempts()).toBe(65)
expect(server.presence()?.fallback).toBeUndefined()
})
})
describe('WSL relay startup spool replay', () => {
@@ -1,99 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { GRAB_BUDGET } from '../../../../../shared/browser/element-grab/browser-grab-types'
const grabMocks = vi.hoisted(() => ({
webContentsFromIdMock: vi.fn(),
guestOnMock: vi.fn(),
guestOffMock: vi.fn(),
guestSetBackgroundThrottlingMock: vi.fn(),
guestSetWindowOpenHandlerMock: vi.fn(),
guestExecuteJavaScriptMock: vi.fn(),
guestExecuteJavaScriptInIsolatedWorldMock: vi.fn(),
guestIsDestroyedMock: vi.fn(() => false),
guestGetZoomFactorMock: vi.fn(() => 1),
guestCapturePageMock: vi.fn(),
menuBuildFromTemplateMock: vi.fn(),
rendererSendMock: vi.fn(),
rendererIsDestroyedMock: vi.fn(() => false)
}))
vi.mock('electron', () => ({
clipboard: { writeText: vi.fn() },
shell: { openExternal: vi.fn() },
Menu: { buildFromTemplate: grabMocks.menuBuildFromTemplateMock },
webContents: { fromId: grabMocks.webContentsFromIdMock }
}))
import { browserManager } from '../browser-manager'
import { resetGrabTestEnvironment } from './browser-manager-grab-test-harness'
const { guestCapturePageMock, guestExecuteJavaScriptMock } = grabMocks
describe('browserManager grab operations', () => {
let guest: Electron.WebContents
beforeEach(() => {
guest = resetGrabTestEnvironment(grabMocks)
})
describe('captureSelectionScreenshot', () => {
it('captures, crops, and converts screenshot dimensions back to CSS pixels', async () => {
const cropMock = vi.fn(() => ({
toPNG: vi.fn(() => Buffer.from('png-data'))
}))
guestCapturePageMock.mockResolvedValue({
isEmpty: vi.fn(() => false),
getSize: vi.fn(() => ({ width: 2000, height: 1000 })),
crop: cropMock
})
guestExecuteJavaScriptMock.mockImplementation(async (script: string) =>
script === 'window.innerWidth' ? 1000 : undefined
)
const screenshot = await browserManager.captureSelectionScreenshot(
'tab-1',
{ x: 10, y: 20, width: 100, height: 50 },
guest
)
expect(cropMock).toHaveBeenCalledWith({ x: 20, y: 40, width: 200, height: 100 })
expect(screenshot).toEqual({
mimeType: 'image/png',
dataUrl: `data:image/png;base64,${Buffer.from('png-data').toString('base64')}`,
width: 100,
height: 50
})
expect(guestExecuteJavaScriptMock).toHaveBeenNthCalledWith(
1,
expect.stringContaining('__orcaGrab')
)
expect(guestExecuteJavaScriptMock).toHaveBeenNthCalledWith(
2,
expect.stringContaining('__orcaGrab')
)
expect(guestExecuteJavaScriptMock).toHaveBeenNthCalledWith(3, 'window.innerWidth')
})
it('omits screenshots that exceed the byte budget', async () => {
const oversizedBuffer = Buffer.alloc(GRAB_BUDGET.screenshotMaxBytes + 1)
guestCapturePageMock.mockResolvedValue({
isEmpty: vi.fn(() => false),
getSize: vi.fn(() => ({ width: 1000, height: 500 })),
crop: vi.fn(() => ({
toPNG: vi.fn(() => oversizedBuffer)
}))
})
guestExecuteJavaScriptMock.mockImplementation(async (script: string) =>
script === 'window.innerWidth' ? 1000 : undefined
)
const screenshot = await browserManager.captureSelectionScreenshot(
'tab-1',
{ x: 0, y: 0, width: 100, height: 50 },
guest
)
expect(screenshot).toBeNull()
})
})
})
@@ -1,5 +1,4 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { BrowserGrabPayload } from '../../../../../shared/browser/element-grab/browser-grab-types'
const grabMocks = vi.hoisted(() => ({
webContentsFromIdMock: vi.fn(),
@@ -35,57 +34,6 @@ import {
const { guestExecuteJavaScriptMock } = grabMocks
function makeValidGrabPayload(): BrowserGrabPayload {
return {
page: {
sanitizedUrl: 'https://example.com/',
title: 'Example',
viewportWidth: 1280,
viewportHeight: 720,
scrollX: 0,
scrollY: 0,
devicePixelRatio: 2,
capturedAt: '2026-04-10T00:00:00.000Z'
},
target: {
tagName: 'button',
selector: 'button',
textSnippet: 'Click me',
htmlSnippet: '<button>Click me</button>',
attributes: {},
accessibility: {
role: 'button',
accessibleName: 'Click me',
ariaLabel: null,
ariaLabelledBy: null
},
rectViewport: { x: 0, y: 0, width: 100, height: 40 },
rectPage: { x: 0, y: 0, width: 100, height: 40 },
computedStyles: {
display: 'block',
position: 'static',
width: '100px',
height: '40px',
margin: '0',
padding: '0',
color: '#000',
backgroundColor: '#fff',
border: 'none',
borderRadius: '0',
fontFamily: 'sans-serif',
fontSize: '14px',
fontWeight: '400',
lineHeight: '20px',
textAlign: 'left',
zIndex: 'auto'
}
},
nearbyText: [],
ancestorPath: ['div', 'body'],
screenshot: null
}
}
describe('browserManager grab operations', () => {
let guest: Electron.WebContents
@@ -94,19 +42,6 @@ describe('browserManager grab operations', () => {
})
describe('awaitGrabSelection', () => {
it('does not treat a valid payload message field as cancellation', async () => {
guestExecuteJavaScriptMock.mockResolvedValueOnce({
...makeValidGrabPayload(),
message: 'cancelled'
})
const result = await browserManager.awaitGrabSelection('tab-1', 'op-1', guest)
expect(result.kind).toBe('selected')
if (result.kind === 'selected') {
expect(result.payload.target.tagName).toBe('button')
}
})
it('main-side clamp redacts secret-bearing attribute values', async () => {
const mockPayload = {
page: {
@@ -1,254 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const browserMocks = vi.hoisted(() => ({
appGetPathMock: vi.fn(() => '/downloads'),
shellOpenExternalMock: vi.fn(),
browserWindowFromWebContentsMock: vi.fn(),
menuBuildFromTemplateMock: vi.fn(),
guestOffMock: vi.fn(),
guestOnMock: vi.fn(),
guestSetBackgroundThrottlingMock: vi.fn(),
guestSetWindowOpenHandlerMock: vi.fn(),
guestOpenDevToolsMock: vi.fn(),
webContentsFromIdMock: vi.fn(),
screenGetCursorScreenPointMock: vi.fn(() => ({ x: 0, y: 0 })),
openPopupWithOriginBarMock: vi.fn(),
processUserAgentMode: 'clean',
processUserAgent: 'Mozilla/5.0 (Test) Chrome/140.0.0.0'
}))
vi.mock('electron', () => ({
app: {
getPath: browserMocks.appGetPathMock
},
BrowserWindow: {
fromWebContents: browserMocks.browserWindowFromWebContentsMock
},
clipboard: { writeText: vi.fn() },
shell: { openExternal: browserMocks.shellOpenExternalMock },
Menu: {
buildFromTemplate: browserMocks.menuBuildFromTemplateMock
},
screen: {
getCursorScreenPoint: browserMocks.screenGetCursorScreenPointMock
},
webContents: {
fromId: browserMocks.webContentsFromIdMock
}
}))
vi.mock('../../popups/presentation/popup-origin-bar-window', () => ({
openPopupWithOriginBar: browserMocks.openPopupWithOriginBarMock
}))
vi.mock('../../../identity/browser-process-user-agent', () => ({
getBrowserProcessUserAgentIdentity: () => ({
mode: browserMocks.processUserAgentMode,
userAgent: browserMocks.processUserAgent,
electronUserAgent:
browserMocks.processUserAgentMode === 'native'
? browserMocks.processUserAgent
: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) orca/1.0.0 Chrome/134.0.0.0 Electron/30.0.0 Safari/537.36'
})
}))
vi.mock('../../../identity/browser-tab-identity', async (importOriginal) =>
(
await import('../viewport/browser-manager-desktop-ua-metadata-test-fixture')
).withPinnedDesktopUserAgentMetadata(await importOriginal<object>())
)
import { browserManager } from '../browser-manager'
import { googleAuthUserAgent } from '../../../identity/browser-google-auth-ua'
import {
guestBaseUserAgent,
rendererWebContentsId,
resetBrowserManagerMocks,
resetBrowserManagerState
} from '../browser-manager-test-harness'
import { pinnedDesktopUserAgentMetadata as desktopMetadata } from '../viewport/browser-manager-desktop-ua-metadata-test-fixture'
const {
guestOffMock,
guestOnMock,
guestSetBackgroundThrottlingMock,
guestSetWindowOpenHandlerMock,
guestOpenDevToolsMock,
webContentsFromIdMock
} = browserMocks
describe('browserManager', () => {
beforeEach(() => {
resetBrowserManagerMocks(browserMocks)
resetBrowserManagerState()
browserMocks.processUserAgentMode = 'clean'
browserMocks.processUserAgent = guestBaseUserAgent
})
afterEach(() => {
vi.useRealTimers()
})
// setUserAgent during will-redirect can abort and replay the request, breaking sign-in POST flows.
it('retargets the auth-host UA over CDP during a redirect instead of cancelling it', async () => {
const debuggerHandlers = new Map<string, () => void>()
let holdNextUserAgentOverride = false
let releaseHeldUserAgentOverride = (): void => {}
let rejectNextUserAgentOverride = false
const sendCommand = vi.fn((method: string) => {
if (method === 'Emulation.setUserAgentOverride' && holdNextUserAgentOverride) {
holdNextUserAgentOverride = false
return new Promise<void>((resolve) => {
releaseHeldUserAgentOverride = resolve
})
}
if (method === 'Emulation.setUserAgentOverride' && rejectNextUserAgentOverride) {
rejectNextUserAgentOverride = false
return Promise.reject(new Error('debugger detached'))
}
return Promise.resolve(undefined)
})
const guest = {
id: 418,
isDestroyed: vi.fn(() => false),
isCurrentlyAudible: vi.fn(() => false),
send: vi.fn(),
getType: vi.fn(() => 'webview'),
setBackgroundThrottling: guestSetBackgroundThrottlingMock,
setWindowOpenHandler: guestSetWindowOpenHandlerMock,
on: guestOnMock,
off: guestOffMock,
openDevTools: guestOpenDevToolsMock,
getURL: vi.fn(() => 'https://mail.google.com/'),
getUserAgent: vi.fn(() => guestBaseUserAgent),
setUserAgent: vi.fn(),
debugger: {
isAttached: vi.fn(() => true),
sendCommand,
on: vi.fn((event: string, handler: () => void) => debuggerHandlers.set(event, handler)),
off: vi.fn((event: string) => debuggerHandlers.delete(event))
},
session: { getUserAgent: vi.fn(() => guestBaseUserAgent) }
}
webContentsFromIdMock.mockReturnValue(guest)
browserManager.attachGuestPolicies(guest as never)
browserManager.registerGuest({
browserPageId: 'browser-redirect-ua',
webContentsId: guest.id,
rendererWebContentsId
})
const willRedirect = guestOnMock.mock.calls.find(
([event]) => event === 'will-redirect'
)?.[1] as (event: unknown, url: string, isInPlace: boolean, isMainFrame: boolean) => void
sendCommand.mockClear()
rejectNextUserAgentOverride = true
willRedirect(
{ preventDefault: vi.fn() },
'https://accounts.google.com/v3/signin/identifier',
false,
true
)
await expect(sendCommand.mock.results.at(-1)?.value).rejects.toThrow('debugger detached')
// Failed writes must remain retryable.
willRedirect(
{ preventDefault: vi.fn() },
'https://accounts.google.com/v3/signin/identifier',
false,
true
)
await expect(sendCommand.mock.results.at(-1)?.value).resolves.toBeUndefined()
expect(guest.setUserAgent).not.toHaveBeenCalled()
expect(sendCommand).toHaveBeenCalledWith('Emulation.setUserAgentOverride', {
userAgent: googleAuthUserAgent(),
userAgentMetadata: desktopMetadata
})
// Detaching clears Chromium's CDP overrides and invalidates the confirmed identity.
sendCommand.mockClear()
debuggerHandlers.get('detach')?.()
willRedirect(
{ preventDefault: vi.fn() },
'https://accounts.google.com/v3/signin/identifier',
false,
true
)
expect(sendCommand).toHaveBeenCalledWith('Emulation.setUserAgentOverride', {
userAgent: googleAuthUserAgent(),
userAgentMetadata: desktopMetadata
})
// getUserAgent omits the CDP override; clear it on exit to restore UA and client hints.
sendCommand.mockClear()
const didStartNavigation = guestOnMock.mock.calls.find(
([event]) => event === 'did-start-navigation'
)?.[1] as (event: unknown, url: string, isInPlace: boolean, isMainFrame: boolean) => void
rejectNextUserAgentOverride = true
didStartNavigation(null, 'https://example.com/', false, true)
await expect(sendCommand.mock.results.at(-1)?.value).rejects.toThrow('debugger detached')
didStartNavigation(null, 'https://example.com/', false, true)
expect(guest.setUserAgent).not.toHaveBeenCalled()
expect(sendCommand.mock.calls).toEqual([
['Emulation.setUserAgentOverride', { userAgent: '' }],
['Emulation.setUserAgentOverride', { userAgent: '' }]
])
// Newer confirmed writes outrank older pending writes.
debuggerHandlers.get('detach')?.()
holdNextUserAgentOverride = true
willRedirect(
{ preventDefault: vi.fn() },
'https://accounts.google.com/v3/signin/identifier',
false,
true
)
const olderHeldWrite = sendCommand.mock.results.at(-1)?.value as Promise<void>
didStartNavigation(null, 'https://example.com/', false, true)
await expect(sendCommand.mock.results.at(-1)?.value).resolves.toBeUndefined()
sendCommand.mockClear()
willRedirect(
{ preventDefault: vi.fn() },
'https://accounts.google.com/v3/signin/identifier',
false,
true
)
expect(sendCommand).toHaveBeenCalledWith('Emulation.setUserAgentOverride', {
userAgent: googleAuthUserAgent(),
userAgentMetadata: desktopMetadata
})
releaseHeldUserAgentOverride()
await olderHeldWrite
// A newer failure must preserve older pending writes.
debuggerHandlers.get('detach')?.()
holdNextUserAgentOverride = true
willRedirect(
{ preventDefault: vi.fn() },
'https://accounts.google.com/v3/signin/identifier',
false,
true
)
const heldWrite = sendCommand.mock.results.at(-1)?.value as Promise<void>
rejectNextUserAgentOverride = true
didStartNavigation(null, 'https://example.com/', false, true)
await expect(sendCommand.mock.results.at(-1)?.value).rejects.toThrow('debugger detached')
releaseHeldUserAgentOverride()
await heldWrite
sendCommand.mockClear()
didStartNavigation(null, 'https://example.com/', false, true)
expect(sendCommand).toHaveBeenCalledWith('Emulation.setUserAgentOverride', { userAgent: '' })
browserManager.unregisterGuest('browser-redirect-ua')
})
// Prefer a stale UA to aborting the redirect when CDP is unavailable.
// Direct navigation permits setUserAgent before request dispatch.
// Keep the process UA override until direct navigation can replace the stale auth base UA.
})
@@ -73,60 +73,6 @@ describe('browserManager', () => {
vi.useRealTimers()
})
it('drops a queued failure when a replacement navigation starts before registration', () => {
const rendererSendMock = vi.fn()
const guest = {
id: 407,
isDestroyed: vi.fn(() => false),
getType: vi.fn(() => 'webview'),
setBackgroundThrottling: guestSetBackgroundThrottlingMock,
isCurrentlyAudible: vi.fn(() => false),
setWindowOpenHandler: guestSetWindowOpenHandlerMock,
on: guestOnMock,
off: guestOffMock,
openDevTools: guestOpenDevToolsMock,
getURL: vi.fn(() => 'https://example.com/'),
...guestUaMethods()
}
webContentsFromIdMock.mockImplementation((id: number) =>
id === guest.id
? guest
: id === rendererWebContentsId
? { isDestroyed: vi.fn(() => false), send: rendererSendMock }
: null
)
browserManager.attachGuestPolicies(guest as never)
const didFailLoad = guestOnMock.mock.calls.find(
([event]) => event === 'did-fail-load'
)?.[1] as (
event: unknown,
errorCode: number,
errorDescription: string,
validatedUrl: string,
isMainFrame: boolean
) => void
const didStartNavigation = guestOnMock.mock.calls.find(
([event]) => event === 'did-start-navigation'
)?.[1] as (event: unknown, url: string, isInPlace: boolean, isMainFrame: boolean) => void
didFailLoad(null, -202, 'Certificate authority invalid', 'https://localhost:3443/', true)
didStartNavigation(null, 'https://example.com/', false, true)
expect(
browserManager.registerGuest({
browserPageId: 'browser-late-registration',
webContentsId: guest.id,
rendererWebContentsId
})
).toBe(true)
expect(rendererSendMock).not.toHaveBeenCalledWith(
'browser:guest-load-failed',
expect.anything()
)
expect(browserManager.getBrowserPageLoadError('browser-late-registration')).toBeNull()
})
it('restores a certificate failure when a retry navigation aborts before committing', () => {
const guest = {
id: 406,
@@ -32,21 +32,12 @@ vi.mock('../../popups/presentation/popup-origin-bar-window', () => ({
import { browserManager } from '../browser-manager'
import {
createDownloadItem,
getDownloadItemEventHandler,
rendererWebContentsId,
resetBrowserManagerMocks,
resetBrowserManagerState
} from '../browser-manager-test-harness'
const {
guestOffMock,
guestOnMock,
guestSetBackgroundThrottlingMock,
guestSetWindowOpenHandlerMock,
guestOpenDevToolsMock,
webContentsFromIdMock,
shellOpenExternalMock
} = browserMocks
const { webContentsFromIdMock } = browserMocks
let didCreateWindow: (window: { webContents: unknown }) => void = () => {}
const fakeGuest = (id: number) => ({
@@ -76,202 +67,6 @@ describe('browserManager popup child policies', () => {
vi.useRealTimers()
})
it('attaches guest policies to created popup child windows', () => {
const rendererSendMock = vi.fn()
const childSetBackgroundThrottlingMock = vi.fn()
const childSetWindowOpenHandlerMock = vi.fn()
const childOnMock = vi.fn()
const childOffMock = vi.fn()
const childOpenDevToolsMock = vi.fn()
const childGuest = {
id: 4040,
isDestroyed: vi.fn(() => false),
isCurrentlyAudible: vi.fn(() => false),
getType: vi.fn(() => 'webview'),
setBackgroundThrottling: childSetBackgroundThrottlingMock,
setWindowOpenHandler: childSetWindowOpenHandlerMock,
on: childOnMock,
off: childOffMock,
openDevTools: childOpenDevToolsMock,
getURL: vi.fn(() => 'https://identity.example.com/login')
}
const guest = {
id: 404,
isDestroyed: vi.fn(() => false),
isCurrentlyAudible: vi.fn(() => false),
getType: vi.fn(() => 'webview'),
setBackgroundThrottling: guestSetBackgroundThrottlingMock,
setWindowOpenHandler: guestSetWindowOpenHandlerMock,
on: guestOnMock,
off: guestOffMock,
openDevTools: guestOpenDevToolsMock
}
webContentsFromIdMock.mockImplementation((id: number) => {
if (id === guest.id) {
return guest
}
if (id === rendererWebContentsId) {
return { isDestroyed: vi.fn(() => false), send: rendererSendMock }
}
return null
})
browserManager.attachGuestPolicies(guest as never)
browserManager.registerGuest({
browserPageId: 'browser-1',
webContentsId: guest.id,
rendererWebContentsId
})
const didCreateWindowHandler = guestOnMock.mock.calls.find(
([event]) => event === 'did-create-window'
)?.[1] as ((window: { webContents: typeof childGuest }) => void) | undefined
expect(didCreateWindowHandler).toBeTypeOf('function')
didCreateWindowHandler?.({ webContents: childGuest })
expect(childSetBackgroundThrottlingMock).not.toHaveBeenCalled()
expect(childSetWindowOpenHandlerMock).toHaveBeenCalledTimes(1)
expect(childOnMock.mock.calls.filter(([event]) => event === 'did-create-window')).toHaveLength(
1
)
expect(childOnMock.mock.calls.filter(([event]) => event === 'will-navigate')).toHaveLength(1)
expect(childOnMock.mock.calls.filter(([event]) => event === 'will-redirect')).toHaveLength(1)
const childWindowOpenHandler = childSetWindowOpenHandlerMock.mock.calls[0][0] as (details: {
url: string
}) => { action: 'allow' | 'deny' }
expect(childWindowOpenHandler({ url: 'https://identity.example.com/login' })).toMatchObject({
action: 'allow'
})
expect(childWindowOpenHandler({ url: 'file:///etc/passwd' })).toEqual({ action: 'deny' })
expect(rendererSendMock).toHaveBeenCalledWith('browser:popup', {
browserPageId: 'browser-1',
origin: 'null',
action: 'blocked'
})
browserManager.notifyPermissionDenied({
guestWebContentsId: childGuest.id,
permission: 'notifications',
rawUrl: 'https://identity.example.com/login'
})
expect(rendererSendMock).toHaveBeenCalledWith('browser:permission-denied', {
browserPageId: 'browser-1',
permission: 'notifications',
origin: 'https://identity.example.com'
})
const childDidFailLoadHandler = childOnMock.mock.calls.find(
([event]) => event === 'did-fail-load'
)?.[1] as
| ((
event: Electron.Event,
errorCode: number,
errorDescription: string,
validatedURL: string,
isMainFrame: boolean
) => void)
| undefined
childDidFailLoadHandler?.(
{} as Electron.Event,
-105,
'Name not resolved',
'https://identity.example.com/unavailable',
true
)
expect(rendererSendMock).not.toHaveBeenCalledWith(
'browser:guest-load-failed',
expect.anything()
)
const childDownloadItem = createDownloadItem()
browserManager.handleGuestWillDownload({
guestWebContentsId: childGuest.id,
item: childDownloadItem
})
expect(rendererSendMock).toHaveBeenCalledWith(
'browser:download-requested',
expect.objectContaining({ browserPageId: 'browser-1' })
)
const childDownloadDoneHandler = getDownloadItemEventHandler(childDownloadItem, 'once', 'done')
childDownloadDoneHandler?.({} as Electron.Event, 'completed')
const managerState = browserManager as unknown as {
popupOwnerContextByGuestId: Map<number, unknown>
}
expect(managerState.popupOwnerContextByGuestId.has(childGuest.id)).toBe(true)
const cleanupChildOnMock = vi.fn()
const cleanupChildGuest = {
...childGuest,
id: 4041,
on: cleanupChildOnMock,
off: vi.fn(),
setBackgroundThrottling: vi.fn(),
setWindowOpenHandler: vi.fn()
}
const childDidCreateWindowHandler = childOnMock.mock.calls.find(
([event]) => event === 'did-create-window'
)?.[1] as ((window: { webContents: typeof cleanupChildGuest }) => void) | undefined
childDidCreateWindowHandler?.({ webContents: cleanupChildGuest })
expect(managerState.popupOwnerContextByGuestId.has(cleanupChildGuest.id)).toBe(true)
const cleanupChildWindowOpenHandler = cleanupChildGuest.setWindowOpenHandler.mock
.calls[0][0] as (details: { url: string }) => { action: 'allow' | 'deny' }
expect(
cleanupChildWindowOpenHandler({ url: 'https://identity.example.com/continue' })
).toMatchObject({ action: 'allow' })
const cleanupChildDestroyedHandler = cleanupChildOnMock.mock.calls.find(
([event]) => event === 'destroyed'
)?.[1] as (() => void) | undefined
cleanupChildDestroyedHandler?.()
expect(managerState.popupOwnerContextByGuestId.has(cleanupChildGuest.id)).toBe(false)
const replacementGuest = {
...guest,
id: 405,
on: vi.fn(),
off: vi.fn(),
setBackgroundThrottling: vi.fn(),
setWindowOpenHandler: vi.fn()
}
webContentsFromIdMock.mockImplementation((id: number) => {
if (id === guest.id) {
return guest
}
if (id === replacementGuest.id) {
return replacementGuest
}
if (id === rendererWebContentsId) {
return { isDestroyed: vi.fn(() => false), send: rendererSendMock }
}
return null
})
browserManager.attachGuestPolicies(replacementGuest as never)
browserManager.registerGuest({
browserPageId: 'browser-1',
webContentsId: replacementGuest.id,
rendererWebContentsId
})
expect(childWindowOpenHandler({ url: 'https://identity.example.com/next' })).toEqual({
action: 'deny'
})
expect(shellOpenExternalMock).toHaveBeenCalledWith('https://identity.example.com/next')
expect(managerState.popupOwnerContextByGuestId.has(childGuest.id)).toBe(false)
const childDestroyedHandler = childOnMock.mock.calls.find(
([event]) => event === 'destroyed'
)?.[1] as (() => void) | undefined
childDestroyedHandler?.()
expect(managerState.popupOwnerContextByGuestId.has(childGuest.id)).toBe(false)
browserManager.unregisterAll()
expect(childOffMock).toHaveBeenCalledWith('did-create-window', expect.any(Function))
expect(childOffMock).toHaveBeenCalledWith('will-navigate', expect.any(Function))
expect(childOffMock).toHaveBeenCalledWith('will-redirect', expect.any(Function))
})
it("saves a popup page's agent download to its path and refuses a second claim on the tab", async () => {
const opener = fakeGuest(406)
const popup = fakeGuest(4060)
@@ -67,21 +67,6 @@ describe('browser guest destruction ownership', () => {
mocks.guests.clear()
})
it('releases registered callbacks and ownership after 1000 distinct guest destructions', () => {
for (let index = 0; index < 1000; index++) {
register(`retired-${index}`).destroy()
}
expectRetainedCount(0)
})
it('keeps explicit unregister before destruction idempotent', () => {
const guest = register('explicit')
manager.unregisterGuest('explicit')
guest.destroy()
manager.unregisterGuest('explicit')
expectRetainedCount(0)
})
it('does not let a captured old destroyed callback retire a replacement guest', () => {
const old = register('replacement')
const [oldDestroyed] = old.listeners('destroyed')
@@ -93,42 +78,6 @@ describe('browser guest destruction ownership', () => {
expectRetainedCount(1)
})
it('cleans a popup without retiring its live primary page', () => {
const parent = register('popup-parent')
const popup = createGuest()
manager.attachGuestPolicies(popup.asWebContents(), {
rootGuestWebContentsId: parent.id,
browserTabId: 'popup-parent'
})
popup.destroy()
expect(manager.getGuestWebContentsId('popup-parent')).toBe(parent.id)
expectRetainedCount(1)
})
it('cleans policies for a guest destroyed before registration', () => {
const guest = createGuest()
manager.attachGuestPolicies(guest.asWebContents())
guest.destroy()
expectRetainedCount(0)
})
it('preserves live guest ownership when its renderer process needs reload recovery', () => {
const guest = register('renderer-recovery')
guest.emit('render-process-gone', {}, { reason: 'crashed', exitCode: 1 })
expect(manager.getGuestWebContentsId('renderer-recovery')).toBe(guest.id)
expect(manager.getSessionProfileIdForTab('renderer-recovery')).toBe('profile-1')
expectRetainedCount(1)
})
it('rebuilds ownership when a restored page registers its fresh guest', () => {
register('fresh-owner').destroy()
const replacement = register('fresh-owner')
expect(manager.getGuestWebContentsId('fresh-owner')).toBe(replacement.id)
expect(manager.getWorktreeIdForTab('fresh-owner')).toBe('worktree-1')
expect(manager.getSessionProfileIdForTab('fresh-owner')).toBe('profile-1')
expectRetainedCount(1)
})
it('preserves a sibling page using the same browser session profile', () => {
const retiring = register('retiring')
const sibling = register('sibling')
@@ -1,229 +0,0 @@
import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({
appGetPathMock: vi.fn(() => '/downloads'),
shellOpenExternalMock: vi.fn(),
browserWindowFromWebContentsMock: vi.fn(),
menuBuildFromTemplateMock: vi.fn(),
guestOffMock: vi.fn(),
guestOnMock: vi.fn(),
guestSetBackgroundThrottlingMock: vi.fn(),
guestSetWindowOpenHandlerMock: vi.fn(),
guestOpenDevToolsMock: vi.fn(),
webContentsFromIdMock: vi.fn(),
screenGetCursorScreenPointMock: vi.fn(() => ({ x: 0, y: 0 })),
openPopupWithOriginBarMock: vi.fn(),
processUserAgentMode: 'clean',
processUserAgent: ''
}))
vi.mock('electron', () => ({
app: { getPath: mocks.appGetPathMock },
BrowserWindow: { fromWebContents: mocks.browserWindowFromWebContentsMock },
clipboard: { writeText: vi.fn() },
shell: { openExternal: mocks.shellOpenExternalMock },
Menu: { buildFromTemplate: mocks.menuBuildFromTemplateMock },
screen: { getCursorScreenPoint: mocks.screenGetCursorScreenPointMock },
webContents: { fromId: mocks.webContentsFromIdMock }
}))
vi.mock('../../popups/presentation/popup-origin-bar-window', () => ({
openPopupWithOriginBar: mocks.openPopupWithOriginBarMock
}))
vi.mock('../../../identity/browser-process-user-agent', () => ({
getBrowserProcessUserAgentIdentity: () => ({
mode: mocks.processUserAgentMode,
userAgent: mocks.processUserAgent,
electronUserAgent:
mocks.processUserAgentMode === 'native'
? mocks.processUserAgent
: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) orca/1.0.0 Chrome/134.0.0.0 Electron/30.0.0 Safari/537.36'
})
}))
import { browserManager } from '../browser-manager'
import { resetBrowserManagerMocks, resetBrowserManagerState } from '../browser-manager-test-harness'
import {
createViewportGuestFactory,
GUEST_CLEAN_UA
} from './browser-manager-viewport-test-fixtures'
const makeGuest = createViewportGuestFactory(mocks)
const mobile = { width: 375, height: 667, deviceScaleFactor: 2, mobile: true }
const desktop = { width: 1440, height: 900, deviceScaleFactor: 1, mobile: false }
const guests = new Map<number, Record<string, unknown>>()
const registeredGuests = readViewportStateMap('webContentsIdByTabId')
const presetIntents = readViewportStateMap('viewportPresetByTabId')
const pendingOperations = readViewportStateMap('viewportOpsByTabId')
const cdpUserAgentOverrides = readViewportStateMap('cdpUserAgentOverrideStateByGuestId')
function readViewportStateMap(
name:
| 'webContentsIdByTabId'
| 'viewportPresetByTabId'
| 'viewportOpsByTabId'
| 'cdpUserAgentOverrideStateByGuestId'
): Map<unknown, unknown> {
const value: unknown = browserManager[name]
if (!(value instanceof Map)) {
throw new Error(`Expected manager state map: ${name}`)
}
return value
}
function register(tab: string, id: number) {
const handle = makeGuest(id)
guests.set(id, handle.guest)
expect(
browserManager.registerOffscreenGuest({
browserPageId: tab,
webContentsId: id
})
).toBe(true)
return handle
}
function pause(handle: ReturnType<typeof makeGuest>, method: string) {
const entered = Promise.withResolvers<void>()
const gate = Promise.withResolvers<void>()
let blocked = false
handle.debuggerSendCommand.mockImplementation((next) => {
if (!blocked && next === method) {
blocked = true
entered.resolve()
return gate.promise
}
return Promise.resolve()
})
return { entered: entered.promise, ...gate }
}
describe('browser viewport operation ownership', () => {
beforeEach(() => {
expect(process.env.ORCA_BACKGROUND_LAUNCH).toBe('1')
resetBrowserManagerMocks(mocks)
resetBrowserManagerState()
mocks.processUserAgentMode = 'clean'
mocks.processUserAgent = GUEST_CLEAN_UA
guests.clear()
mocks.webContentsFromIdMock.mockImplementation((id) => guests.get(id))
})
afterEach(() => {
browserManager.unregisterAll()
vi.restoreAllMocks()
})
it('does not recreate closed-tab UA intent after a late touch completion', async () => {
const handle = register('closed', 100)
const gate = pause(handle, 'Emulation.setTouchEmulationEnabled')
const result = browserManager.setViewportOverride('closed', mobile)
await gate.entered
browserManager.unregisterGuest('closed')
expect(presetIntents.size).toBe(0)
const isDestroyed = handle.guest.isDestroyed
expect(vi.isMockFunction(isDestroyed)).toBe(true)
if (vi.isMockFunction(isDestroyed)) {
isDestroyed.mockReturnValue(true)
}
gate.resolve()
await expect(result).resolves.toBe(false)
expect(registeredGuests.size).toBe(0)
expect(presetIntents.size).toBe(0)
expect(cdpUserAgentOverrides.size).toBe(0)
})
it('preserves replacement desktop intent after an old clear fails', async () => {
const handle = register('replacement', 102)
await browserManager.setViewportOverride('replacement', mobile)
const gate = pause(handle, 'Emulation.setUserAgentOverride')
const result = browserManager.setViewportOverride('replacement', null)
await gate.entered
browserManager.unregisterGuest('replacement')
register('replacement', 103)
await expect(browserManager.setViewportOverride('replacement', desktop)).resolves.toBe(true)
gate.reject(new Error('Old target closed'))
await expect(result).resolves.toBe(false)
expect(registeredGuests.get('replacement')).toBe(103)
expect(presetIntents.get('replacement')).toEqual({
guestWebContentsId: 103,
requested: desktop,
applied: desktop
})
})
// Requested and applied metrics may clear while the separate UA override remains tracked.
it('same-owner clear failure keeps the standing mobile override tracked', async () => {
const handle = register('same-owner', 106)
await browserManager.setViewportOverride('same-owner', mobile)
const gate = pause(handle, 'Emulation.setUserAgentOverride')
const result = browserManager.setViewportOverride('same-owner', null)
await gate.entered
gate.reject(new Error('Protocol error'))
await expect(result).resolves.toBe(false)
expect(presetIntents.get('same-owner')).toEqual({
guestWebContentsId: 106,
requested: null,
applied: null
})
expect(handle.presentedUserAgent()).toContain('iPhone')
expect(cdpUserAgentOverrides.has(106)).toBe(true)
})
it('old queued operations cannot remove or join a replacement promise tail', async () => {
const old = register('queued-replacement', 114)
const oldGate = pause(old, 'Emulation.setTouchEmulationEnabled')
const first = browserManager.setViewportOverride('queued-replacement', mobile)
const second = browserManager.setViewportOverride('queued-replacement', null)
await oldGate.entered
browserManager.unregisterGuest('queued-replacement')
const replacement = register('queued-replacement', 115)
const newGate = pause(replacement, 'Emulation.setTouchEmulationEnabled')
const replacementFirst = browserManager.setViewportOverride('queued-replacement', desktop)
const replacementSecond = browserManager.setViewportOverride('queued-replacement', mobile)
await newGate.entered
const tail = pendingOperations.get('queued-replacement')
oldGate.resolve()
await expect(first).resolves.toBe(false)
await expect(second).resolves.toBe(false)
expect(pendingOperations.get('queued-replacement')).toBe(tail)
newGate.resolve()
await expect(replacementFirst).resolves.toBe(true)
await expect(replacementSecond).resolves.toBe(true)
expect(pendingOperations.size).toBe(0)
expect(presetIntents.get('queued-replacement')).toEqual({
guestWebContentsId: 115,
requested: mobile,
applied: mobile
})
expect(replacement.presentedUserAgent()).toContain('iPhone')
})
it('normal same-owner toggles preserve last-requested order and remove the promise tail', async () => {
const handle = register('serialized', 116)
const gate = pause(handle, 'Emulation.setTouchEmulationEnabled')
const first = browserManager.setViewportOverride('serialized', mobile)
await gate.entered
const second = browserManager.setViewportOverride('serialized', desktop)
const third = browserManager.setViewportOverride('serialized', null)
gate.resolve()
expect(await Promise.all([first, second, third])).toEqual([true, true, true])
// Clear mobile identity when desktop metrics replace the mobile viewport.
expect(handle.debuggerSendCommand.mock.calls.map(([method]) => method)).toEqual([
'Emulation.setDeviceMetricsOverride',
'Emulation.setTouchEmulationEnabled',
'Emulation.setUserAgentOverride',
'Emulation.setDeviceMetricsOverride',
'Emulation.setTouchEmulationEnabled',
'Emulation.setUserAgentOverride',
'Emulation.clearDeviceMetricsOverride',
'Emulation.setTouchEmulationEnabled'
])
expect(handle.presentedUserAgent()).toBe(GUEST_CLEAN_UA)
expect(pendingOperations.size).toBe(0)
expect(presetIntents.get('serialized')).toEqual({
guestWebContentsId: 116,
requested: null,
applied: null
})
})
})
@@ -5,8 +5,7 @@ import {
type AnyAuthMethod,
type AuthenticationType,
type AuthHandlerMiddleware,
type ConnectConfig,
type ParsedKey
type ConnectConfig
} from 'ssh2'
vi.mock('os', () => ({
@@ -31,7 +30,6 @@ vi.mock('./ssh-identity-file-read', () => ({
}))
import { buildConnectConfig } from '../connection/ssh-connect-config'
import { getPassphrasePrivateKeyPath } from './ssh-private-key-authentication'
import '../system-transport/system-ssh-args'
import { walkInitialAuthLadder } from '../connection/testing/ssh-connection-test-fixtures'
import type { SshTarget } from '../../../shared/ssh-types'
@@ -144,33 +142,6 @@ describe('ordered SSH private-key authentication', () => {
expect(nextAuth(config, false)).toBe('keyboard-interactive')
})
it('keeps explicit manual keys and unresolved imported keys as singular overrides', () => {
const manual = buildConnectConfig(
makeTarget({
source: 'manual',
configHost: 'manual.example.com',
host: 'manual.example.com',
identityFile: '/keys/manual'
}),
makeResolved(),
{ includeAgent: false, includePrivateKey: true }
)
const unresolvedImport = buildConnectConfig(makeTarget(), null, {
includeAgent: false,
includePrivateKey: true
})
expect(manual.privateKey).toEqual(Buffer.from('/keys/manual'))
expect(unresolvedImport.privateKey).toEqual(Buffer.from('/keys/stale-imported'))
// Single-key targets also need per-stage keyboard-interactive restart for MFA.
expect(nextAuth(manual, true)).toMatchObject({ type: 'none' })
expect(nextAuth(manual, false)).toMatchObject({
type: 'publickey',
key: Buffer.from('/keys/manual')
})
expect(nextAuth(manual, false)).toBe('keyboard-interactive')
})
it('keeps the password challenge on an agent attempt that carries a key, because no key retry follows it', () => {
vi.stubEnv('SSH_AUTH_SOCK', '/tmp/agent.sock')
// Real key bytes, so '/keys/explicit' counts as unencrypted and IdentitiesOnly builds its agent.
@@ -216,35 +187,11 @@ describe('ordered SSH private-key authentication', () => {
expect(partialSuccessAuth(config, ['keyboard-interactive'])).toBe(false)
})
it('offers every resolved key for a manually owned config-picker target', () => {
const config = buildConnectConfig(
makeTarget({
source: 'manual',
configHost: 'prod',
host: 'prod.internal',
identityFile: undefined
}),
makeResolved(),
{ includeAgent: false, includePrivateKey: true }
)
expect(nextAuth(config, true)).toMatchObject({ type: 'none' })
expect(nextAuth(config, false)).toMatchObject({
type: 'publickey',
key: Buffer.from('/keys/unauthorized-first')
})
expect(nextAuth(config, false)).toMatchObject({
type: 'publickey',
key: Buffer.from('/keys/authorized-second')
})
})
it('resets ordered authentication for credential retries without extra key reads', () => {
it('resets ordered authentication for credential retries', () => {
const config = buildConnectConfig(makeTarget(), makeResolved(), {
includeAgent: false,
includePrivateKey: true
})
const readsAfterResolution = mockReadFileSync.mock.calls.length
expect(nextAuth(config, true)).toMatchObject({ type: 'none' })
config.password = 'retry-password'
@@ -257,26 +204,5 @@ describe('ordered SSH private-key authentication', () => {
type: 'publickey',
key: Buffer.from('/keys/unauthorized-first')
})
expect(mockReadFileSync).toHaveBeenCalledTimes(readsAfterResolution)
})
it('keeps encrypted-key prompts tied to the matching fresh identity path', () => {
vi.spyOn(utils, 'parseKey').mockImplementation((key) => {
if (
Buffer.from(key as Buffer)
.toString()
.includes('encrypted-second')
) {
return new Error('Encrypted private OpenSSH key detected, but no passphrase given')
}
return { isPrivateKey: () => true } as ParsedKey
})
const config = buildConnectConfig(
makeTarget(),
makeResolved({ identityFile: ['/keys/first', '/keys/encrypted-second'] }),
{ includeAgent: false, includePrivateKey: true }
)
expect(getPassphrasePrivateKeyPath(config)).toBe('/keys/encrypted-second')
})
})
@@ -7,9 +7,6 @@ import type { SFTPWrapper } from 'ssh2'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { uploadBuffer, writeStringViaSftp } from './sftp-upload'
import { uploadFile } from './sftp-file-upload'
import { writeRemoteInstallFile } from '../../../managed-server/install/remote-install-transfers'
import type { SshConnection } from '../../connection/ssh-connection'
import { getRemoteHostPlatform } from '../../remote-host/ssh-remote-platform'
function sftpNoSuchFileError(): Error {
return Object.assign(new Error('file does not exist'), { code: 2 })
@@ -78,25 +75,3 @@ describe('late SFTP stream errors', () => {
)
})
})
describe('sandboxed SFTP subsystem diagnosis', () => {
it('leaves a permission refusal as itself rather than blaming a chroot', async () => {
// Permission denial names visible paths; mislabeling it as sandboxing sends users to fix ProxyJump for chmod.
const conn = {
writeFile: () => Promise.reject(Object.assign(new Error('permission denied'), { code: 3 }))
} as unknown as SshConnection
const failure: unknown = await writeRemoteInstallFile(
conn,
getRemoteHostPlatform('linux-x64'),
'/home/user/.orca-remote/relay-1/.version',
'v1'
).then(
() => null,
(err: unknown) => err
)
expect((failure as Error).message).toBe('permission denied')
expect(failure).not.toHaveProperty('sandboxedSftpNamespace')
})
})
@@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { chmod, mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import '../config/ssh-g-config-resolution'
@@ -62,16 +62,6 @@ describe('the union of known_hosts entries', () => {
expect(verdict(entries, 'shared.example', ED_C)).toBe('mismatch')
})
it('skips a missing file and keeps the rest', async () => {
const root = await createRoot()
const present = join(root, 'known_hosts')
await writeFile(present, hostLine('alpha.example', ED_A))
const entries = await loadKnownHostsEntries([join(root, 'absent'), present])
expect(verdict(entries, 'alpha.example', ED_A)).toBe('match')
})
it.skipIf(process.platform === 'win32' || process.getuid?.() === 0)(
'skips an unreadable file and keeps the rest',
async () => {
@@ -118,15 +108,6 @@ describe('loadKnownHostsEvidence', () => {
expect(evidence.unreadableFileCount).toBe(1)
}
)
it('counts a path that is a directory', async () => {
const root = await createRoot()
await mkdir(join(root, 'a_directory'))
const evidence = await loadKnownHostsEvidence([join(root, 'a_directory')])
expect(evidence.unreadableFileCount).toBe(1)
})
})
/** OpenSSH10.2p1 prints spaced trust paths unquoted; splitting can hide existing changed-key evidence as absent files. */
@@ -31,76 +31,6 @@ describe('which server an SSH host runs on connect', () => {
expect(d.autoUpdate).not.toHaveBeenCalled()
})
it('checks for an update right after pairing with a server another version runs', async () => {
const d = deps({
isEmptyHost: () => true,
deploy: vi.fn(async () => ({
outcome: 'already-current' as const,
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the orchestrator reads only the environment id.
environment: { id: 'env-1' } as never,
activeVersion: '0.9'
})),
autoUpdate: vi.fn(async () => ({
outcome: 'deferred' as const,
code: 'orcad_update_terminals_running',
reason: '3 terminals are running'
}))
})
await expect(resolveHostServerOnConnect(target, d)).resolves.toEqual({
route: 'managed',
environmentId: 'env-1',
update: { state: 'deferred', detail: '3 terminals are running' }
})
expect(d.autoUpdate).toHaveBeenCalledWith('env-1', expect.anything())
expect(d.abandonDeploy).not.toHaveBeenCalled()
})
// COMPAT(legacy-relay-bridge): the census only reports; converting leaves relay terminals running.
it('converts anyway when the bridge takes the live or unproven relay terminals', async () => {
for (const verdict of ['live', 'unverifiable'] as const) {
const proof = { verdict, count: 2, bridged: true as const }
const d = deps({ relayTerminals: async () => proof })
await expect(resolveHostServerOnConnect(target, d)).resolves.toMatchObject({
route: 'managed'
})
expect(d.convert).toHaveBeenCalledWith(target, proof)
}
})
it('surfaces a refusal for any other reason, naming it', async () => {
const d = deps({
convert: async () => ({
outcome: 'refused',
verdict: 'live',
code: 'orcad_migration_preflight_blocked',
reason: 'An automation still runs on this host.'
})
})
await expect(resolveHostServerOnConnect(target, d)).resolves.toEqual({
route: 'unserved',
reason: 'refused',
detail: 'An automation still runs on this host.'
})
})
it('offers "Move anyway" when only saved state refuses', async () => {
const d = deps({
convert: async () => ({
outcome: 'refused',
verdict: 'live',
code: 'orcad_migration_preflight_blocked',
reason: 'This SSH host cannot move yet: saved tabs and panes (2).',
canLeaveBehind: true
})
})
await expect(resolveHostServerOnConnect(target, d)).resolves.toEqual({
route: 'unserved',
reason: 'refused',
detail: 'This SSH host cannot move yet: saved tabs and panes (2).',
canLeaveBehind: true
})
})
it('falls back to the relay ladder for good when orcad cannot run, and remembers why', async () => {
const d = deps({
isEmptyHost: () => true,
@@ -168,32 +98,6 @@ describe('which server an SSH host runs on connect', () => {
warn.mockRestore()
})
it('touches no host when this build carries no orcad template', async () => {
const d = deps({ hasTemplate: () => false, isEmptyHost: () => true })
await expect(resolveHostServerOnConnect(target, d)).resolves.toEqual({
route: 'unserved',
reason: 'orcad_unavailable',
detail: 'artifacts_unavailable'
})
expect(d.deploy).not.toHaveBeenCalled()
})
it('retries a transient failure on the next connect without recording it', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
const d = deps({
convert: async () => {
throw new Error('Connection lost')
}
})
await expect(resolveHostServerOnConnect(target, d)).resolves.toEqual({
route: 'unserved',
reason: 'failed',
detail: 'Connection lost'
})
expect(d.recordUnavailable).not.toHaveBeenCalled()
warn.mockRestore()
})
it('names a conflict the server will keep refusing, and the way out, instead of a retry', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
const d = deps({
@@ -241,19 +145,6 @@ describe('which server an SSH host runs on connect', () => {
warn.mockRestore()
})
it('keeps a staged claim of another desktop’s move a retry, since it expires', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
const d = deps({
convert: async () => {
throw new Error('orcad_migration_staged_claim_conflict:repository-path:/root/migrepo')
}
})
await expect(resolveHostServerOnConnect(target, d)).resolves.toMatchObject({
reason: 'failed'
})
warn.mockRestore()
})
it('records why when even the stdio bridge cannot reach the deployed server', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
const d = deps({
@@ -341,36 +232,5 @@ describe('which server an SSH host runs on connect', () => {
})
expect(order).toEqual(['start', 'update'])
})
it('keeps the old version serving while terminals run, and retries on a later connect', async () => {
const reason = '2 terminals are running on this host.'
const d = managed({
autoUpdate: async () => ({
outcome: 'deferred',
code: 'orcad_update_terminals_running',
reason
})
})
await expect(resolveHostServerOnConnect(target, d)).resolves.toEqual({
route: 'managed',
environmentId: 'env-9',
update: { state: 'deferred', detail: reason }
})
expect(d.recordUpdateFailure).not.toHaveBeenCalled()
})
it('still connects when the update check itself throws', async () => {
const d = managed({
autoUpdate: async () => {
throw new Error('ssh dropped')
}
})
await expect(resolveHostServerOnConnect(target, d)).resolves.toEqual({
route: 'managed',
environmentId: 'env-9',
update: { state: 'check-failed', detail: 'ssh dropped' }
})
expect(d.recordUpdateFailure).not.toHaveBeenCalled()
})
})
})
@@ -38,14 +38,6 @@ function loginShellProbe(shell: string, mode: string) {
)
}
function decodePowerShellCommand(command: string): string {
const match = command.match(/-EncodedCommand\s+([A-Za-z0-9+/=]+)/)
if (!match) {
throw new Error(`No encoded PowerShell command found in: ${command}`)
}
return Buffer.from(match[1]!, 'base64').toString('utf16le')
}
describe('resolveRemoteNodePath', () => {
beforeEach(() => {
execCommandMock.mockReset()
@@ -60,46 +52,36 @@ describe('resolveRemoteNodePath', () => {
await expect(resolveRemoteNodePath(conn)).resolves.toBe(
'/home/u/.nvm/versions/node/v22.22.0/bin/node'
)
expect(execCommandMock.mock.calls[1]![1]).toContain("PATH='/usr/bin':$PATH npm --version")
expect(execCommandMock.mock.calls[2]![1]).toContain(
"PATH='/home/u/.nvm/versions/node/v22.22.0/bin':$PATH npm --version"
)
})
it.runIf(process.platform !== 'win32')(
'accepts npm elsewhere on PATH without probing another Node candidate',
async () => {
const root = mkdtempSync(path.join(os.tmpdir(), 'orca-split-node-npm-'))
try {
const nodePath = path.join(root, 'selected node', 'bin', 'node')
const npmBinDir = path.join(root, 'npm elsewhere', 'bin')
mkdirSync(path.dirname(nodePath), { recursive: true })
mkdirSync(npmBinDir, { recursive: true })
writeFileSync(nodePath, '#!/bin/sh\nprintf "v22.22.0\\n"\n')
writeFileSync(path.join(npmBinDir, 'npm'), '#!/bin/sh\nprintf "11.13.0\\n"\n')
chmodSync(nodePath, 0o755)
chmodSync(path.join(npmBinDir, 'npm'), 0o755)
it.runIf(process.platform !== 'win32')('accepts npm elsewhere on PATH', async () => {
const root = mkdtempSync(path.join(os.tmpdir(), 'orca-split-node-npm-'))
try {
const nodePath = path.join(root, 'selected node', 'bin', 'node')
const npmBinDir = path.join(root, 'npm elsewhere', 'bin')
mkdirSync(path.dirname(nodePath), { recursive: true })
mkdirSync(npmBinDir, { recursive: true })
writeFileSync(nodePath, '#!/bin/sh\nprintf "v22.22.0\\n"\n')
writeFileSync(path.join(npmBinDir, 'npm'), '#!/bin/sh\nprintf "11.13.0\\n"\n')
chmodSync(nodePath, 0o755)
chmodSync(path.join(npmBinDir, 'npm'), 0o755)
execCommandMock
.mockResolvedValueOnce(`${nodePath}\n${path.join(root, 'fallback', 'bin', 'node')}\n`)
.mockImplementationOnce((_conn: SshConnection, command: string) =>
Promise.resolve(
execFileSync('/bin/sh', ['-c', command], {
encoding: 'utf8',
env: { HOME: root, PATH: npmBinDir }
})
)
execCommandMock
.mockResolvedValueOnce(`${nodePath}\n${path.join(root, 'fallback', 'bin', 'node')}\n`)
.mockImplementationOnce((_conn: SshConnection, command: string) =>
Promise.resolve(
execFileSync('/bin/sh', ['-c', command], {
encoding: 'utf8',
env: { HOME: root, PATH: npmBinDir }
})
)
)
await expect(resolveRemoteNodePath(conn)).resolves.toBe(nodePath)
// One inventory exec plus one candidate probe keeps SSH startup work bounded.
expect(execCommandMock).toHaveBeenCalledTimes(2)
} finally {
rmSync(root, { recursive: true, force: true })
}
await expect(resolveRemoteNodePath(conn)).resolves.toBe(nodePath)
} finally {
rmSync(root, { recursive: true, force: true })
}
)
})
it('finds node under a MISE_DATA_DIR exported from a shell dotfile', async () => {
execCommandMock
@@ -290,14 +272,6 @@ describe('resolveRemoteNodePath', () => {
)
})
it('accepts Node 18 (the exact minimum) as valid', async () => {
execCommandMock
.mockResolvedValueOnce('/usr/local/bin/node\n')
.mockResolvedValueOnce('v18.0.0\n')
await expect(resolveRemoteNodePath(conn)).resolves.toBe('/usr/local/bin/node')
})
it('falls back to the login shell when path probes find nothing', async () => {
execCommandMock
.mockResolvedValueOnce('\n')
@@ -328,37 +302,6 @@ describe('resolveRemoteNodePath', () => {
)
})
it('respects a non-default $SHELL instead of hardcoding bash', async () => {
execCommandMock
.mockResolvedValueOnce('\n')
.mockResolvedValueOnce('/usr/bin/fish')
.mockImplementationOnce(loginShellAnswer('/opt/homebrew/bin/node\n'))
.mockResolvedValueOnce('v22.0.0\n')
await resolveRemoteNodePath(conn)
expect(execCommandMock).toHaveBeenNthCalledWith(
3,
conn,
loginShellProbe('/usr/bin/fish', '-lc'),
{ wrapCommand: false, timeoutMs: 8_000 }
)
})
it('uses /bin/sh when the remote shell expansion falls back to it', async () => {
execCommandMock
.mockResolvedValueOnce('\n')
.mockResolvedValueOnce('/bin/sh\n')
.mockImplementationOnce(loginShellAnswer('/usr/local/bin/node\n'))
.mockResolvedValueOnce('v20.0.0\n')
await expect(resolveRemoteNodePath(conn)).resolves.toBe('/usr/local/bin/node')
expect(execCommandMock).toHaveBeenNthCalledWith(3, conn, loginShellProbe('/bin/sh', '-c'), {
wrapCommand: false,
timeoutMs: 8_000
})
})
it('uses -c (not -lc) for a csh login shell, which rejects combined -lc', async () => {
// csh/tcsh require plain -c; -lc/-l can fail with Bad modifier (#8701).
execCommandMock
@@ -374,25 +317,6 @@ describe('resolveRemoteNodePath', () => {
})
})
it('uses -c (not -lc) for a tcsh login shell', async () => {
execCommandMock
.mockResolvedValueOnce('\n')
.mockResolvedValueOnce('/usr/bin/tcsh\n')
.mockImplementationOnce(loginShellAnswer('/usr/local/bin/node\n'))
.mockResolvedValueOnce('v20.0.0\n')
await expect(resolveRemoteNodePath(conn)).resolves.toBe('/usr/local/bin/node')
expect(execCommandMock).toHaveBeenNthCalledWith(
3,
conn,
loginShellProbe('/usr/bin/tcsh', '-c'),
{
wrapCommand: false,
timeoutMs: 8_000
}
)
})
it('keeps the default path-probe fallback for SSH session-limit-shaped errors', async () => {
const sessionLimitError = Object.assign(new Error('(SSH) Channel open failure: open failed'), {
reason: 4
@@ -416,73 +340,6 @@ describe('resolveRemoteNodePath', () => {
).rejects.toBe(sessionLimitError)
})
it('rethrows SSH session-limit errors from candidate version checks in strict mode', async () => {
const sessionLimitError = Object.assign(new Error('(SSH) Channel open failure: open failed'), {
reason: 4
})
execCommandMock
.mockResolvedValueOnce('/usr/local/bin/node\n')
.mockRejectedValueOnce(sessionLimitError)
await expect(
resolveRemoteNodePath(conn, undefined, { rethrowSessionLimitErrors: true })
).rejects.toBe(sessionLimitError)
})
it('rethrows SSH session-limit errors from login-shell resolution in strict mode', async () => {
const sessionLimitError = Object.assign(new Error('(SSH) Channel open failure: open failed'), {
reason: 4
})
execCommandMock.mockResolvedValueOnce('\n').mockRejectedValueOnce(sessionLimitError)
await expect(
resolveRemoteNodePath(conn, undefined, { rethrowSessionLimitErrors: true })
).rejects.toBe(sessionLimitError)
})
it('rethrows SSH session-limit errors from package-manager hint probing in strict mode', async () => {
const sessionLimitError = Object.assign(new Error('(SSH) Channel open failure: open failed'), {
reason: 4
})
execCommandMock
.mockResolvedValueOnce('\n')
.mockResolvedValueOnce('/bin/bash')
.mockResolvedValueOnce('\n')
.mockRejectedValueOnce(sessionLimitError)
await expect(
resolveRemoteNodePath(conn, undefined, { rethrowSessionLimitErrors: true })
).rejects.toBe(sessionLimitError)
})
it('rethrows SSH session-limit errors from Windows node resolution in strict mode', async () => {
const sessionLimitError = Object.assign(new Error('(SSH) Channel open failure: open failed'), {
reason: 4
})
execCommandMock.mockRejectedValueOnce(sessionLimitError)
await expect(
resolveRemoteNodePath(conn, getRemoteHostPlatform('win32-x64'), {
rethrowSessionLimitErrors: true
})
).rejects.toBe(sessionLimitError)
})
it('rethrows SSH session-limit errors from Windows version checks in strict mode', async () => {
const sessionLimitError = Object.assign(new Error('(SSH) Channel open failure: open failed'), {
reason: 4
})
execCommandMock
.mockResolvedValueOnce('C:\\Program Files\\nodejs\\node.exe\n')
.mockRejectedValueOnce(sessionLimitError)
await expect(
resolveRemoteNodePath(conn, getRemoteHostPlatform('win32-x64'), {
rethrowSessionLimitErrors: true
})
).rejects.toBe(sessionLimitError)
})
it('surfaces an AbortError instead of not-found when the shared signal is aborted', async () => {
// Shared bootstrap abort must remain cancellation; swallowing it as Node-not-found defeats sequential fallback.
const controller = new AbortController()
@@ -508,9 +365,6 @@ describe('resolveRemoteNodePath', () => {
await expect(resolveRemoteNodePath(conn, getRemoteHostPlatform('win32-x64'))).resolves.toBe(
'C:/Program Files/nodejs/node.exe'
)
const discoveryScript = decodePowerShellCommand(execCommandMock.mock.calls[0]![1] as string)
expect(discoveryScript).not.toMatch(/Write-Output \$path\s+exit 0/)
})
describe('strict resolution, which settles rung D only on an answered "no Node"', () => {