From 609daf43d2057627fd39d545454bdeffec6a2aab Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Tue, 28 Jul 2026 16:01:32 -0700 Subject: [PATCH] test(worktrees): pin the disconnected-SSH no-teardown invariant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main's new directSshAuthority gate bails before any refresh when an SSH target is not connected. That is exactly the #10562 safety rule — "host unreachable" must never be read as "worktree deleted" — so pin it: a disconnected target issues no teardown RPC and keeps its renderer state. Co-authored-by: Orca --- .../src/store/slices/worktrees.test.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/renderer/src/store/slices/worktrees.test.ts b/src/renderer/src/store/slices/worktrees.test.ts index 6f994ecb206..37498110b66 100644 --- a/src/renderer/src/store/slices/worktrees.test.ts +++ b/src/renderer/src/store/slices/worktrees.test.ts @@ -787,6 +787,48 @@ describe('fetchWorktrees', () => { expect(store.getState().tabsByWorktree[deleted.id]).toBeUndefined() }) + // Why (#10562): "I can't reach the host" must never be read as "the worktree is + // gone". A disconnected SSH target has no authoritative scan, so it must not + // purge and must not kill anything on the far side. + it('never stops terminals for a disconnected SSH target', async () => { + const store = createTestStore() + const deleted = makeWorktree({ id: 'repo1::/p/deleted', repoId: 'repo1', path: '/p/deleted' }) + const surviving = makeWorktree({ id: 'repo1::/p/surv', repoId: 'repo1', path: '/p/surv' }) + + store.setState({ + repos: [ + { + id: 'repo1', + path: '/p/repo1', + displayName: 'R', + badgeColor: '#000', + addedAt: 0, + connectionId: TEST_SSH_AUTHORITY.targetId + } + ], + // Why: the target dropped — no connected authority for this host. + sshConnectionStates: new Map([ + [ + TEST_SSH_AUTHORITY.targetId, + { + targetId: TEST_SSH_AUTHORITY.targetId, + status: 'disconnected', + error: null, + reconnectAttempt: 1 + } + ] + ]), + worktreesByRepo: { repo1: [deleted, surviving] }, + detectedWorktreesByRepo: { repo1: makeDetectedResult('repo1', [deleted, surviving]) }, + tabsByWorktree: { [deleted.id]: [{ id: 'tab-d', worktreeId: deleted.id }] } + } as unknown as Partial) + + await store.getState().fetchWorktrees('repo1') + + expect(mockApi.runtime.call).not.toHaveBeenCalled() + expect(store.getState().tabsByWorktree[deleted.id]).toBeDefined() + }) + it('coalesces fetchDetectedWorktrees with a matching fetchWorktrees refresh', async () => { const store = createTestStore() const refreshed = makeWorktree({