From 2222e5475480bb808cfcd39e2ac3204c75bd3dc1 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 31 Aug 2026 18:18:15 -0700 Subject: [PATCH] refactor(test): organize SSH and terminal recovery fixtures (#17751) --- config/scripts/pr-e2e-gate-contract.test.mjs | 4 +- ...time-terminal-close-continuity-fixtures.ts | 8 +- ...terminal-close-continuity-graph-fixture.ts | 6 +- ...terminal-close-continuity-state-fixture.ts | 6 +- ...-runtime-terminal-close-continuity.test.ts | 2 +- .../ssh-connection-test-client.ts | 0 src/main/ssh/ssh-connection-test-harness.ts | 6 +- ...mote-workspace-target-sync-test-harness.ts | 10 +- .../remote-workspace-target-sync.test.ts | 2 +- ...nal-orphan-recovery-regression-fixtures.ts | 6 +- .../src/runtime/web-session-tabs-sync.ts | 91 +++++++++---------- ...on-terminal-orphan-inventory-retry.test.ts | 2 +- ...phan-recovery-adoption-regressions.test.ts | 2 +- ...rminal-orphan-recovery-regressions.test.ts | 2 +- ...nal-orphan-recovery-topology-fence.test.ts | 2 +- .../web-session-terminal-orphan-recovery.ts | 13 +-- 16 files changed, 79 insertions(+), 83 deletions(-) rename src/main/runtime/{ => __fixtures__}/orca-runtime-terminal-close-continuity-fixtures.ts (96%) rename src/main/runtime/{ => __fixtures__}/orca-runtime-terminal-close-continuity-graph-fixture.ts (96%) rename src/main/runtime/{ => __fixtures__}/orca-runtime-terminal-close-continuity-state-fixture.ts (93%) rename src/main/ssh/{ => __tests__}/ssh-connection-test-client.ts (100%) rename src/renderer/src/hooks/{ => __tests__}/remote-workspace-target-sync-test-harness.ts (95%) rename src/renderer/src/runtime/{ => __fixtures__}/web-session-terminal-orphan-recovery-regression-fixtures.ts (94%) diff --git a/config/scripts/pr-e2e-gate-contract.test.mjs b/config/scripts/pr-e2e-gate-contract.test.mjs index a2b1f753fee..7cf2b4e11b4 100644 --- a/config/scripts/pr-e2e-gate-contract.test.mjs +++ b/config/scripts/pr-e2e-gate-contract.test.mjs @@ -316,7 +316,9 @@ describe('PR E2E gate contract', () => { selectPrE2eSpecs(['src/renderer/src/hooks/remote-workspace-session-merge.test.ts']) ).toEqual([]) expect( - selectPrE2eSpecs(['src/renderer/src/hooks/remote-workspace-target-sync-test-harness.ts']) + selectPrE2eSpecs([ + 'src/renderer/src/hooks/__tests__/remote-workspace-target-sync-test-harness.ts' + ]) ).toEqual([]) }) diff --git a/src/main/runtime/orca-runtime-terminal-close-continuity-fixtures.ts b/src/main/runtime/__fixtures__/orca-runtime-terminal-close-continuity-fixtures.ts similarity index 96% rename from src/main/runtime/orca-runtime-terminal-close-continuity-fixtures.ts rename to src/main/runtime/__fixtures__/orca-runtime-terminal-close-continuity-fixtures.ts index 8222db2d19b..94528481622 100644 --- a/src/main/runtime/orca-runtime-terminal-close-continuity-fixtures.ts +++ b/src/main/runtime/__fixtures__/orca-runtime-terminal-close-continuity-fixtures.ts @@ -1,8 +1,8 @@ import { vi, type Mock } from 'vitest' -import { makePaneKey } from '../../shared/stable-pane-id' -import type { WorkspaceSessionState } from '../../shared/workspace-session-state-types' -import type { RuntimeTerminalListResult } from '../../shared/runtime-types' -import { OrcaRuntimeService } from './orca-runtime' +import { makePaneKey } from '../../../shared/stable-pane-id' +import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types' +import type { RuntimeTerminalListResult } from '../../../shared/runtime-types' +import { OrcaRuntimeService } from '../orca-runtime' import { CANARY_INCARNATION_ID, CANARY_LEAF_ID, diff --git a/src/main/runtime/orca-runtime-terminal-close-continuity-graph-fixture.ts b/src/main/runtime/__fixtures__/orca-runtime-terminal-close-continuity-graph-fixture.ts similarity index 96% rename from src/main/runtime/orca-runtime-terminal-close-continuity-graph-fixture.ts rename to src/main/runtime/__fixtures__/orca-runtime-terminal-close-continuity-graph-fixture.ts index 1213421a3da..28ee94cb16b 100644 --- a/src/main/runtime/orca-runtime-terminal-close-continuity-graph-fixture.ts +++ b/src/main/runtime/__fixtures__/orca-runtime-terminal-close-continuity-graph-fixture.ts @@ -1,5 +1,5 @@ -import type { WorkspaceSessionState } from '../../shared/workspace-session-state-types' -import type { OrcaRuntimeService } from './orca-runtime' +import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types' +import type { OrcaRuntimeService } from '../orca-runtime' import { CANARY_LEAF_ID, CANARY_TAB_ID, @@ -13,7 +13,7 @@ import { canarySyncedLeaf, canarySyncedTab } from './orca-runtime-terminal-close-continuity-state-fixture' -import { makePaneKey } from '../../shared/stable-pane-id' +import { makePaneKey } from '../../../shared/stable-pane-id' export type CloseContinuityGraphOptions = { ptyId: string diff --git a/src/main/runtime/orca-runtime-terminal-close-continuity-state-fixture.ts b/src/main/runtime/__fixtures__/orca-runtime-terminal-close-continuity-state-fixture.ts similarity index 93% rename from src/main/runtime/orca-runtime-terminal-close-continuity-state-fixture.ts rename to src/main/runtime/__fixtures__/orca-runtime-terminal-close-continuity-state-fixture.ts index 9eef41fb675..d304ff57d23 100644 --- a/src/main/runtime/orca-runtime-terminal-close-continuity-state-fixture.ts +++ b/src/main/runtime/__fixtures__/orca-runtime-terminal-close-continuity-state-fixture.ts @@ -1,6 +1,6 @@ -import { getDefaultWorkspaceSession } from '../../shared/constants' -import { makePaneKey } from '../../shared/stable-pane-id' -import type { WorkspaceSessionState } from '../../shared/workspace-session-state-types' +import { getDefaultWorkspaceSession } from '../../../shared/constants' +import { makePaneKey } from '../../../shared/stable-pane-id' +import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types' export const REPO_ID = 'repo-close-continuity' export const WORKTREE_PATH = '/tmp/terminal-close-continuity' diff --git a/src/main/runtime/orca-runtime-terminal-close-continuity.test.ts b/src/main/runtime/orca-runtime-terminal-close-continuity.test.ts index 0b69b45df82..ef24301c7cf 100644 --- a/src/main/runtime/orca-runtime-terminal-close-continuity.test.ts +++ b/src/main/runtime/orca-runtime-terminal-close-continuity.test.ts @@ -19,7 +19,7 @@ import { STALE_TAB_ID, TAB_ID, WORKTREE_ID -} from './orca-runtime-terminal-close-continuity-fixtures' +} from './__fixtures__/orca-runtime-terminal-close-continuity-fixtures' describe('terminal close and handle incarnation continuity', () => { it('delegates a stale spawn-time tab through its current PTY-backed renderer surface', async () => { diff --git a/src/main/ssh/ssh-connection-test-client.ts b/src/main/ssh/__tests__/ssh-connection-test-client.ts similarity index 100% rename from src/main/ssh/ssh-connection-test-client.ts rename to src/main/ssh/__tests__/ssh-connection-test-client.ts diff --git a/src/main/ssh/ssh-connection-test-harness.ts b/src/main/ssh/ssh-connection-test-harness.ts index 15b0cb011f7..5c4567598e5 100644 --- a/src/main/ssh/ssh-connection-test-harness.ts +++ b/src/main/ssh/ssh-connection-test-harness.ts @@ -5,7 +5,7 @@ import type { MockSystemCommandChannel, MockSystemSshProcess } from './ssh-conne import type { SshResolvedConfig } from './ssh-config-parser' import type { SystemSshBuildArgsOptions } from './system-ssh-args' import type { SshTarget } from '../../shared/ssh-types' -import { resetSsh2ClientState, ssh2Mock } from './ssh-connection-test-client' +import { resetSsh2ClientState, ssh2Mock } from './__tests__/ssh-connection-test-client' export { clientInstances, connectAttempts, @@ -17,8 +17,8 @@ export { resetSsh2ClientState, ssh2Mock, VALID_ED25519_HOST_KEY -} from './ssh-connection-test-client' -export type { MockSshClient, Ssh2ModuleMock } from './ssh-connection-test-client' +} from './__tests__/ssh-connection-test-client' +export type { MockSshClient, Ssh2ModuleMock } from './__tests__/ssh-connection-test-client' export type SystemSshBinaryModuleMock = { findSystemSsh: typeof findSystemSshMock } diff --git a/src/renderer/src/hooks/remote-workspace-target-sync-test-harness.ts b/src/renderer/src/hooks/__tests__/remote-workspace-target-sync-test-harness.ts similarity index 95% rename from src/renderer/src/hooks/remote-workspace-target-sync-test-harness.ts rename to src/renderer/src/hooks/__tests__/remote-workspace-target-sync-test-harness.ts index 807c1d4f7c4..fcc8909af3c 100644 --- a/src/renderer/src/hooks/remote-workspace-target-sync-test-harness.ts +++ b/src/renderer/src/hooks/__tests__/remote-workspace-target-sync-test-harness.ts @@ -2,14 +2,14 @@ import { vi } from 'vitest' import type { RemoteWorkspaceObservedPatchResult, RemoteWorkspaceObservedSnapshot -} from '../../../shared/remote-workspace-types' -import type { DirectSshAuthority, SshProviderEpoch } from '../../../shared/ssh-types' -import type { AppState } from '../store/types' +} from '../../../../shared/remote-workspace-types' +import type { DirectSshAuthority, SshProviderEpoch } from '../../../../shared/ssh-types' +import type { AppState } from '../../store/types' import type { DirectSshPreparationInput, DirectSshPreparationToken -} from './direct-ssh-reconnect-coordinator' -import { createRemoteWorkspaceTargetSync } from './remote-workspace-target-sync' +} from '../direct-ssh-reconnect-coordinator' +import { createRemoteWorkspaceTargetSync } from '../remote-workspace-target-sync' export type Deferred = { promise: Promise diff --git a/src/renderer/src/hooks/remote-workspace-target-sync.test.ts b/src/renderer/src/hooks/remote-workspace-target-sync.test.ts index e1aad24fbb4..b3242077052 100644 --- a/src/renderer/src/hooks/remote-workspace-target-sync.test.ts +++ b/src/renderer/src/hooks/remote-workspace-target-sync.test.ts @@ -18,7 +18,7 @@ import { snapshot, token, worktree -} from './remote-workspace-target-sync-test-harness' +} from './__tests__/remote-workspace-target-sync-test-harness' describe('createRemoteWorkspaceTargetSync', () => { it('captures local tabs before get when deciding a revision-zero upload', async () => { diff --git a/src/renderer/src/runtime/web-session-terminal-orphan-recovery-regression-fixtures.ts b/src/renderer/src/runtime/__fixtures__/web-session-terminal-orphan-recovery-regression-fixtures.ts similarity index 94% rename from src/renderer/src/runtime/web-session-terminal-orphan-recovery-regression-fixtures.ts rename to src/renderer/src/runtime/__fixtures__/web-session-terminal-orphan-recovery-regression-fixtures.ts index f2f7b353909..3f4860f2302 100644 --- a/src/renderer/src/runtime/web-session-terminal-orphan-recovery-regression-fixtures.ts +++ b/src/renderer/src/runtime/__fixtures__/web-session-terminal-orphan-recovery-regression-fixtures.ts @@ -1,9 +1,9 @@ import type { RuntimeMobileSessionTabsResult, RuntimeMobileSessionTerminalClientTab -} from '../../../shared/runtime-types' -import { toRemoteRuntimePtyId } from './runtime-terminal-stream' -import type { TerminalOrphanRecoveryState } from './web-session-terminal-orphan-recovery-surface' +} from '../../../../shared/runtime-types' +import { toRemoteRuntimePtyId } from '../runtime-terminal-stream' +import type { TerminalOrphanRecoveryState } from '../web-session-terminal-orphan-recovery-surface' export const ENVIRONMENT_ID = 'remote-runtime' diff --git a/src/renderer/src/runtime/web-session-tabs-sync.ts b/src/renderer/src/runtime/web-session-tabs-sync.ts index 4d7070c8368..31627070ebc 100644 --- a/src/renderer/src/runtime/web-session-tabs-sync.ts +++ b/src/renderer/src/runtime/web-session-tabs-sync.ts @@ -164,21 +164,45 @@ type ReceivedSessionTabsSnapshot = SnapshotFreshness & { * minted by several publishers. Retain a bounded predecessor set so a frame * queued by a restarted host cannot be mistaken for a fresh publication. */ -type SessionTabsRuntimeHistory = { +type RetiredValueHistory = { current: string | null retired: string[] } +function hasRetiredValue(history: RetiredValueHistory | undefined, value: string): boolean { + return history?.retired.includes(value) ?? false +} + +function noteRetiredValue( + history: RetiredValueHistory | undefined, + value: string, + retiredLimit: number +): RetiredValueHistory { + if (!history) { + return { current: value, retired: [] } + } + if (history.current === value) { + return history + } + if (history.current && !history.retired.includes(history.current)) { + history.retired.push(history.current) + if (history.retired.length > retiredLimit) { + history.retired.splice(0, history.retired.length - retiredLimit) + } + } + history.current = value + return history +} + +type SessionTabsRuntimeHistory = RetiredValueHistory + /** * A host restart changes the publication epoch, but frames from the previous * epoch can still be queued on a sibling subscription. Keep a small history * of epochs that have already been superseded so those delayed frames cannot * roll the mirror back after the replacement epoch is accepted. */ -type SessionTabsPublicationEpochHistory = { - current: string - retired: string[] -} +type SessionTabsPublicationEpochHistory = RetiredValueHistory type SessionTabsRecoveryState = { pendingCount: number @@ -438,32 +462,20 @@ function getSessionTabsRuntimeIdFromResponse( } function isRetiredSessionTabsRuntimeId(environmentId: string, runtimeId: string): boolean { - return ( - sessionTabsRuntimeHistoryByEnvironment.get(environmentId)?.retired.includes(runtimeId) ?? false - ) + return hasRetiredValue(sessionTabsRuntimeHistoryByEnvironment.get(environmentId), runtimeId) } function noteSessionTabsRuntimeId( environmentId: string, runtimeId: string ): SessionTabsRuntimeHistory { - const existing = sessionTabsRuntimeHistoryByEnvironment.get(environmentId) - if (!existing) { - const created: SessionTabsRuntimeHistory = { current: runtimeId, retired: [] } - sessionTabsRuntimeHistoryByEnvironment.set(environmentId, created) - return created - } - if (existing.current === runtimeId) { - return existing - } - if (existing.current && !existing.retired.includes(existing.current)) { - existing.retired.push(existing.current) - if (existing.retired.length > SESSION_TABS_RETIRED_RUNTIME_ID_LIMIT) { - existing.retired.splice(0, existing.retired.length - SESSION_TABS_RETIRED_RUNTIME_ID_LIMIT) - } - } - existing.current = runtimeId - return existing + const history = noteRetiredValue( + sessionTabsRuntimeHistoryByEnvironment.get(environmentId), + runtimeId, + SESSION_TABS_RETIRED_RUNTIME_ID_LIMIT + ) + sessionTabsRuntimeHistoryByEnvironment.set(environmentId, history) + return history } function isCurrentSessionTabsRuntimeId(environmentId: string, runtimeId: string): boolean { @@ -505,33 +517,20 @@ function acceptSessionTabsRuntimeId( } function isRetiredSessionTabsPublicationEpoch(key: string, publicationEpoch: string): boolean { - return ( - sessionTabsPublicationEpochHistoryByWorktree.get(key)?.retired.includes(publicationEpoch) ?? - false - ) + return hasRetiredValue(sessionTabsPublicationEpochHistoryByWorktree.get(key), publicationEpoch) } function noteSessionTabsPublicationEpoch( key: string, publicationEpoch: string ): SessionTabsPublicationEpochHistory { - const existing = sessionTabsPublicationEpochHistoryByWorktree.get(key) - if (!existing) { - const created = { current: publicationEpoch, retired: [] } - sessionTabsPublicationEpochHistoryByWorktree.set(key, created) - return created - } - if (existing.current === publicationEpoch) { - return existing - } - if (!existing.retired.includes(existing.current)) { - existing.retired.push(existing.current) - if (existing.retired.length > SESSION_TABS_RETIRED_EPOCH_LIMIT) { - existing.retired.splice(0, existing.retired.length - SESSION_TABS_RETIRED_EPOCH_LIMIT) - } - } - existing.current = publicationEpoch - return existing + const history = noteRetiredValue( + sessionTabsPublicationEpochHistoryByWorktree.get(key), + publicationEpoch, + SESSION_TABS_RETIRED_EPOCH_LIMIT + ) + sessionTabsPublicationEpochHistoryByWorktree.set(key, history) + return history } function recordReceivedWebSessionTabsSnapshot( diff --git a/src/renderer/src/runtime/web-session-terminal-orphan-inventory-retry.test.ts b/src/renderer/src/runtime/web-session-terminal-orphan-inventory-retry.test.ts index 71851b9bc29..0fe298ba141 100644 --- a/src/renderer/src/runtime/web-session-terminal-orphan-inventory-retry.test.ts +++ b/src/renderer/src/runtime/web-session-terminal-orphan-inventory-retry.test.ts @@ -6,7 +6,7 @@ import { makeSnapshot, makeState, pendingSurface -} from './web-session-terminal-orphan-recovery-regression-fixtures' +} from './__fixtures__/web-session-terminal-orphan-recovery-regression-fixtures' import { clearWebSessionTerminalOrphanRecoveryForTests, recoverWebSessionTerminalOrphansBeforeApply diff --git a/src/renderer/src/runtime/web-session-terminal-orphan-recovery-adoption-regressions.test.ts b/src/renderer/src/runtime/web-session-terminal-orphan-recovery-adoption-regressions.test.ts index 07f423a34cd..05e41e94739 100644 --- a/src/renderer/src/runtime/web-session-terminal-orphan-recovery-adoption-regressions.test.ts +++ b/src/renderer/src/runtime/web-session-terminal-orphan-recovery-adoption-regressions.test.ts @@ -7,7 +7,7 @@ import { makeSnapshot, makeState, pendingSurface -} from './web-session-terminal-orphan-recovery-regression-fixtures' +} from './__fixtures__/web-session-terminal-orphan-recovery-regression-fixtures' import { clearWebSessionTerminalOrphanRecoveryForTests, recoverWebSessionTerminalOrphansBeforeApply diff --git a/src/renderer/src/runtime/web-session-terminal-orphan-recovery-regressions.test.ts b/src/renderer/src/runtime/web-session-terminal-orphan-recovery-regressions.test.ts index f9e1a80b6e3..9e995de8776 100644 --- a/src/renderer/src/runtime/web-session-terminal-orphan-recovery-regressions.test.ts +++ b/src/renderer/src/runtime/web-session-terminal-orphan-recovery-regressions.test.ts @@ -11,7 +11,7 @@ import { makeSnapshot, makeState, pendingSurface -} from './web-session-terminal-orphan-recovery-regression-fixtures' +} from './__fixtures__/web-session-terminal-orphan-recovery-regression-fixtures' import { clearWebSessionTerminalOrphanRecoveryForTests, recoverWebSessionTerminalOrphansBeforeApply diff --git a/src/renderer/src/runtime/web-session-terminal-orphan-recovery-topology-fence.test.ts b/src/renderer/src/runtime/web-session-terminal-orphan-recovery-topology-fence.test.ts index 34442a52b5d..5271c5ee736 100644 --- a/src/renderer/src/runtime/web-session-terminal-orphan-recovery-topology-fence.test.ts +++ b/src/renderer/src/runtime/web-session-terminal-orphan-recovery-topology-fence.test.ts @@ -7,7 +7,7 @@ import { makeSnapshot, makeState, pendingSurface -} from './web-session-terminal-orphan-recovery-regression-fixtures' +} from './__fixtures__/web-session-terminal-orphan-recovery-regression-fixtures' import { clearWebSessionTerminalOrphanRecoveryForTests, recoverWebSessionTerminalOrphansBeforeApply diff --git a/src/renderer/src/runtime/web-session-terminal-orphan-recovery.ts b/src/renderer/src/runtime/web-session-terminal-orphan-recovery.ts index 99247e4ef09..3671817ff1c 100644 --- a/src/renderer/src/runtime/web-session-terminal-orphan-recovery.ts +++ b/src/renderer/src/runtime/web-session-terminal-orphan-recovery.ts @@ -81,6 +81,7 @@ async function recoverTerminalOrphans( const localTopologyIsCurrent = (): boolean => !getCurrentState || captureTerminalRecoveryTopologyToken(getCurrentState(), snapshot.worktree) === topologyToken + const isRecoveryCurrent = (): boolean => isCurrent() && localTopologyIsCurrent() const prepared = prepareTerminalOrphanRecovery(recoveryState, snapshot, environmentId) if ( prepared.candidates.length === 0 && @@ -97,10 +98,7 @@ async function recoverTerminalOrphans( expectedEnvironmentPairingRevision, isCurrent }) - if (!paneResolution || !isCurrent()) { - return null - } - if (!localTopologyIsCurrent()) { + if (!paneResolution || !isRecoveryCurrent()) { return null } const candidates = [...prepared.candidates, ...paneResolution.resolved] @@ -117,10 +115,7 @@ async function recoverTerminalOrphans( expectedEnvironmentPairingRevision, isCurrent }) - if (!inventory || !isCurrent()) { - return null - } - if (!localTopologyIsCurrent()) { + if (!inventory || !isRecoveryCurrent()) { return null } const { retained, removed, claims } = inventory @@ -220,7 +215,7 @@ async function recoverTerminalOrphans( const adoptedSnapshot = adoptionResponse.result.snapshot const adoptedRows = terminalRowsBySurface(adoptedSnapshot) - const missingClaims = claimSurfaces(candidates, claims).filter((surface) => { + const missingClaims = claimedSurfaces.filter((surface) => { const rows = adoptedRows.get(surfaceKey(surface.tabId, surface.leafId)) return !rows?.some(isValidReadySurface) })