diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index ccfa6d2f826..95657ce6dc4 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -11478,7 +11478,6 @@ "src/renderer/src/store/slices/worktrees/teardown/remove-worktree-shared-records.test.ts": 2086, "src/renderer/src/store/slices/worktrees/teardown/removed-workspace-unsaved-files.test.ts": 2481, "src/renderer/src/store/slices/worktrees/teardown/worktree-delete-state.test.ts": 1160, - "src/renderer/src/store/slices/wsl-hosts.test.ts": 1908, "src/renderer/src/store/slices/wsl-moved-project-chat-hydration.test.ts": 1658, "src/renderer/src/store/slices/wsl-moved-project-chat-purge.test.ts": 1222, "src/renderer/src/store/store-identity-churn-probe.test.ts": 67, @@ -11486,17 +11485,11 @@ "src/renderer/src/store/terminals/restored-relay-session-identity.test.ts": 2196, "src/renderer/src/store/terminals/restored-terminal-rebind-restarts.test.ts": 1886, "src/renderer/src/store/terminals/structured-chat-tab-rename.test.ts": 3154, - "src/renderer/src/store/terminals/terminal-chat-pane-layout-owner.test.ts": 1753, "src/renderer/src/store/terminals/terminal-exited-pane-liveness.test.ts": 1721, "src/renderer/src/store/terminals/terminal-no-op-subscriber.test.ts": 1763, - "src/renderer/src/store/terminals/terminal-pane-expansion-write-bailout.test.tsx": 1927, - "src/renderer/src/store/terminals/terminal-shutdown-guards-identity.test.ts": 47, - "src/renderer/src/store/terminals/terminal-sleep-layout-owner.test.ts": 58, "src/renderer/src/store/terminals/terminal-startup-queue-restart.test.ts": 1895, "src/renderer/src/store/terminals/terminal-tab-close-intent.test.ts": 1945, - "src/renderer/src/store/terminals/terminal-tab-create-through-layout-owner.test.ts": 1863, "src/renderer/src/store/terminals/terminal-tab-mirrored-id.test.ts": 1662, - "src/renderer/src/store/terminals/terminal-workspace-routing.scale.test.ts": 104, "src/renderer/src/store/terminals/workspace-terminal-placeholders.test.ts": 302, "src/renderer/src/store/use-runtime-status-view.test.tsx": 1979, "src/renderer/src/store/workspace-layout-cache.test.ts": 63, @@ -11504,7 +11497,6 @@ "src/renderer/src/store/workspace-record-selectors.test.ts": 93, "src/renderer/src/store/worktree-diff-comments-selector.test.ts": 346, "src/renderer/src/store/worktree-host-collision-index.test.ts": 1875, - "src/renderer/src/store/worktree-visibility-defaults-by-host.test.ts": 45, "src/renderer/src/web/preload-api/web-agent-session-attachment-upload.test.ts": 1588, "src/renderer/src/web/preload-api/web-ai-vault-search-compat.test.ts": 306, "src/renderer/src/web/preload-api/web-chat-permission-normalization.test.ts": 293, diff --git a/src/renderer/src/store/slices/worktrees/teardown/removed-workspace-unsaved-files.test.ts b/src/renderer/src/store/slices/worktrees/teardown/removed-workspace-unsaved-files.test.ts index 3ed6f94de4c..94b76aafe01 100644 --- a/src/renderer/src/store/slices/worktrees/teardown/removed-workspace-unsaved-files.test.ts +++ b/src/renderer/src/store/slices/worktrees/teardown/removed-workspace-unsaved-files.test.ts @@ -106,18 +106,6 @@ describe('confirmed discards apply only to the confirmed removal', () => { expect(shownContents()).toEqual(['typed after confirming']) }) - it('reaches the teardown through removeWorktree', async () => { - vi.stubGlobal('window', { api: createStoreCascadesMockApi() }) - const store = dirtyStore([GONE]) - const confirmed = confirmUnsavedDiscard(store.getState, [{ id: GONE, hostId: 'local' }]) - await store.getState().removeWorktree({ id: GONE, executionHostId: 'local' }, false, { - confirmedDiscards: confirmed - }) - expect(store.getState().openFiles).toEqual([]) - await settle() - expect(show).not.toHaveBeenCalled() - }) - it.each(purgePaths)('%s still offers text a dialog confirmed', async (_, purge, host) => { const store = dirtyStore([GONE], host) confirmUnsavedDiscard(store.getState, [{ id: GONE, hostId: host ?? 'local' }]) @@ -156,19 +144,6 @@ describe('delayed text is committed before a purge', () => { panes.forEach((unregister) => unregister()) } }) - - it('leaves a surviving workspace’s CSV cell editing', () => { - const survivor = `${TEST_REPO.id}::/survivor` - const store = dirtyStore([GONE, survivor]) - const cell = vi.fn() - const unregister = registerPendingEditorFlush(`${survivor}/file.ts`, cell, () => true) - try { - store.getState().purgeWorktreeTerminalState([GONE]) - expect(cell).not.toHaveBeenCalled() - } finally { - unregister() - } - }) }) describe('the closed-files notice', () => { @@ -237,14 +212,4 @@ describe('the closed-files notice', () => { unregister() } }) - - it('falls back to a sharing tab’s draft when the dirty tab has none', async () => { - const store = dirtyStore([GONE]) - const tab = (id: string) => - makeOpenFile({ id, filePath: '/w/shared.ts', worktreeId: GONE, isDirty: true }) - store.setState({ openFiles: [tab('first'), tab('second')], editorDrafts: { second: 'kept' } }) - store.getState().purgeWorktreeTerminalState([GONE]) - await vi.waitFor(() => expect(show).toHaveBeenCalledTimes(1)) - expect(shownContents()).toEqual(['kept']) - }) }) diff --git a/src/renderer/src/store/slices/wsl-hosts.test.ts b/src/renderer/src/store/slices/wsl-hosts.test.ts deleted file mode 100644 index a03c36659a0..00000000000 --- a/src/renderer/src/store/slices/wsl-hosts.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { expect, it } from 'vitest' -import type { WslHostStatus } from '../../../../shared/wsl-host-types' -import { createTestStore } from './store-test-helpers' - -it('publishes a deferral-only change for an already-ready WSL host', () => { - const store = createTestStore() - const ready: WslHostStatus = { - environmentId: 'wsl-ubuntu', - distro: 'Ubuntu', - state: 'ready', - detail: null - } - store.getState().applyWslHostStatus(ready) - const deferred: WslHostStatus = { - ...ready, - deferredUpdate: { - candidateVersion: 'next', - code: 'live-terminals', - reason: 'Terminals are still running.', - forceable: true - } - } - store.getState().applyWslHostStatus(deferred) - expect(store.getState().wslHostStatuses[ready.environmentId]).toEqual(deferred) -}) diff --git a/src/renderer/src/store/store-identity-churn-probe.test.ts b/src/renderer/src/store/store-identity-churn-probe.test.ts index 51fdb872254..79b44d90382 100644 --- a/src/renderer/src/store/store-identity-churn-probe.test.ts +++ b/src/renderer/src/store/store-identity-churn-probe.test.ts @@ -1,6 +1,6 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { create, type StoreApi } from 'zustand' -import { withReactCommitCascadeWriteProbe } from './react-commit-cascade-write-probe' +import { beforeEach, describe, expect, it } from 'vitest' +import { create } from 'zustand' +import './react-commit-cascade-write-probe' import { armStoreIdentityChurnProbe, disarmStoreIdentityChurnProbe, @@ -41,53 +41,6 @@ describe('store identity churn probe', () => { disarmStoreIdentityChurnProbe() }) - it('flags a refresh that rebuilds an array with unchanged contents', () => { - const store = createProbeStore() - armStoreIdentityChurnProbe() - - store.getState().refresh([{ id: 'a', label: 'A' }]) - - expect(churnFor('rows')).toBe(1) - expect(readStoreIdentityChurnReport()[0]).toMatchObject({ - field: 'rows', - churnedWrites: 1, - replacedWrites: 1, - sites: [] - }) - }) - - it('flags a keyed update that rewrites an entry with the same value', () => { - const store = createProbeStore() - armStoreIdentityChurnProbe() - - store.getState().touch('a', 'idle') - - expect(churnFor('entries')).toBe(1) - }) - - it('does not flag writes that change the value', () => { - const store = createProbeStore() - armStoreIdentityChurnProbe() - - store.getState().refresh([{ id: 'a', label: 'B' }]) - store.getState().touch('a', 'running') - store.getState().bump() - - expect(readStoreIdentityChurnReport()).toEqual([]) - }) - - it('names the write site when capture is requested', () => { - const store = createProbeStore() - armStoreIdentityChurnProbe({ captureSites: true }) - - store.getState().refresh([{ id: 'a', label: 'A' }]) - - const [row] = readStoreIdentityChurnReport() - expect(row.sites).toHaveLength(1) - expect(row.sites[0]).toMatchObject({ churnedWrites: 1 }) - expect(row.sites[0].site).toContain('store-identity-churn-probe.test') - }) - it('leaves a functional updater to zustand: called once, with the live state', () => { const store = createProbeStore() const seen: unknown[] = [] @@ -107,81 +60,4 @@ describe('store identity churn probe', () => { expect(store.getState().counter).toBe(1) expect(churnFor('rows')).toBe(1) }) - - it('passes disarmed writes straight through without reading state', () => { - const innerSet = vi.fn() - const innerGet = vi.fn(() => ({ counter: 0 })) - const api = { setState: innerSet, getState: innerGet } as unknown as StoreApi<{ - counter: number - }> - const creator = withStoreIdentityChurnProbe<{ counter: number }>(() => ({ counter: 0 })) - creator(innerSet, innerGet, api) - const updater = (state: { counter: number }) => ({ counter: state.counter + 1 }) - - api.setState(updater, true) - - // The disarmed path forwards the exact arguments and never calls get(). - expect(innerSet).toHaveBeenCalledTimes(1) - expect(innerSet.mock.calls[0]).toEqual([updater, true]) - expect(innerGet).not.toHaveBeenCalled() - }) - - it('composes with the cascade probe without dropping or doubling a write', () => { - // Mirrors store/index.ts: churn probe outermost, cascade probe inside it. - const store = create()( - withStoreIdentityChurnProbe( - withReactCommitCascadeWriteProbe((set) => ({ - rows: [{ id: 'a', label: 'A' }], - entries: { a: { status: 'idle' } }, - counter: 0, - refresh: (rows) => set({ rows }), - touch: (id, status) => - set((state) => ({ entries: { ...state.entries, [id]: { status } } })), - bump: () => set((state) => ({ counter: state.counter + 1 })) - })) - ) - ) - let updaterCalls = 0 - armStoreIdentityChurnProbe({ captureSites: true }) - - store.getState().bump() - store.setState((state) => { - updaterCalls += 1 - return { counter: state.counter + 10 } - }) - store.getState().refresh([{ id: 'a', label: 'A' }]) - store.setState({ ...store.getState(), counter: 100 }, true) - - expect(updaterCalls).toBe(1) - expect(store.getState().counter).toBe(100) - expect(store.getState().rows).toEqual([{ id: 'a', label: 'A' }]) - // The named site is this test, not the sibling probe's wrapper frame. - const [row] = readStoreIdentityChurnReport() - expect(row).toMatchObject({ field: 'rows', churnedWrites: 1 }) - expect(row.sites[0].site).toContain('store-identity-churn-probe.test') - }) - - it('still sees churn on a replace write', () => { - const store = createProbeStore() - const rows = store.getState().rows - armStoreIdentityChurnProbe() - - store.setState({ ...store.getState(), rows: [{ ...rows[0] }] }, true) - - expect(churnFor('rows')).toBe(1) - }) - - it('treats distinct class instances as changed rather than equal', () => { - const store = create<{ value: unknown; put: (value: unknown) => void }>()( - withStoreIdentityChurnProbe((set) => ({ - value: new Map([['a', 1]]), - put: (value) => set({ value }) - })) - ) - armStoreIdentityChurnProbe() - - store.getState().put(new Map([['a', 1]])) - - expect(readStoreIdentityChurnReport()).toEqual([]) - }) }) diff --git a/src/renderer/src/store/terminals/restored-relay-session-identity.test.ts b/src/renderer/src/store/terminals/restored-relay-session-identity.test.ts index db5247a92b2..950fefd615f 100644 --- a/src/renderer/src/store/terminals/restored-relay-session-identity.test.ts +++ b/src/renderer/src/store/terminals/restored-relay-session-identity.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import type { WorkspaceSessionState } from '../../../../shared/workspace-session-state-types' import { buildWorkspaceSessionPayload } from '@/lib/workspace-session' -import { getOrphanTerminalIds } from '../slices/terminal-orphan-helpers' +import '../slices/terminal-orphan-helpers' import { createTestStore, makeTab, makeWorktree } from '../slices/store-test-helpers' const layoutOwner = vi.hoisted(() => ({ isMain: false })) @@ -94,57 +94,5 @@ describe.each([false, true])( expect(payload.activeWorktreeIdsOnShutdown).toContain(WORKTREE_ID) expect(payload.activeConnectionIdsAtShutdown).toEqual([TARGET_ID]) }) - - it('rebinds the restored tab to its live relay PTY and keeps publishing that id', async () => { - const store = seedStore() - store.getState().hydrateWorkspaceSession(persistedSession()) - - await store.getState().reconnectPersistedTerminals() - - expect(store.getState().tabsByWorktree[WORKTREE_ID][0].ptyId).toBe(RELAY_PTY_ID) - expect(store.getState().ptyIdsByTabId[TAB_ID]).toEqual([RELAY_PTY_ID]) - expect(buildWorkspaceSessionPayload(store.getState()).remoteSessionIdsByTabId).toEqual({ - [TAB_ID]: RELAY_PTY_ID - }) - }) - - it('keeps a deferred relay session id while the host is disconnected', async () => { - const store = seedStore('disconnected') - store.getState().hydrateWorkspaceSession(persistedSession()) - await store.getState().reconnectPersistedTerminals() - - expect(store.getState().deferredSshSessionIdsByTabId[TAB_ID]).toBe(RELAY_PTY_ID) - // Loss of contact is not evidence the remote PTY exited, so the handle survives into the next write. - expect(buildWorkspaceSessionPayload(store.getState()).remoteSessionIdsByTabId).toEqual({ - [TAB_ID]: RELAY_PTY_ID - }) - }) - - it('leaves a tab whose relay handle is unverifiable alone instead of retiring it', async () => { - const store = seedStore('disconnected') - store.getState().hydrateWorkspaceSession(persistedSession()) - await store.getState().reconnectPersistedTerminals() - - // No live PTY, no row binding, host unreachable — `unverifiable`, never `exited`. - expect([...getOrphanTerminalIds(store.getState(), WORKTREE_ID)]).toEqual([]) - expect(store.getState().tabsByWorktree[WORKTREE_ID].map((tab) => tab.id)).toEqual([TAB_ID]) - }) - - it('does not invent a session id for a tab that never had one', () => { - const store = seedStore() - const session = persistedSession() - session.tabsByWorktree[WORKTREE_ID] = [ - makeTab({ id: TAB_ID, worktreeId: WORKTREE_ID, ptyId: null }) - ] - delete session.remoteSessionIdsByTabId - delete session.activeConnectionIdsAtShutdown - session.activeWorktreeIdsOnShutdown = [] - store.getState().hydrateWorkspaceSession(session) - - const payload = buildWorkspaceSessionPayload(store.getState()) - - expect(payload.remoteSessionIdsByTabId).toBeUndefined() - expect(payload.activeWorktreeIdsOnShutdown).toEqual([]) - }) } ) diff --git a/src/renderer/src/store/terminals/structured-chat-tab-rename.test.ts b/src/renderer/src/store/terminals/structured-chat-tab-rename.test.ts index 39475dbf0a3..964ab53b6c0 100644 --- a/src/renderer/src/store/terminals/structured-chat-tab-rename.test.ts +++ b/src/renderer/src/store/terminals/structured-chat-tab-rename.test.ts @@ -59,24 +59,6 @@ function structuredTab(): Tab { } } -const TERMINAL_TAB_ID = 'terminal-1' -const TERMINAL_UNIFIED_ID = 'unified-terminal-1' - -function terminalTab(): Tab { - return { - id: TERMINAL_UNIFIED_ID, - entityId: TERMINAL_TAB_ID, - groupId: 'group-1', - worktreeId: WORKTREE, - contentType: 'terminal', - label: 'Terminal', - customLabel: null, - color: null, - sortOrder: 1, - createdAt: 2 - } -} - function storeWithStructuredTab(): ReturnType { const store = createTestStore() seedStore(store, { @@ -112,43 +94,6 @@ function expectChange(windowShows: () => void, mainGets: Record } } -describe('renaming a terminal tab still resolves', () => { - it('routes a terminal rename through its entityId, not the unified id', async () => { - const store = createTestStore() - seedStore(store, { - repos: [{ id: 'local-repo', path: '/tmp/app', name: 'app' }] as never, - worktreesByRepo: { - 'local-repo': [makeWorktree({ id: WORKTREE, repoId: 'local-repo', path: '/tmp/app' })] - }, - unifiedTabsByWorktree: { [WORKTREE]: [terminalTab(), structuredTab()] } - }) - - // Keyed by the TERMINAL's entityId — the structured tab must not absorb it. - store.getState().setTabCustomTitle(TERMINAL_TAB_ID, 'Build logs') - await layoutApplyTestTransport.settle() - - const tabs = store.getState().unifiedTabsByWorktree[WORKTREE] ?? [] - expectChange( - () => expect(tabs.find((t) => t.id === TERMINAL_UNIFIED_ID)?.customLabel).toBe('Build logs'), - { type: 'renameTab', tabId: TERMINAL_UNIFIED_ID, title: 'Build logs' } - ) - expect(tabs.find((t) => t.id === STRUCTURED_TAB_ID)?.customLabel).toBeNull() - }) -}) - -describe('recoloring a structured chat tab', () => { - it('writes the color onto the agent-session tab', async () => { - const store = storeWithStructuredTab() - store.getState().setTabColor(STRUCTURED_TAB_ID, 'red') - await layoutApplyTestTransport.settle() - expectChange(() => expect(colorOf(store)).toBe('red'), { - type: 'setTabProps', - tabId: STRUCTURED_TAB_ID, - color: 'red' - }) - }) -}) - describe('renaming a structured chat tab', () => { beforeEach(() => { vi.mocked(toast.error).mockClear() diff --git a/src/renderer/src/store/terminals/terminal-chat-pane-layout-owner.test.ts b/src/renderer/src/store/terminals/terminal-chat-pane-layout-owner.test.ts deleted file mode 100644 index dc17f743963..00000000000 --- a/src/renderer/src/store/terminals/terminal-chat-pane-layout-owner.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type * as AgentStatusModule from '@/lib/agent-status' -import { createTestStore, makeWorktree, seedStore } from '../slices/store-test-helpers' -import { createStoreCascadesMockApi } from '../slices/store-cascades-test-harness' -import { _resetLayoutOwnerPaneIntentsForTests } from '@/runtime/layout-owner-pane-intents' - -import { LOCAL_EXECUTION_HOST_ID } from '../../../../shared/execution-host' -import { LayoutApplyParams } from '../../../../shared/rpc-contract/workspace-layout-params' -import { applyLayoutCommand } from '../../../../shared/workspace-layout/workspace-layout-commands' -import { layoutCommandsOf } from '@/runtime/layout-apply-test-double' -import type { - LayoutTerminalTab, - WorkspaceLayoutModel -} from '../../../../shared/workspace-layout/workspace-layout-model' - -const mocks = vi.hoisted(() => ({ - layoutOwnerIsMain: true, - callRuntimeRpc: vi.fn(async (_target: unknown, _method: string, _params: unknown) => ({ - ok: true, - result: {} - })) -})) - -vi.mock('../../../../shared/layout-owner-switch', () => ({ - get ORCA_LAYOUT_OWNER_IS_MAIN() { - return mocks.layoutOwnerIsMain - } -})) -vi.mock('@/runtime/runtime-rpc-client', () => ({ callRuntimeRpc: mocks.callRuntimeRpc })) -vi.mock('sonner', () => ({ - toast: { info: vi.fn(), success: vi.fn(), error: vi.fn(), warning: vi.fn() } -})) -vi.mock('@/lib/agent-status', async (importOriginal) => { - const actual = await importOriginal() - return { ...actual, detectAgentStatusFromTitle: vi.fn().mockReturnValue(null) } -}) - -createStoreCascadesMockApi() - -const WT = 'repo1::/path/wt1' -const LEAF = '11111111-1111-4111-8111-111111111111' - -let store: ReturnType -/** Main's copy; it changes only through what the window sends. */ -let main: WorkspaceLayoutModel - -beforeEach(() => { - mocks.layoutOwnerIsMain = true - mocks.callRuntimeRpc.mockClear() - _resetLayoutOwnerPaneIntentsForTests() - store = createTestStore() - seedStore(store, { - worktreesByRepo: { repo1: [makeWorktree({ id: WT, repoId: 'repo1', path: '/path/wt1' })] }, - activeWorktreeId: WT - }) - main = { - hostId: LOCAL_EXECUTION_HOST_ID, - workspaces: {}, - records: {}, - legacy: { terminalRowOwners: {} } - } -}) - -/** Applies what the window sent to main's copy, as `layout.apply` does; any refusal fails. */ -function applySent() { - const commands = mocks.callRuntimeRpc.mock.calls.flatMap(([, , params]) => - layoutCommandsOf(LayoutApplyParams.parse(params).command) - ) - mocks.callRuntimeRpc.mockClear() - let id = 0 - for (const command of commands) { - const applied = applyLayoutCommand(main, command, { - mintId: () => `main-${++id}`, - mintLeafId: () => `leaf-${++id}`, - now: () => 1 - }) - if (!applied.ok) { - throw new Error(`${command.type} refused: ${applied.code}`) - } - main = applied.model - } - return commands -} - -function mainTab(tabId: string): LayoutTerminalTab { - const tab = main.workspaces[WT]!.tabs.find((entry) => entry.id === tabId) - if (tab?.kind !== 'terminal') { - throw new Error('main holds no such terminal tab') - } - return tab -} - -/** A one-pane tab main holds, as the window made it. */ -function heldTab(): string { - const tab = store.getState().createTab(WT, undefined, undefined, { initialLeafId: LEAF }) - applySent() - return tab.id -} - -function setChatLeaf(tabId: string, chatLeafId: string | null): void { - const { chatLeafId: _old, ...layout } = store.getState().terminalLayoutsByTabId[tabId]! - store.getState().setTabLayout(tabId, chatLeafId ? { ...layout, chatLeafId } : layout) -} - -describe('the chat pane in a layout-owner build', () => { - it('records each chat pane the window picks in main, so its save changes none', () => { - const tabId = heldTab() - - setChatLeaf(tabId, LEAF) - expect(applySent()).toEqual([{ type: 'setChatPane', workspace: WT, tabId, leafId: LEAF }]) - expect(mainTab(tabId).panes.chatLeafId).toBe(LEAF) - - setChatLeaf(tabId, null) - applySent() - expect(mainTab(tabId).panes.chatLeafId).toBeUndefined() - }) -}) diff --git a/src/renderer/src/store/terminals/terminal-no-op-subscriber.test.ts b/src/renderer/src/store/terminals/terminal-no-op-subscriber.test.ts index 0568dd0428e..9cc6854e9ba 100644 --- a/src/renderer/src/store/terminals/terminal-no-op-subscriber.test.ts +++ b/src/renderer/src/store/terminals/terminal-no-op-subscriber.test.ts @@ -3,42 +3,11 @@ import { flushTerminalInputActivity, resetTerminalInputActivityCoalescingForTests } from '@/lib/terminal-input-activity-coalescing' -import { createTestStore, makeLayout } from '../slices/store-test-helpers' +import { createTestStore } from '../slices/store-test-helpers' afterEach(resetTerminalInputActivityCoalescingForTests) describe('terminal no-op subscriber budget', () => { - it('publishes real mutations once and keeps repeated actions silent', () => { - const store = createTestStore() - const draft = { tabId: 'tab', agent: 'codex', text: 'draft', createdAt: 1 } as const - store.getState().seedNativeChatLaunchPrompt(draft) - store.getState().seedNativeChatLaunchDraft(draft) - store.getState().setTabLayout('tab', makeLayout()) - const listener = vi.fn() - store.subscribe(listener) - - const actions = [ - () => store.getState().markDefaultTerminalTabsApplied('folder-workspace'), - () => store.getState().markUnverifiedPtyLoss('tab'), - () => store.getState().markPtySourceDisowned('pty'), - () => store.getState().markNativeChatLaunchPromptFailed('tab'), - () => store.getState().markNativeChatLaunchDraftAdopted('tab'), - () => store.getState().resolveNativeChatLaunchDraft('tab', draft), - () => store.getState().replaceTerminalLayoutPanePtyId('tab', 'leaf', 'pty'), - () => store.getState().clearNativeChatLaunchPrompt('tab'), - () => store.getState().clearNativeChatLaunchDraft('tab') - ] - for (const action of actions) { - listener.mockClear() - action() - expect(listener).toHaveBeenCalledTimes(1) - const before = store.getState() - action() - expect(listener).toHaveBeenCalledTimes(1) - expect(store.getState()).toBe(before) - } - }) - it('retains draft generations when stale resolutions arrive without notifying', () => { const store = createTestStore() const draft = { tabId: 'tab', agent: 'codex', text: 'new draft', createdAt: 2 } as const diff --git a/src/renderer/src/store/terminals/terminal-pane-expansion-write-bailout.test.tsx b/src/renderer/src/store/terminals/terminal-pane-expansion-write-bailout.test.tsx deleted file mode 100644 index d0d32f7742f..00000000000 --- a/src/renderer/src/store/terminals/terminal-pane-expansion-write-bailout.test.tsx +++ /dev/null @@ -1,152 +0,0 @@ -// @vitest-environment happy-dom - -import { Profiler } from 'react' -import { act, cleanup, render } from '@testing-library/react' -import { afterEach, describe, expect, it } from 'vitest' -import { createTestStore } from '../slices/store-test-helpers' - -afterEach(cleanup) - -type TestStore = ReturnType - -const TAB_ID = 'tab-1' -const NO_OP_WRITES = 25 - -function recordPublishedMapKeys(store: TestStore): string[] { - const published: string[] = [] - store.subscribe((next, previous) => { - if (next.expandedPaneByTabId !== previous.expandedPaneByTabId) { - published.push('expandedPaneByTabId') - } - if (next.canExpandPaneByTabId !== previous.canExpandPaneByTabId) { - published.push('canExpandPaneByTabId') - } - }) - return published -} - -// Mirrors use-terminal-workspace-store-bindings.ts:17, which subscribes to the raw map. -function ExpandedPaneSubscriber({ store }: { store: TestStore }): React.JSX.Element { - const expandedPaneByTabId = store((s) => s.expandedPaneByTabId) - return {String(expandedPaneByTabId[TAB_ID] === true)} -} - -function CanExpandPaneSubscriber({ store }: { store: TestStore }): React.JSX.Element { - const canExpandPaneByTabId = store((s) => s.canExpandPaneByTabId) - return {String(canExpandPaneByTabId[TAB_ID] === true)} -} - -function renderCommitCounter(subscriber: React.JSX.Element): () => number { - let commits = 0 - render( - { - commits += 1 - }} - > - {subscriber} - - ) - const mountCommits = commits - return () => commits - mountCommits -} - -describe('setTabPaneExpanded', () => { - it('publishes the first write for an unseen tab and a real toggle', () => { - const store = createTestStore() - const published = recordPublishedMapKeys(store) - - store.getState().setTabPaneExpanded(TAB_ID, false) - expect(published).toEqual(['expandedPaneByTabId']) - expect(store.getState().expandedPaneByTabId[TAB_ID]).toBe(false) - - store.getState().setTabPaneExpanded(TAB_ID, true) - expect(published).toEqual(['expandedPaneByTabId', 'expandedPaneByTabId']) - expect(store.getState().expandedPaneByTabId[TAB_ID]).toBe(true) - }) - - it('bails out when the value is unchanged', () => { - const store = createTestStore() - store.getState().setTabPaneExpanded(TAB_ID, false) - const before = store.getState().expandedPaneByTabId - // Root identity too: returning `{}` keeps the map but allocates a new root, so zustand still walks every listener. - const rootBefore = store.getState() - const published = recordPublishedMapKeys(store) - - for (let i = 0; i < NO_OP_WRITES; i += 1) { - store.getState().setTabPaneExpanded(TAB_ID, false) - } - - expect(published).toEqual([]) - expect(store.getState().expandedPaneByTabId).toBe(before) - expect(store.getState()).toBe(rootBefore) - }) - - it('costs no React commit in a map subscriber when the value is unchanged', () => { - const store = createTestStore() - store.getState().setTabPaneExpanded(TAB_ID, false) - const commitsSinceMount = renderCommitCounter() - - for (let i = 0; i < NO_OP_WRITES; i += 1) { - act(() => { - store.getState().setTabPaneExpanded(TAB_ID, false) - }) - } - expect(commitsSinceMount()).toBe(0) - - act(() => { - store.getState().setTabPaneExpanded(TAB_ID, true) - }) - expect(commitsSinceMount()).toBe(1) - }) -}) - -describe('setTabCanExpandPane', () => { - it('publishes the first write for an unseen tab and a real toggle', () => { - const store = createTestStore() - const published = recordPublishedMapKeys(store) - - store.getState().setTabCanExpandPane(TAB_ID, false) - expect(published).toEqual(['canExpandPaneByTabId']) - expect(store.getState().canExpandPaneByTabId[TAB_ID]).toBe(false) - - store.getState().setTabCanExpandPane(TAB_ID, true) - expect(published).toEqual(['canExpandPaneByTabId', 'canExpandPaneByTabId']) - expect(store.getState().canExpandPaneByTabId[TAB_ID]).toBe(true) - }) - - it('bails out when the value is unchanged', () => { - const store = createTestStore() - store.getState().setTabCanExpandPane(TAB_ID, false) - const before = store.getState().canExpandPaneByTabId - const rootBefore = store.getState() - const published = recordPublishedMapKeys(store) - - for (let i = 0; i < NO_OP_WRITES; i += 1) { - store.getState().setTabCanExpandPane(TAB_ID, false) - } - - expect(published).toEqual([]) - expect(store.getState().canExpandPaneByTabId).toBe(before) - expect(store.getState()).toBe(rootBefore) - }) - - it('costs no React commit in a map subscriber when the value is unchanged', () => { - const store = createTestStore() - store.getState().setTabCanExpandPane(TAB_ID, false) - const commitsSinceMount = renderCommitCounter() - - for (let i = 0; i < NO_OP_WRITES; i += 1) { - act(() => { - store.getState().setTabCanExpandPane(TAB_ID, false) - }) - } - expect(commitsSinceMount()).toBe(0) - - act(() => { - store.getState().setTabCanExpandPane(TAB_ID, true) - }) - expect(commitsSinceMount()).toBe(1) - }) -}) diff --git a/src/renderer/src/store/terminals/terminal-shutdown-guards-identity.test.ts b/src/renderer/src/store/terminals/terminal-shutdown-guards-identity.test.ts deleted file mode 100644 index b184f6390fb..00000000000 --- a/src/renderer/src/store/terminals/terminal-shutdown-guards-identity.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import type { AppState } from '../types' -import { createTerminalShutdownGuardController } from './terminal-shutdown-guards' - -vi.mock('@/components/terminal-pane/pty-dispatcher', () => ({ - restorePtyDataHandlersAfterFailedShutdown: vi.fn(), - unregisterPtyDataHandlers: vi.fn(() => []) -})) -vi.mock('@/components/terminal-pane/terminal-parked-watcher-registry', () => ({ - disposeParkedTerminalWatchersForPtyIds: vi.fn() -})) -vi.mock('@/components/terminal-pane/pty-shutdown-exit-deferral', () => ({ - clearCommittedPtyShutdownSettlements: vi.fn(), - hasCommittedPtyShutdownSettlement: vi.fn(() => false), - markCommittedPtyShutdowns: vi.fn(), - noteCommittedPtyShutdownSettlements: vi.fn(), - settleDeferredPtyShutdownExits: vi.fn() -})) - -function harness(initial: Partial, exitGuardPtyIds: readonly string[]) { - let current = initial as AppState - const set = vi.fn((update: unknown) => { - const patch = - typeof update === 'function' ? (update as (s: AppState) => object)(current) : update - current = { ...current, ...(patch as object) } - }) - const guards = createTerminalShutdownGuardController({ - exitGuardPtyIds, - get: (() => current) as never, - keepIdentifiers: false, - rendererShutdownPtyIds: exitGuardPtyIds, - runtimeOwnsRemotePtys: false, - set: set as never, - tabs: [] - }) - return { guards, set, state: () => current } -} - -describe('markShutdownPending identity', () => { - it('does not write the store when there is nothing to guard', () => { - const { guards, set } = harness({ suppressedPtyExitIds: {}, pendingPtyShutdownIds: {} }, []) - - guards.markShutdownPending() - - expect(set).not.toHaveBeenCalled() - }) - - it('still counts a pending owner when every id is already suppressed', () => { - const suppressedPtyExitIds: Record = { 'pty-1': true } - const { guards, state } = harness( - { suppressedPtyExitIds, pendingPtyShutdownIds: { 'pty-1': 1 } }, - ['pty-1'] - ) - - guards.markShutdownPending() - - expect(state().suppressedPtyExitIds).toBe(suppressedPtyExitIds) - expect(state().pendingPtyShutdownIds).toEqual({ 'pty-1': 2 }) - }) - - it('suppresses the ids that were not yet suppressed', () => { - const { guards, state } = harness( - { suppressedPtyExitIds: { 'pty-1': true }, pendingPtyShutdownIds: {} }, - ['pty-1', 'pty-2'] - ) - - guards.markShutdownPending() - - expect(state().suppressedPtyExitIds).toEqual({ 'pty-1': true, 'pty-2': true }) - expect(state().pendingPtyShutdownIds).toEqual({ 'pty-1': 1, 'pty-2': 1 }) - }) -}) diff --git a/src/renderer/src/store/terminals/terminal-sleep-layout-owner.test.ts b/src/renderer/src/store/terminals/terminal-sleep-layout-owner.test.ts deleted file mode 100644 index cfe60cbc499..00000000000 --- a/src/renderer/src/store/terminals/terminal-sleep-layout-owner.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { SleepingAgentSessionRecord } from '../../../../shared/agent-session/lifecycle/agent-session-resume' -import type { TerminalTab } from '../../../../shared/terminal-tab-types' -import type { recordLayoutApply } from '@/runtime/layout-apply-send' -import { - recordPaneSleepThroughLayoutOwner, - recordPaneWakeThroughLayoutOwner, - recordSleepingRecordChangesThroughLayoutOwner -} from './terminal-sleep-layout-owner' - -const mocks = vi.hoisted(() => ({ send: vi.fn() })) - -vi.mock('@/runtime/layout-apply-send', () => ({ recordLayoutApply: mocks.send })) - -const WT = 'repo1::/path/wt1' -const STATE = {} -const A1 = 'tab-a:11111111-1111-4111-8111-111111111111' -const A2 = 'tab-a:22222222-2222-4222-8222-222222222222' -const B1 = 'tab-b:33333333-3333-4333-8333-333333333333' - -function record(paneKey: string): SleepingAgentSessionRecord { - return { - paneKey, - worktreeId: WT, - agent: 'codex', - providerSession: { key: 'session_id', id: paneKey }, - prompt: '', - state: 'done', - capturedAt: 1, - updatedAt: 1 - } -} - -/** A window holding these terminal tabs in WT. */ -function holding(tabIds: string[]): { tabsByWorktree: Record } { - return { - tabsByWorktree: { - [WT]: tabIds.map((id) => ({ - id, - ptyId: null, - worktreeId: WT, - title: 'Terminal', - customTitle: null, - color: null, - sortOrder: 0, - createdAt: 1 - })) - } - } -} - -describe('sleep and wake through the layout owner', () => { - beforeEach(() => { - mocks.send.mockReset() - }) - - it('sends one sleep per tab naming only the panes it stopped', () => { - const records = { - [A1]: record(A1), - [A2]: record(A2), - [B1]: record(B1), - // A legacy pane key names no pane main holds. - 'tab-a:1': record('tab-a:1') - } - - recordPaneSleepThroughLayoutOwner(STATE, WT, records) - - expect(mocks.send.mock.calls.map((call) => call[2])).toEqual([ - { type: 'sleep', tabId: 'tab-a', paneKeys: [A1, A2], records: [record(A1), record(A2)] }, - { type: 'sleep', tabId: 'tab-b', paneKeys: [B1], records: [record(B1)] } - ]) - }) - - it('sends nothing when nothing slept, as a worktree removal', () => { - recordPaneSleepThroughLayoutOwner(STATE, WT, {}) - - expect(mocks.send).not.toHaveBeenCalled() - }) - - it('records the wake of the one pane the window resumed, and has main start nothing', () => { - recordPaneWakeThroughLayoutOwner(STATE, WT, A2) - - expect(mocks.send).toHaveBeenCalledWith(STATE, WT, { - type: 'wake', - tabId: 'tab-a', - paneKeys: [A2], - recordOnly: true - }) - }) - - it('records a live capture and a dropped record without stopping or starting anything', () => { - const live = { ...record(A1), state: 'working' as const, origin: 'live' as const } - const kept = record(B1) - - recordSleepingRecordChangesThroughLayoutOwner( - holding(['tab-a', 'tab-b']), - { [A2]: record(A2), [B1]: kept }, - { [A1]: live, [B1]: kept } - ) - - expect(mocks.send.mock.calls.map((call) => call[2])).toEqual([ - { type: 'sleep', tabId: 'tab-a', paneKeys: [A1], records: [live], recordOnly: true }, - { type: 'wake', tabId: 'tab-a', paneKeys: [A2], recordOnly: true } - ]) - }) - - it('sends nothing for a tab the window closed: main dropped its records with the tab', () => { - recordSleepingRecordChangesThroughLayoutOwner(holding(['tab-b']), { [A2]: record(A2) }, {}) - // A periodic capture from a stale agent status can name the closed tab again. - recordSleepingRecordChangesThroughLayoutOwner(holding(['tab-b']), {}, { [A1]: record(A1) }) - - expect(mocks.send).not.toHaveBeenCalled() - }) -}) diff --git a/src/renderer/src/store/terminals/terminal-tab-close-intent.test.ts b/src/renderer/src/store/terminals/terminal-tab-close-intent.test.ts index b40b2d5cbfc..dfdbfff5b51 100644 --- a/src/renderer/src/store/terminals/terminal-tab-close-intent.test.ts +++ b/src/renderer/src/store/terminals/terminal-tab-close-intent.test.ts @@ -89,19 +89,6 @@ vi.mock('@/lib/agent-status', async (importOriginal) => ({ mockApi.worktrees.updateMeta.mockResolvedValue({}) }) - it('commits the close in main even when the SSH shutdown throws', async () => { - mockApi.pty.kill.mockRejectedValue(new Error('relay shutdown failed')) - const store = storeWithSshTab() - - store.getState().closeTab('ssh-tab') - await Promise.resolve() - - expect(mockApi.pty.kill).toHaveBeenCalledWith(SSH_PTY) - expect(sentCloses(closeTerminalSurface)).toEqual([ - { worktreeId: SSH_WORKTREE, tabId: 'ssh-tab', reason: 'user' } - ]) - }) - it('removes the tab and kills it without waiting for main to write the close', async () => { closeTerminalSurface.mockReturnValue(new Promise(() => {})) layoutApplyTestTransport.answerWith('pending') @@ -114,27 +101,6 @@ vi.mock('@/lib/agent-status', async (importOriginal) => ({ await Promise.resolve() expect(mockApi.pty.kill).toHaveBeenCalledWith(SSH_PTY) }) - - it.each([['user'], ['cleanup']] as const)('sends the intent for a %s close', (reason) => { - storeWithSshTab().getState().closeTab('ssh-tab', { reason }) - - expect(sentCloses(closeTerminalSurface)).toEqual([ - { worktreeId: SSH_WORKTREE, tabId: 'ssh-tab', reason } - ]) - }) - - // Why: exits still retire through main's own exit handling until main decides exits itself. - it('sends nothing for a pty-exit close', () => { - storeWithSshTab().getState().closeTab('ssh-tab', { reason: 'pty-exit' }) - - expect(sentCloses(closeTerminalSurface)).toEqual([]) - }) - - it('sends nothing when a paired host owns the close', () => { - storeWithSshTab().getState().closeTab('ssh-tab', { remoteCloseOwnedByHost: true }) - - expect(sentCloses(closeTerminalSurface)).toEqual([]) - }) }) } @@ -144,30 +110,6 @@ vi.mock('@/lib/agent-status', async (importOriginal) => ({ const WORKTREE = 'repo::/tmp/app' - /** Maps a closing tab has no entry in; closing must not give them a new reference. */ - const UNTOUCHED_FIELDS = [ - 'terminalLayoutsByTabId', - 'ptyIdsByTabId', - 'runtimePaneTitlesByTabId', - 'lastKnownRelayPtyIdByTabId', - 'deferredSshSessionIdsByTabId', - 'pendingReconnectPtyIdByTabId', - 'pendingStartupByTabId', - 'automaticAgentResumeClaimsByTabId', - 'nativeChatLaunchPromptByTabId', - 'nativeChatLaunchDraftByTabId', - 'pendingInitialCwdByTabId', - 'pendingIssueCommandSplitByTabId', - 'expandedPaneByTabId', - 'canExpandPaneByTabId', - 'cacheTimerByKey', - 'lastTerminalInputAtByPaneKey', - 'unreadTerminalTabs', - 'unreadTerminalPanes', - 'unreadAgentCompletionPanes', - 'tabBarOrderByWorktree' - ] as const - function storeWithTwoTabs(): ReturnType { const store = createTestStore() seedStore(store, { @@ -195,23 +137,6 @@ vi.mock('@/lib/agent-status', async (importOriginal) => ({ mockApi.worktrees.updateMeta.mockResolvedValue({}) }) - it('keeps the reference of every per-tab map the closing tab had no entry in', () => { - const store = storeWithTwoTabs() - const before = store.getState() - const snapshot = Object.fromEntries( - UNTOUCHED_FIELDS.map((field) => [field, before[field]]) - ) as Record - - store.getState().closeTab('tab-a') - - const after = store.getState() - // The tab really closed — otherwise the identity assertions below are vacuous. - expect(after.tabsByWorktree[WORKTREE].map((tab) => tab.id)).toEqual(['tab-b']) - for (const field of UNTOUCHED_FIELDS) { - expect(after[field], field).toBe(snapshot[field]) - } - }) - it('still drops the closing tab from a map that did hold it', () => { const store = storeWithTwoTabs() store.setState({ @@ -233,88 +158,3 @@ vi.mock('@/lib/agent-status', async (importOriginal) => ({ }) }) } - -{ - const mockApi = createStoreCascadesMockApi() - const mockWindow = globalThis.window - - const REMOTE_WORKTREE = 'remote-repo::/srv/app' - const LOCAL_WORKTREE = 'local-repo::/tmp/app' - - function storeWithBothWorktrees(): ReturnType { - const store = createTestStore() - seedStore(store, { - repos: [ - { id: 'remote-repo', path: '/srv/app', name: 'app', connectionId: 'ssh-1' }, - { id: 'local-repo', path: '/tmp/app', name: 'app' } - ] as never, - worktreesByRepo: { - 'remote-repo': [ - makeWorktree({ id: REMOTE_WORKTREE, repoId: 'remote-repo', path: '/srv/app' }) - ], - 'local-repo': [makeWorktree({ id: LOCAL_WORKTREE, repoId: 'local-repo', path: '/tmp/app' })] - }, - tabsByWorktree: { - [REMOTE_WORKTREE]: [makeTab({ id: 'remote-tab', worktreeId: REMOTE_WORKTREE })], - [LOCAL_WORKTREE]: [makeTab({ id: 'local-tab', worktreeId: LOCAL_WORKTREE })] - } - }) - return store - } - - const closeTerminalSurface = vi.fn() - Object.assign(mockApi, { session: { closeTerminalSurface } }) - - describe('closeTab close-record mirror', () => { - beforeEach(() => { - globalThis.window = mockWindow - }) - - beforeEach(() => { - vi.clearAllMocks() - layoutApplyTestTransport.reset() - closeTerminalSurface.mockResolvedValue(undefined) - mockApi.worktrees.updateMeta.mockResolvedValue({}) - }) - - // Cover every workspace kind: close mirror suppresses re-seeding before main’s saved copy hydrates. - it.each([ - ['remote-tab', REMOTE_WORKTREE], - ['local-tab', LOCAL_WORKTREE] - ])('mirrors the record main is told to write for %s', (tabId, worktreeId) => { - const store = storeWithBothWorktrees() - - store.getState().closeTab(tabId) - - expect(store.getState().closedTerminalTabTombstonesByTabId[tabId]).toEqual({ - closedAt: expect.any(Number), - worktreeId, - reason: 'user' - }) - expect(sentCloses(closeTerminalSurface)).toEqual([{ worktreeId, tabId, reason: 'user' }]) - }) - - it('mirrors a cleanup close with its reason', () => { - const store = storeWithBothWorktrees() - - store.getState().closeTab('remote-tab', { reason: 'cleanup' }) - - expect(store.getState().closedTerminalTabTombstonesByTabId['remote-tab']?.reason).toBe( - 'cleanup' - ) - }) - - // Main alone closes for a process exit; a paired host owns its own records. - it.each([ - ['a pty-exit close', { reason: 'pty-exit' as const }], - ['a host-owned close', { remoteCloseOwnedByHost: true }] - ])('mirrors nothing for %s', (_label, opts) => { - const store = storeWithBothWorktrees() - - store.getState().closeTab('remote-tab', opts) - - expect(store.getState().closedTerminalTabTombstonesByTabId).toEqual({}) - expect(sentCloses(closeTerminalSurface)).toEqual([]) - }) - }) -} diff --git a/src/renderer/src/store/terminals/terminal-tab-create-through-layout-owner.test.ts b/src/renderer/src/store/terminals/terminal-tab-create-through-layout-owner.test.ts deleted file mode 100644 index d9959c2846a..00000000000 --- a/src/renderer/src/store/terminals/terminal-tab-create-through-layout-owner.test.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { AppState } from '../types' -import type { LayoutApplyReply } from '../../../../shared/workspace-layout/workspace-layout-command-types' -import { - _resetLayoutFramePositionForTests, - beginLayoutFramePositions, - endLayoutFramePositions, - noteLayoutFramePosition -} from '@/runtime/layout-frame-position' -import { - _resetLayoutOwnerPaneIntentsForTests, - takeTabRowFields -} from '@/runtime/layout-owner-pane-intents' -import { createTestStore, makeTab } from '../slices/store-test-helpers' -import { - createInitialTerminalThroughLayoutOwner, - recordCreatedTerminalTabInLayoutOwner -} from './terminal-tab-create-through-layout-owner' - -const mocks = vi.hoisted(() => { - const held: { - pairedServer: string | null - twin: boolean - reply: ((value: LayoutApplyReply) => void) | null - fail: ((error: Error) => void) | null - } = { pairedServer: null, twin: false, reply: null, fail: null } - return Object.assign(held, { callRuntimeRpc: vi.fn() }) -}) - -vi.mock('@/runtime/runtime-rpc-client', () => ({ callRuntimeRpc: mocks.callRuntimeRpc })) -vi.mock('@/lib/resolve-owner', () => ({ - runtimeEnvironmentIdUnlessTwin: () => mocks.pairedServer, - runtimeEnvironmentIdForWorkspaceOwner: () => - mocks.twin ? { kind: 'ambiguous' } : { kind: 'resolved', environmentId: null } -})) - -const WORKTREE = 'repo1::/path/wt' - -function stateWith(tabIds: string[] = [], activeWorktreeId = WORKTREE): AppState { - const store = createTestStore() - store.setState({ - activeWorktreeId, - tabsByWorktree: { [WORKTREE]: tabIds.map((id) => makeTab({ id, worktreeId: WORKTREE })) } - }) - return store.getState() -} - -function start(state: () => AppState = () => stateWith()) { - const create = vi.fn() - const routed = createInitialTerminalThroughLayoutOwner(state, WORKTREE, create) - const command = mocks.callRuntimeRpc.mock.calls[0]?.[2]?.command - return { create, routed, command } -} - -const settle = () => new Promise((resolve) => setTimeout(resolve, 0)) - -beforeEach(() => { - _resetLayoutOwnerPaneIntentsForTests() - endLayoutFramePositions() - mocks.pairedServer = null - mocks.twin = false - mocks.callRuntimeRpc.mockReset() - mocks.callRuntimeRpc.mockImplementation( - () => - new Promise((resolve, reject) => { - mocks.reply = resolve - mocks.fail = reject - }) - ) -}) - -describe('createInitialTerminalThroughLayoutOwner', () => { - it('asks main for the first tab and leaves drawing it, with the window’s row fields, to main’s frame', async () => { - const { create, routed, command } = start() - - expect(routed).toBe(true) - expect(command).toMatchObject({ - type: 'createTerminalTab', - workspace: WORKTREE, - initial: 'activation' - }) - noteLayoutFramePosition(1) - mocks.reply?.({ ok: true, result: { tabId: command.tabId, leafId: command.leafId } }) - await settle() - - expect(create).not.toHaveBeenCalled() - // Kept for the frame, however late an owed write lets it in. - expect(takeTabRowFields(command.tabId)).toEqual({ pendingActivationSpawn: true }) - }) - - it('draws nothing itself when no frame is coming: the reopened stream’s snapshot brings it', async () => { - const { create, command } = start() - - mocks.reply?.({ ok: true, result: { tabId: command.tabId, leafId: command.leafId } }) - await settle() - - expect(create).not.toHaveBeenCalled() - expect(takeTabRowFields(command.tabId)).toEqual({ pendingActivationSpawn: true }) - }) - - it('draws nothing itself when the stream ends before main’s frame', async () => { - _resetLayoutFramePositionForTests() - beginLayoutFramePositions() - const { create, command } = start() - - mocks.reply?.({ - ok: true, - result: { tabId: command.tabId, leafId: command.leafId }, - frameSeq: 1 - }) - await settle() - endLayoutFramePositions() - - expect(create).not.toHaveBeenCalled() - expect(takeTabRowFields(command.tabId)).toEqual({ pendingActivationSpawn: true }) - }) - - it('draws nothing itself when a frame showed the tab before the stream ended', async () => { - let state = stateWith() - const { create, command } = start(() => state) - - state = stateWith([command.tabId]) - mocks.reply?.({ ok: true, result: { tabId: command.tabId, leafId: command.leafId } }) - await settle() - - expect(create).not.toHaveBeenCalled() - }) - - it('draws nothing when main’s copy already has a tab there', async () => { - const { create, command } = start() - - mocks.reply?.({ ok: true, result: {} }) - await settle() - - expect(create).not.toHaveBeenCalled() - expect(takeTabRowFields(command.tabId)).toBeNull() - }) - - it('opens the terminal as today when main holds no partition for the workspace, or fails', async () => { - const unowned = start() - mocks.reply?.({ ok: false, code: 'host_not_owned' }) - await settle() - expect(unowned.create).toHaveBeenCalledWith() - - mocks.callRuntimeRpc.mockClear() - const failed = start() - mocks.fail?.(new Error('layout_store_unavailable')) - await settle() - expect(failed.create).toHaveBeenCalledWith() - expect(takeTabRowFields(failed.command.tabId)).toBeNull() - }) - - it('opens nothing as today once the user has left the workspace', async () => { - let state = stateWith() - const { create } = start(() => state) - - state = stateWith([], 'repo1::/path/other') - mocks.reply?.({ ok: false, code: 'host_not_owned' }) - await settle() - - expect(create).not.toHaveBeenCalled() - }) - - it('opens nothing as today once the workspace got a tab meanwhile', async () => { - let state = stateWith() - const { create } = start(() => state) - - state = stateWith(['tab-opened-meanwhile']) - mocks.fail?.(new Error('layout_store_unavailable')) - await settle() - - expect(create).not.toHaveBeenCalled() - }) - - it('leaves a paired server’s workspace to the caller', () => { - mocks.pairedServer = 'env-1' - - const { routed } = start() - - expect(routed).toBe(false) - expect(mocks.callRuntimeRpc).not.toHaveBeenCalled() - }) -}) - -describe('recordCreatedTerminalTabInLayoutOwner', () => { - const command = { - type: 'createTerminalTab' as const, - workspace: WORKTREE, - tabId: 'tab-drawn', - leafId: 'leaf-drawn' - } - - it.each([ - [false, 1], - [true, 0] - ])( - 'on host_not_owned (twin: %s), closes the drawn tab unless a paired server may hold it', - async (twin, closes) => { - vi.spyOn(console, 'warn').mockImplementation(() => {}) - mocks.twin = twin - const state = stateWith(['tab-drawn']) - const closeTab = vi.fn() - recordCreatedTerminalTabInLayoutOwner({ ...state, closeTab }, command) - mocks.reply?.({ ok: false, code: 'host_not_owned' }) - await settle() - expect(closeTab).toHaveBeenCalledTimes(closes) - } - ) -}) diff --git a/src/renderer/src/store/terminals/terminal-tab-mirrored-id.test.ts b/src/renderer/src/store/terminals/terminal-tab-mirrored-id.test.ts index 055cfdf25a9..ca29bf454e6 100644 --- a/src/renderer/src/store/terminals/terminal-tab-mirrored-id.test.ts +++ b/src/renderer/src/store/terminals/terminal-tab-mirrored-id.test.ts @@ -26,15 +26,4 @@ describe('createTab with a mirrored host tab id', () => { store.getState().createTab(WT, undefined, undefined, { id: MIRRORED, mirroredHostTab: true }) ).toThrow(/already exists/) }) - it('still ignores a mirrored id from a caller that did not opt in', () => { - const tab = seeded().getState().createTab(WT, undefined, undefined, { id: MIRRORED }) - expect(tab.id).not.toBe(MIRRORED) - }) - it('ignores an opted-in id that is not a mirror of a valid host tab id', () => { - const tab = seeded().getState().createTab(WT, undefined, undefined, { - id: 'plain-tab', - mirroredHostTab: true - }) - expect(tab.id).not.toBe('plain-tab') - }) }) diff --git a/src/renderer/src/store/terminals/terminal-workspace-routing.scale.test.ts b/src/renderer/src/store/terminals/terminal-workspace-routing.scale.test.ts deleted file mode 100644 index 8d4b3d6b39c..00000000000 --- a/src/renderer/src/store/terminals/terminal-workspace-routing.scale.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { AppState } from '../types' -import { - getRemoteConnectionIdForWorktree, - worktreeUsesRemoteConnection, - worktreeUsesWslPath -} from './terminal-workspace-routing' -import { rightSidebarShowsPullRequestData } from '@/lib/right-sidebar-visibility' - -const REPO_COUNT = 10 -const WORKTREE_COUNT = 400 - -/** Counts every `id` read so a rescan shows up as a multiple of the row count. */ -function buildCountingState(): { - state: AppState - reads: () => number - worktreeId: string -} { - let idReads = 0 - const repos = Array.from({ length: REPO_COUNT }, (_, index) => ({ - id: `repo-${index}`, - name: `repo-${index}`, - path: `/repos/repo-${index}`, - connectionId: null - })) - const worktreesByRepo: Record = {} - const perRepo = WORKTREE_COUNT / REPO_COUNT - for (let repoIndex = 0; repoIndex < REPO_COUNT; repoIndex++) { - worktreesByRepo[`repo-${repoIndex}`] = Array.from({ length: perRepo }, (_, index) => { - const id = `repo-${repoIndex}::/repos/repo-${repoIndex}/wt-${index}` - return { - get id() { - idReads++ - return id - }, - repoId: `repo-${repoIndex}`, - path: `/repos/repo-${repoIndex}/wt-${index}`, - branch: `branch-${index}`, - hostId: 'local' - } - }) - } - return { - state: { - repos, - worktreesByRepo, - folderWorkspaces: [], - projectGroups: [], - activeView: 'worktrees', - activeWorktreeId: 'repo-9::/repos/repo-9/wt-39', - rightSidebarOpen: true, - rightSidebarTab: 'checks' - } as unknown as AppState, - reads: () => idReads, - worktreeId: 'repo-9::/repos/repo-9/wt-39' - } -} - -describe('terminal workspace routing scales with tab count, not workspace count', () => { - it('answers repeated owner lookups without rescanning every worktree', () => { - const { state, reads, worktreeId } = buildCountingState() - const CALLS = 200 - - for (let call = 0; call < CALLS; call++) { - worktreeUsesRemoteConnection(state, worktreeId) - getRemoteConnectionIdForWorktree(state, worktreeId) - worktreeUsesWslPath(state, worktreeId) - rightSidebarShowsPullRequestData(state) - } - - // One index build plus O(1) hits; rescanning costs CALLS × WORKTREE_COUNT ID reads. - expect(reads()).toBeLessThanOrEqual(WORKTREE_COUNT * 2) - expect(reads()).toBeLessThan(CALLS * WORKTREE_COUNT) - }) -}) diff --git a/src/renderer/src/store/terminals/workspace-terminal-placeholders.test.ts b/src/renderer/src/store/terminals/workspace-terminal-placeholders.test.ts index b95c766b2aa..a6090d4be2b 100644 --- a/src/renderer/src/store/terminals/workspace-terminal-placeholders.test.ts +++ b/src/renderer/src/store/terminals/workspace-terminal-placeholders.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' import type { Repo } from '../../../../shared/repo-types' -import type { Worktree } from '../../../../shared/worktree/types' + import { DEFAULT_REPO_BADGE_COLOR } from '../../../../shared/constants' import { buildRuntimeSessionPlaceholders } from './workspace-terminal-placeholders' import { addHydratedSshWorktreePlaceholders } from './workspace-terminal-ssh-placeholders' @@ -16,131 +16,10 @@ const repo: Repo = { executionHostId: 'local' } -const worktree: Worktree = { - id: 'repo-1::/repos/one', - repoId: 'repo-1', - hostId: 'local', - displayName: 'main', - comment: '', - linkedIssue: null, - linkedPR: null, - linkedLinearIssue: null, - linkedGitLabMR: null, - linkedGitLabIssue: null, - isArchived: false, - isUnread: false, - isPinned: false, - sortOrder: 0, - lastActivityAt: 0, - path: '/repos/one', - head: '', - branch: '', - isBare: false, - isMainWorktree: true -} - -describe('buildRuntimeSessionPlaceholders', () => { - it('returns the original repos array when no placeholder repo is needed', () => { - const repos = [repo] - const worktreesByRepo = { 'repo-1': [worktree] } - - const result = buildRuntimeSessionPlaceholders({ - repos, - runtimeHostIdByWorkspaceSessionKey: {}, - worktreesByRepo - }) - - // Hydration writes these straight to the store; a fresh array would rerender - // every component selecting the whole repo list for no data change. - expect(result.repos).toBe(repos) - expect(result.worktreesByRepo).toBe(worktreesByRepo) - }) - - it('keeps the original repos array when the session only references known repos', () => { - const repos = [repo] - - const result = buildRuntimeSessionPlaceholders({ - repos, - runtimeHostIdByWorkspaceSessionKey: { 'repo-1::/repos/one': 'runtime:host-1' }, - worktreesByRepo: { 'repo-1': [worktree] } - }) - - expect(result.repos).toBe(repos) - }) - - it('still appends a placeholder repo for an unknown runtime workspace', () => { - const repos = [repo] - - const result = buildRuntimeSessionPlaceholders({ - repos, - runtimeHostIdByWorkspaceSessionKey: { 'repo-2::/repos/two': 'runtime:host-1' }, - worktreesByRepo: { 'repo-1': [worktree] } - }) - - expect(result.repos).not.toBe(repos) - expect(result.repos.map((entry) => entry.id)).toEqual(['repo-1', 'repo-2']) - expect(repos).toHaveLength(1) - }) -}) - describe('addHydratedSshWorktreePlaceholders', () => { - it('returns the original map when no SSH placeholder is needed', () => { - const worktreesByRepo = { 'repo-1': [worktree] } - - const result = addHydratedSshWorktreePlaceholders([repo], worktreesByRepo, { - 'repo-1::/repos/one': [] - }) - - expect(result).toBe(worktreesByRepo) - }) - - it('returns the original map when the SSH worktree is already present', () => { - const sshRepo: Repo = { ...repo, id: 'ssh-repo', connectionId: 'ssh-1' } - const sshWorktree: Worktree = { ...worktree, id: 'ssh-repo::/repos/ssh', repoId: 'ssh-repo' } - const worktreesByRepo = { 'ssh-repo': [sshWorktree] } - - const result = addHydratedSshWorktreePlaceholders([sshRepo], worktreesByRepo, { - 'ssh-repo::/repos/ssh': [] - }) - - expect(result).toBe(worktreesByRepo) - }) - - it('still adds a placeholder for an SSH worktree with no row, without mutating the caller', () => { - const sshRepo: Repo = { ...repo, id: 'ssh-repo', connectionId: 'ssh-1' } - const worktreesByRepo = { 'ssh-repo': [] as Worktree[] } - - const result = addHydratedSshWorktreePlaceholders([sshRepo], worktreesByRepo, { - 'ssh-repo::/repos/ssh': [] - }) - - expect(result).not.toBe(worktreesByRepo) - expect(result['ssh-repo'].map((entry) => entry.id)).toEqual(['ssh-repo::/repos/ssh']) - expect(worktreesByRepo['ssh-repo']).toHaveLength(0) - }) - const placeholderFor = (repos: Repo[]) => addHydratedSshWorktreePlaceholders(repos, {}, { 'ssh-repo::/repos/ssh': [] })['ssh-repo'][0] - it('stamps a direct SSH placeholder with its target, as a listing would', () => { - const sshRepo: Repo = { ...repo, id: 'ssh-repo', connectionId: 'box', executionHostId: null } - expect(placeholderFor([sshRepo])).toMatchObject({ hostId: 'ssh:box' }) - expect(placeholderFor([sshRepo]).runtimeOwnerEnvironmentId).toBeUndefined() - }) - - it('stamps a server-nested SSH placeholder with both the target and the server', () => { - const hubRepo: Repo = { - ...repo, - id: 'ssh-repo', - connectionId: 'box', - executionHostId: 'runtime:hub' - } - expect(placeholderFor([hubRepo])).toMatchObject({ - hostId: 'ssh:box', - runtimeOwnerEnvironmentId: 'hub' - }) - }) - it('routes a restored, selected server-nested SSH tab through its server, not a direct dial', () => { const hubRepo: Repo = { ...repo, @@ -165,14 +44,6 @@ describe('addHydratedSshWorktreePlaceholders', () => { owner: { endpoint: { kind: 'environment', environmentId: 'hub' }, at: 'ssh:box' } }) }) - - it('leaves the placeholder unstamped when the repo id lives on several hosts', () => { - const local: Repo = { ...repo, id: 'ssh-repo', connectionId: null, executionHostId: 'local' } - const hub: Repo = { ...local, connectionId: 'box', executionHostId: 'runtime:hub' } - const placeholder = placeholderFor([local, hub]) - expect(placeholder.hostId).toBeUndefined() - expect(placeholder.runtimeOwnerEnvironmentId).toBeUndefined() - }) }) describe('buildRuntimeSessionPlaceholders for a server repo that nests SSH', () => { diff --git a/src/renderer/src/store/workspace-layout-cache.test.ts b/src/renderer/src/store/workspace-layout-cache.test.ts index 44b089ef354..d80f74d0d58 100644 --- a/src/renderer/src/store/workspace-layout-cache.test.ts +++ b/src/renderer/src/store/workspace-layout-cache.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' import type { PublishedWorkspaceLayout } from '../../../shared/workspace-layout/workspace-layout-published' -import { applyLayoutFrame, EMPTY_WORKSPACE_LAYOUT_CACHE } from './workspace-layout-cache' +import { applyLayoutFrame } from './workspace-layout-cache' function layout(worktreeId: string, tabIds: string[] = []): PublishedWorkspaceLayout { return { @@ -13,31 +13,6 @@ function layout(worktreeId: string, tabIds: string[] = []): PublishedWorkspaceLa } describe('applyLayoutFrame', () => { - it('a workspace frame replaces one workspace and leaves the rest untouched', () => { - const b = layout('b') - const cache = { a: layout('a'), b } - const next = applyLayoutFrame(cache, { - type: 'workspace', - key: 'a', - layout: layout('a', ['t1']) - }) - expect(next).toEqual({ a: layout('a', ['t1']), b }) - expect(next.b).toBe(b) - expect(cache).toEqual({ a: layout('a'), b }) - expect( - applyLayoutFrame(EMPTY_WORKSPACE_LAYOUT_CACHE, { type: 'workspace', key: 'c', layout: b }) - ).toEqual({ c: b }) - }) - - it('a removed frame drops one workspace; an absent one keeps the cache', () => { - const b = layout('b') - const cache = { a: layout('a'), b } - const next = applyLayoutFrame(cache, { type: 'removed', key: 'a' }) - expect(next).toEqual({ b }) - expect(next.b).toBe(b) - expect(applyLayoutFrame(next, { type: 'removed', key: 'a' })).toBe(next) - }) - it('a snapshot replaces the whole cache: absent workspaces go, nothing is merged', () => { const b = layout('b') const cache = { a: layout('a', ['t1', 't2']), b, gone: layout('gone') } @@ -53,22 +28,4 @@ describe('applyLayoutFrame', () => { // An equal entry keeps its object, so its readers do not re-render. expect(next.b).toBe(b) }) - - it('a snapshot equal by value keeps the cache object', () => { - const cache = { a: layout('a', ['t1']), b: layout('b') } - const equal = applyLayoutFrame(cache, { - type: 'snapshot', - workspaces: [ - { key: 'a', layout: layout('a', ['t1']) }, - { key: 'b', layout: layout('b') } - ] - }) - expect(equal).toBe(cache) - const subset = applyLayoutFrame(cache, { - type: 'snapshot', - workspaces: [{ key: 'a', layout: layout('a', ['t1']) }] - }) - expect(subset).toEqual({ a: cache.a }) - expect(subset.a).toBe(cache.a) - }) }) diff --git a/src/renderer/src/store/workspace-layout-frame-store-patch.test.ts b/src/renderer/src/store/workspace-layout-frame-store-patch.test.ts index e23b72a2ccc..af135b9c3d0 100644 --- a/src/renderer/src/store/workspace-layout-frame-store-patch.test.ts +++ b/src/renderer/src/store/workspace-layout-frame-store-patch.test.ts @@ -82,14 +82,6 @@ function withoutTab(frame: PublishedWorkspaceLayout, tabId: string): PublishedWo } describe('laying frame slices over the store', () => { - it('drops a terminal tab main closed, with its pane layout', async () => { - const { state, session, frame } = await restored() - const patch = patchFor(state, session, withoutTab(frame, 'tab-shell')) - expect(patch.tabsByWorktree?.[GIT_KEY]?.map((tab) => tab.id)).not.toContain('tab-shell') - expect(patch.terminalLayoutsByTabId).not.toHaveProperty('tab-shell') - expect(patch.unifiedTabsByWorktree?.[GIT_KEY]?.map((tab) => tab.id)).not.toContain('tab-shell') - }) - it('keeps the pane layout of a tab the window’s own pane command holds, until main drops the tab', async () => { const { state, session, frame } = await restored() const newRatios: PublishedWorkspaceLayout = { @@ -109,33 +101,6 @@ describe('laying frame slices over the store', () => { ).not.toHaveProperty('tab-shell') }) - it('keeps an open file’s editor state and place, and drops a file main closed', async () => { - const { state, session, frame } = await restored() - const file = state.openFiles.find((entry) => entry.worktreeId === GIT_KEY)! - const edited = { ...state, openFiles: [{ ...file, isDirty: true }] } - expect(patchFor(edited, session, frame).openFiles).toBeUndefined() - const closed = patchFor(edited, session, { - ...withoutTab(frame, 'editor:src/app.ts'), - editorFiles: [] - }) - expect(closed.openFiles).toEqual([]) - }) - - it('keeps a browser tab’s page state, and drops the pages of a tab main closed', async () => { - const { state, session, frame } = await restored() - const browser = state.browserTabsByWorktree[GIT_KEY]![0]! - const browsing = { - ...state, - browserTabsByWorktree: { [GIT_KEY]: [{ ...browser, url: 'https://example.com/next' }] } - } - expect(patchFor(browsing, session, frame).browserTabsByWorktree).toBeUndefined() - const closed = patchFor(browsing, session, { - ...withoutTab(frame, 'browser-1'), - browserTabs: [] - }) - expect(closed.browserPagesByWorkspace).not.toHaveProperty('browser-1') - }) - it('keeps a client-hosted browser tab this desktop created, with its page and handle, at its place (L274)', async () => { const { state, session, frame } = await restored() const page = state.browserPagesByWorkspace['browser-1']![0]! diff --git a/src/renderer/src/store/workspace-record-selectors.test.ts b/src/renderer/src/store/workspace-record-selectors.test.ts index 8c5ca10ea33..01999076337 100644 --- a/src/renderer/src/store/workspace-record-selectors.test.ts +++ b/src/renderer/src/store/workspace-record-selectors.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' import { hostWorktreeTarget } from '../../../shared/worktree/host-qualified-identity' -import { folderWorkspaceKey } from '../../../shared/workspace-scope' +import '../../../shared/workspace-scope' import type { AppState } from './types' import { makeFolderWorkspace, makeWorktree } from './slices/worktrees-slice-test-fixtures' import { selectWorkspaceByHostTarget } from './workspace-record-selectors' @@ -51,25 +51,4 @@ describe('selectWorkspaceByHostTarget', () => { ) ).toBe(hubRow) }) - - it('matches a folder workspace on its host only', () => { - const state = makeState() - const key = folderWorkspaceKey('folder-1') - expect( - selectWorkspaceByHostTarget(state, hostWorktreeTarget({ id: key, hostId: 'local' }))?.path - ).toBe(folder.folderPath) - expect( - selectWorkspaceByHostTarget(state, hostWorktreeTarget({ id: key, hostId: 'ssh:build-box' })) - ).toBe(undefined) - }) -}) - -describe('selectWorkspaceByHostTarget bucket keys', () => { - it('finds a row filed under a bucket that is not its repo id', () => { - const row = makeWorktree({ id: 'wt-9', repoId: 'repo-9', hostId: 'ssh:remote' }) - const state = makeState({ worktreesByRepo: { remote: [row] } }) - expect( - selectWorkspaceByHostTarget(state, hostWorktreeTarget({ id: 'wt-9', hostId: 'ssh:remote' })) - ).toBe(row) - }) }) diff --git a/src/renderer/src/store/worktree-diff-comments-selector.test.ts b/src/renderer/src/store/worktree-diff-comments-selector.test.ts index 75421dd350c..93a84d56db8 100644 --- a/src/renderer/src/store/worktree-diff-comments-selector.test.ts +++ b/src/renderer/src/store/worktree-diff-comments-selector.test.ts @@ -3,10 +3,7 @@ import type { DiffComment } from '../../../shared/diff-comment-types' import type { AppState } from './types' import { makeWorktree } from './slices/worktrees-slice-test-fixtures' -import { - selectWorktreeDiffComments, - selectWorktreeDiffCommentsOrEmpty -} from './worktree-diff-comments-selector' +import './worktree-diff-comments-selector' import { createDiffCommentViewSelector, sourceParts } from './diff-comment-owner' const TARGET = 'repo-9::/w/target' @@ -94,23 +91,3 @@ describe('note owner view selectors', () => { expect(large.catalogWriteReads).toBeLessThanOrEqual(8 * 1000 + 9 * 1000) }) }) - -describe('selectWorktreeDiffComments', () => { - it('reads a replacement worktree snapshot and handles an absent id', () => { - const firstComments = [makeComment('comment-1')] - const nextComments = [makeComment('comment-2')] - const first = { - 'repo-9': [makeWorktree({ id: TARGET, repoId: 'repo-9', diffComments: firstComments })] - } - const next = { - 'repo-9': [makeWorktree({ id: TARGET, repoId: 'repo-9', diffComments: nextComments })] - } - - expect(selectWorktreeDiffComments({ worktreesByRepo: first }, TARGET)).toBe(firstComments) - expect(selectWorktreeDiffComments({ worktreesByRepo: next }, TARGET)).toBe(nextComments) - expect(selectWorktreeDiffComments({ worktreesByRepo: next }, null)).toBeUndefined() - expect(selectWorktreeDiffCommentsOrEmpty({ worktreesByRepo: next }, null)).toBe( - selectWorktreeDiffCommentsOrEmpty({ worktreesByRepo: next }, undefined) - ) - }) -}) diff --git a/src/renderer/src/store/worktree-host-collision-index.test.ts b/src/renderer/src/store/worktree-host-collision-index.test.ts index fbc2ea90c96..30c93476877 100644 --- a/src/renderer/src/store/worktree-host-collision-index.test.ts +++ b/src/renderer/src/store/worktree-host-collision-index.test.ts @@ -50,14 +50,6 @@ describe('id-keyed worktree projections keep distinct hosts distinct', () => { // Last wins: fetchWorktrees replaces, so the later entry is the current one. expect(all[0]?.head).toBe('fresh') }) - - it('treats an unqualified row as its own bucket rather than inventing a host', () => { - const unqualified: Worktree = { ...baseWorktree, displayName: 'unqualified orca' } - - const all = getIndexedAllWorktrees(byRepo(unqualified, sshRow)) - - expect(all).toHaveLength(2) - }) }) describe('getWorktreeOnHostFromState refuses a lookup that still matches two copies', () => { diff --git a/src/renderer/src/store/worktree-visibility-defaults-by-host.test.ts b/src/renderer/src/store/worktree-visibility-defaults-by-host.test.ts deleted file mode 100644 index 790bbc6e1fd..00000000000 --- a/src/renderer/src/store/worktree-visibility-defaults-by-host.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { getRepoOwnerWorktreeVisibilityDefaults } from './worktree-visibility-defaults-by-host' - -describe('getRepoOwnerWorktreeVisibilityDefaults', () => { - it('resolves defaults from each repository owner', () => { - const settings = { worktreeVisibilityDefaults: { external: 'hide' as const } } - const defaultsByHost = { - local: { external: 'hide' as const }, - 'runtime:show-host': { external: 'show' as const }, - 'runtime:legacy-host': null - } - - expect( - getRepoOwnerWorktreeVisibilityDefaults( - { executionHostId: 'runtime:show-host' }, - settings, - defaultsByHost - ) - ).toEqual({ external: 'show' }) - expect( - getRepoOwnerWorktreeVisibilityDefaults({ connectionId: 'ssh-box' }, settings, defaultsByHost) - ).toEqual({ external: 'hide' }) - expect( - getRepoOwnerWorktreeVisibilityDefaults( - { executionHostId: 'runtime:legacy-host' }, - settings, - defaultsByHost - ) - ).toBeUndefined() - }) -})