Files
orca/mobile/src/session/mobile-session-startup-source.test.ts
Jinwoo Hong d936d8da82 revert(mobile): pull the relay connect-speed mobile pass pending a smaller, verified re-land (#19348)
* Revert "feat(mobile): time relay dial stages so diagnostics say where a slow connect went (#19245)"

This reverts commit 83b1558ecc.

* Revert "perf(mobile): race the direct and relay dials from t=0 on every reconnect (#19308)"

This reverts commit ceafdcad2f.

* Revert "feat(mobile): draw the last known tab strip while a session reconnects (mobile pass) (#19281)"

This reverts commit 643571def6.

* Revert "perf(mobile): open a session with parallel startup RPCs and a pre-warmed terminal engine (#19260)"

This reverts commit c37413271e.

* Revert "perf(mobile): cut the relay reconnect critical path and admit dead sockets faster (mobile pass) (#19280)"

This reverts commit e628090ad4.

* chore: keep the react-doctor suppression for the startup timers

The pattern it covers (a variable number of timers cleared through one cleanup)
predates #19260 and is unchanged by the revert; dropping the entry only re-exposed
a pre-existing finding to the changed-code gate.
2026-09-07 16:44:26 -04:00

294 lines
14 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
readMobileSessionRouteSource,
readMobileSessionRouteSourceFamily
} from './mobile-session-route-source-family.test-support'
const source = readMobileSessionRouteSourceFamily()
const startupSource = readMobileSessionRouteSource('./use-mobile-session-startup.ts')
const tabReconciliationSource = readMobileSessionRouteSource(
'./use-mobile-session-tab-reconciliation.ts'
)
const bulkCloseSource = readMobileSessionRouteSource('./use-mobile-session-bulk-close.ts')
const presentationSource = readMobileSessionRouteSource('./use-mobile-session-presentation.ts')
const tabSwitchingSource = readMobileSessionRouteSource('./use-mobile-session-tab-switching.ts')
const sheetsSource = readMobileSessionRouteSource('./MobileSessionSheets.tsx')
const reconciliationHookSource = readMobileSessionRouteSource(
'./use-mobile-session-tabs-reconciliation.ts'
)
const terminalInventoryRecoverySource = readMobileSessionRouteSource(
'./use-mobile-terminal-inventory-recovery.ts'
)
const terminalSubscriptionSource = readMobileSessionRouteSource(
'./use-mobile-session-terminal-subscription.ts'
)
const terminalListSource = readMobileSessionRouteSource('./use-mobile-session-terminal-list.ts')
const tabReconciliationOwnerSource = readMobileSessionRouteSource(
'./use-mobile-session-tab-reconciliation.ts'
)
const autoCreateHookSource = readMobileSessionRouteSource(
'./use-initial-session-terminal-autocreate.ts'
)
const foundationSource = readMobileSessionRouteSource('./use-mobile-session-foundation.ts')
const terminalRuntimeSource = readMobileSessionRouteSource(
'./use-mobile-session-terminal-runtime.ts'
)
const terminalSubscriptionSourceForIdentity = readMobileSessionRouteSource(
'./use-mobile-session-terminal-subscription.ts'
)
const lifecycleSource = readMobileSessionRouteSource('./use-mobile-session-lifecycle.ts')
function sliceBetween(startPattern: string, endPattern: string, targetSource = source): string {
const start = targetSource.indexOf(startPattern)
expect(start).toBeGreaterThanOrEqual(0)
const end = targetSource.indexOf(endPattern, start)
expect(end).toBeGreaterThan(start)
return targetSource.slice(start, end)
}
describe('mobile session startup', () => {
it('auto-creates one terminal for a newly created empty session', () => {
expect(source).toContain('useWorktreeSessionTabsLoaded(worktreeId)')
expect(source).toContain(
'initialSessionAutoCreateRef.current = createInitialSessionAutoCreateState()'
)
const autoCreateCall = sliceBetween(
'useInitialSessionTerminalAutoCreate({',
'const connectionVerdict =',
presentationSource
)
expect(autoCreateCall).toContain('stateRef: initialSessionAutoCreateRef')
expect(autoCreateCall).toContain(
'consumeCreationRoute: () => router.setParams({ created: undefined })'
)
expect(autoCreateCall).toContain("newlyCreatedWorkspace: created === '1'")
expect(autoCreateCall).toContain('visibleTabCount: visibleTabs.length')
expect(autoCreateCall).toContain('createTerminal: () => void handleCreateTerminal()')
expect(autoCreateHookSource).toContain('shouldAutoCreateInitialSessionTerminal({')
expect(autoCreateHookSource).toContain('stateRef.current.autoCreatedForWorktree === worktreeId')
expect(autoCreateHookSource).toContain('stateRef.current.autoCreatedForWorktree = worktreeId')
expect(autoCreateHookSource).toContain("connState === 'connected'")
expect(autoCreateHookSource).toContain('(visibleTabCount > 0 || activeHandle !== null)')
// Why: both callbacks are re-created every render, so the effect must reach them
// through useEffectEvent rather than deps or a render-time ref write.
expect(autoCreateHookSource).toContain('useEffectEvent(args.consumeCreationRoute)')
expect(autoCreateHookSource).toContain('useEffectEvent(args.createTerminal)')
expect(autoCreateHookSource).toContain('consumeCreationRoute()')
expect(autoCreateHookSource).toContain('createTerminal()')
})
it('arms the auto-create only until the route has published a tab (#9717)', () => {
// Emptiness after a populated list is a close, not a cold hydrate.
expect(source).toContain(
'initialSessionAutoCreateRef.current.sawSessionTabs ||= nextTabs.length > 0'
)
const autoCreateCall = sliceBetween(
'useInitialSessionTerminalAutoCreate({',
'const connectionVerdict =',
presentationSource
)
expect(autoCreateCall).toContain('stateRef: initialSessionAutoCreateRef')
expect(autoCreateHookSource).toContain('sawSessionTabs: stateRef.current.sawSessionTabs')
})
it('delegates stream ownership while retaining degraded polling and a certified sweep', () => {
expect(source).toContain('useMobileSessionTabsReconciliation<')
expect(source).toContain('const applicationRevision = ++appliedSessionTabsRevisionRef.current')
expect(source).toContain('getApplicationRevision: getSessionTabsApplicationRevision')
expect(source).not.toContain("client.subscribe(\n 'session.tabs.subscribe'")
expect(reconciliationHookSource).toContain("client.subscribe(\n 'session.tabs.subscribe'")
expect(reconciliationHookSource).toContain("if (AppState.currentState !== 'active')")
expect(reconciliationHookSource).toContain('suspendTerminalInventoryRecovery(true)')
expect(reconciliationHookSource).toContain('controller.poll()')
expect(reconciliationHookSource).toContain('tabsRequest !== null')
expect(reconciliationHookSource).toContain('void refreshTerminalInventory()')
expect(reconciliationHookSource).toContain("AppState.addEventListener('change'")
expect(reconciliationHookSource).toContain('const interval = setInterval(')
expect(reconciliationHookSource).toContain('RECONCILIATION_INTERVAL_MS = 2000')
expect(terminalInventoryRecoverySource).toContain('CERTIFIED_TERMINAL_SWEEP_MS = 60_000')
expect(reconciliationHookSource).toContain('controller.setReconciliationActive(false)')
expect(reconciliationHookSource).toContain('clearInterval(interval)')
expect(reconciliationHookSource).toContain('appStateSubscription.remove()')
})
it('binds terminal identity to the shared client before subscription effects run', () => {
expect(foundationSource).toContain('const { client, clientId, state: connState }')
expect(foundationSource).toContain(' clientId,')
expect(terminalRuntimeSource).toContain('useRef<string | null>(clientId)')
expect(terminalRuntimeSource).toContain('deviceTokenRef.current = clientId')
expect(terminalRuntimeSource).toContain('inputGate.canSend && clientId !== null')
expect(terminalSubscriptionSourceForIdentity).toContain('if (clientId === null)')
expect(terminalSubscriptionSourceForIdentity).toContain(
"client: { id: clientId, type: 'mobile' as const }"
)
expect(lifecycleSource).not.toContain('deviceTokenRef.current = host.deviceToken')
})
it('confirms terminal stream teardown with a committed inventory-recovery bridge', () => {
expect(terminalSubscriptionSource).toContain(
"if (data.type === 'end' || data.type === 'error')"
)
expect(terminalSubscriptionSource).toContain('signalTerminalInventoryRecovery()')
expect(terminalInventoryRecoverySource).toContain('actionRef.current = recoveryAction')
expect(terminalInventoryRecoverySource).toContain('pendingSignalScopeRef.current = scopeKey')
expect(terminalInventoryRecoverySource).toContain(
'committedScope !== null && committedScope !== scopeKey'
)
expect(terminalListSource).toContain('return terminalInventoryRequest.activate()')
expect(terminalListSource).toContain('if (!isCurrent() || !response.ok)')
expect(terminalInventoryRecoverySource).toContain(
'TERMINAL_INVENTORY_CONFIRMATION_DELAY_MS = 750'
)
expect(terminalInventoryRecoverySource).toContain(
'refreshTerminalInventory({ allowEmptyLoaded: true })'
)
})
it('loads session tabs without waiting for desktop activation', () => {
const startupEffect = sliceBetween(
'void (async () => {',
'return () => {\n disposed = true',
startupSource
)
expect(startupEffect).toContain("void client\n .sendRequest('worktree.activate'")
expect(startupEffect).toContain("if (client && created !== '1' && !isFloatingWorkspaceRoute)")
expect(startupEffect).toContain("if (client && created === '1' && !isFloatingWorkspaceRoute)")
expect(startupEffect).toContain('notifyClients: false')
expect(startupEffect).toContain("navigation: 'caller'")
expect(startupEffect).not.toContain("await client\n .sendRequest('worktree.activate'")
expect(startupEffect.indexOf("sendRequest('worktree.activate'")).toBeLessThan(
startupEffect.indexOf('await ensureSessionTabs()')
)
expect(startupEffect).toContain('headlessActivationNeedsHostRenderer(response.result)')
expect(startupEffect).toContain("showToast('Open Orca on the host to wake sleeping agents.'")
})
it('fails runtime capability gates closed before probing a replacement client', () => {
const capabilityEffect = sliceBetween(
'const hostQueryReplyInputSupportedRef = useRef(false)',
'return {\n consumeAcceptedSessionTabs',
tabReconciliationSource
)
const probeStart = capabilityEffect.indexOf('startRuntimeCapabilityProbe(client,')
expect(probeStart).toBeGreaterThanOrEqual(0)
for (const reset of [
'setBrowserScreencastSupported(null)',
'setAgentSessionHistorySupported(null)',
'setQuickCommandsSupported(null)',
'setShowQuickCommands(false)',
'hostQueryReplyInputSupportedRef.current = false'
]) {
const resetIndex = capabilityEffect.lastIndexOf(reset)
expect(resetIndex).toBeGreaterThanOrEqual(0)
expect(resetIndex).toBeLessThan(probeStart)
}
})
it('activates an already-selected pending terminal tab after hydration', () => {
expect(source).toContain(
'const pendingTerminalActivationAttemptRef = useRef<string | null>(null)'
)
expect(source).toContain('pendingTerminalActivationAttemptRef.current = null')
const pendingActivationEffect = sliceBetween(
"if (!client || connState !== 'connected' || !activePendingTerminalTab) {",
'return {\n bulkCloseActions',
bulkCloseSource
)
expect(pendingActivationEffect).toContain(
'pendingTerminalActivationAttemptRef.current === activationKey'
)
expect(pendingActivationEffect).toContain('activateMobileSessionTab(client,')
expect(pendingActivationEffect).toContain('tabId: activePendingTerminalTab.id')
expect(pendingActivationEffect).toContain('leafId: activePendingTerminalTab.leafId')
expect(pendingActivationEffect).toContain('notifyClients: false')
expect(pendingActivationEffect).toContain("navigation: 'caller'")
expect(pendingActivationEffect).toContain(
'applySessionTabs((response as RpcSuccess).result as SessionTabsResult)'
)
expect(pendingActivationEffect).toContain('scheduleDelayedAction(() => void fetchSessionTabs()')
})
it('keeps ready terminal taps local while publishing caller selection', () => {
const readyTerminalSwitch = sliceBetween(
'const switchTab = useCallback(',
'const switchSessionTab = useCallback(',
tabSwitchingSource
)
expect(readyTerminalSwitch).not.toContain('focusMobileTerminal(client, handle)')
expect(readyTerminalSwitch).toContain('activateMobileSessionTab(client,')
expect(readyTerminalSwitch).toContain('notifyClients: false')
expect(readyTerminalSwitch).toContain("navigation: 'caller'")
})
it('keeps background and pending session-tab activation local to the phone', () => {
const activationRequests = source.split('activateMobileSessionTab(client,').slice(1)
expect(activationRequests).toHaveLength(4)
for (const request of activationRequests) {
expect(request.slice(0, request.indexOf('})'))).toContain('notifyClients: false')
expect(request.slice(0, request.indexOf('})'))).toContain("navigation: 'caller'")
}
})
it('keeps dynamic agent rows above fixed New Tab actions', () => {
const newTabActions = sliceBetween(
'title="New Tab"',
'onClose={() => setShowCreateTabDrawer',
sheetsSource
)
expect(newTabActions.indexOf('...createTabAgentActions')).toBeLessThan(
newTabActions.indexOf("label: 'Terminal'")
)
expect(newTabActions.indexOf("label: 'Terminal'")).toBeLessThan(
newTabActions.indexOf("label: 'Browser'")
)
expect(newTabActions.indexOf("label: 'Browser'")).toBeLessThan(
newTabActions.indexOf("label: 'Markdown Note'")
)
})
it('wires pending-handle recovery through its bounded context (STA-4256)', () => {
const applySessionTabs = sliceBetween(
'const applySessionTabs = useCallback(',
'const consumeAcceptedSessionTabs = useCallback('
)
const recoveryContext = sliceBetween(
'const pendingTerminalRecoveryContextCache = useMemo(',
'const sessionTabsFetchReporting',
tabReconciliationOwnerSource
)
const tabsRefWrite = 'sessionTabsRef.current = nextTabs'
const tabsStateWrite = 'setSessionTabs((prev)'
const activeRefWrite = 'activeSessionTabIdRef.current = active?.id ?? null'
const activeStateWrite = 'setActiveSessionTabId(active?.id ?? null)'
for (const write of [tabsRefWrite, tabsStateWrite, activeRefWrite, activeStateWrite]) {
expect(applySessionTabs).toContain(write)
}
expect(applySessionTabs.indexOf(tabsRefWrite)).toBeLessThan(
applySessionTabs.indexOf(tabsStateWrite)
)
expect(applySessionTabs.indexOf(activeRefWrite)).toBeLessThan(
applySessionTabs.indexOf(activeStateWrite)
)
expect(recoveryContext).toContain('() => new PendingTerminalHandleRecoveryContextCache()')
expect(recoveryContext).toContain('sessionTabsRef.current,')
expect(recoveryContext).toContain('activeSessionTabIdRef.current')
expect(recoveryContext).toContain(
'const pendingTerminalRecoveryContextKey = getPendingTerminalRecoveryContextKey()'
)
expect(source).toContain('hasRecoveryNeed: hasSessionTabsRecoveryNeed')
expect(source).toContain('getPendingTerminalRecoveryContextKey,')
expect(source).toContain('onPendingTerminalRecoveryParked: setParkedPendingTerminalContext')
expect(source).toContain('retryPendingTerminalRecovery()')
})
})