diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index 047be93dc5e..a6f6d8e38f5 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -27,7 +27,6 @@ 1 test-no-in-test-import-actual:src/main/local-worktree-removal-recovery.test.ts 2 test-no-in-test-import-actual:src/main/managed-server/activation/orcad-activation-crash-recovery.test.ts 1 test-no-in-test-import-actual:src/main/managed-server/host-state/orcad-state-mutation-timeout.test.ts -1 test-no-in-test-import-actual:src/main/node-runtime/managed-runtime-installation.test.ts 1 test-no-in-test-import-actual:src/main/node-runtime/pinned-runtime-materializer.test.ts 1 test-no-in-test-import-actual:src/main/omp/omp-session-cwd-lookup.test.ts 6 test-no-in-test-import-actual:src/main/opencode/hook-service-legacy-acl.test.ts @@ -340,7 +339,6 @@ 1 test-no-real-time-wait:src/main/ipc/pty/pty-agent-launch-pane-attach.test.ts 1 test-no-real-time-wait:src/main/ipc/pty/pty-daemon-controller-teardown.test.ts 1 test-no-real-time-wait:src/main/ipc/pty/pty-launch-parity.test.ts -2 test-no-real-time-wait:src/main/ipc/pty/pty-spawn-cwd-fallback-hung-folder.test.ts 1 test-no-real-time-wait:src/main/ipc/rebinding-file-watch-noise.test.ts 1 test-no-real-time-wait:src/main/ipc/rebinding-file-watch.test.ts 3 test-no-real-time-wait:src/main/ipc/runtime-environments/runtime-environment-preference.test.ts @@ -382,7 +380,6 @@ 1 test-no-real-time-wait:src/main/native-chat/transcript-watch-error.test.ts 2 test-no-real-time-wait:src/main/native-chat/transcript-watch-liveness.test.ts 11 test-no-real-time-wait:src/main/native-chat/transcript-watch.test.ts -1 test-no-real-time-wait:src/main/node-runtime/managed-runtime-installation.test.ts 1 test-no-real-time-wait:src/main/opencode/hook-plugin-legacy-tui-ownership.test.ts 6 test-no-real-time-wait:src/main/opencode/hook-plugin-module-contract.test.ts 1 test-no-real-time-wait:src/main/opencode/hook-plugin-opencode2-tui-ownership.test.ts @@ -497,7 +494,6 @@ 1 test-no-real-time-wait:src/renderer/src/components/native-chat/use-structured-agent-session-legacy-recovery.test.tsx 1 test-no-real-time-wait:src/renderer/src/components/native-chat/use-structured-agent-session-options.learned-default.test.tsx 1 test-no-real-time-wait:src/renderer/src/components/native-chat/use-structured-agent-session-read.test.tsx -1 test-no-real-time-wait:src/renderer/src/components/repo/CreateFromPicker.test.tsx 1 test-no-real-time-wait:src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-generation.test.tsx 1 test-no-real-time-wait:src/renderer/src/components/right-sidebar/file-explorer-watch-refresh-scheduler.test.ts 1 test-no-real-time-wait:src/renderer/src/components/right-sidebar/parent-pr-checks-refresh.test.ts @@ -1107,7 +1103,6 @@ 5 test-no-wall-clock:src/renderer/src/components/status-bar/inline-usage-bars.test.tsx 4 test-no-wall-clock:src/renderer/src/components/status-bar/provider-segment-balance.test.tsx 2 test-no-wall-clock:src/renderer/src/components/status-bar/status-bar-antigravity-segment.test.tsx -3 test-no-wall-clock:src/renderer/src/components/status-bar/status-bar-provider-menu-focus.test.tsx 1 test-no-wall-clock:src/renderer/src/components/status-bar/workspace-space-worktree-row.test.tsx 1 test-no-wall-clock:src/renderer/src/components/tab-bar/TabBarCreateEntry.history.test.tsx 2 test-no-wall-clock:src/renderer/src/components/tab-bar/cli-chat-resume-run.test.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index ea07e87a157..ea5ffcde281 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -9051,17 +9051,16 @@ "providers": ["local", "daemon", "ssh", "wsl", "remote-runtime", "mobile-relay"], "coveredPlatforms": ["macos"], "coveredProviders": ["local", "daemon", "ssh"], - "coverageNotes": "Local macOS deterministic evidence covers the renderer snapshot/coordinator, exact local and SSH-shaped PTY request settlement, active-last and pinned terminal-tab routing, component-unmount continuation, and the existing current/legacy daemon management contract. Windows Electron process absence, live SSH/WSL behavior, and remote-runtime/mobile host completion remain explicit gaps.", + "coverageNotes": "Local macOS deterministic evidence covers the renderer snapshot/coordinator, exact local and SSH-shaped PTY request settlement, active-last and pinned terminal-tab routing, component-unmount continuation, and the existing current/legacy daemon management contract. Windows Electron process absence, live SSH/WSL behavior, and remote-runtime/mobile host completion remain explicit gaps. The audit removed the isolated terminal-tab close routing suite. Forced-pin close-call shape, editor-before-browser fallback, browser fallback, empty-worktree deactivation and no replacement-terminal routing lose their supplied-store spy assertions. Remaining lifecycle/coordinator suites are not claimed to reproduce those isolated routing alarms.", "motivatingLinks": ["https://github.com/stablyai/orca/issues/8001"], "invariant": "Every terminal surface confirmed in the invoking renderer is force-closed exactly once after daemon management settles, later-created surfaces and non-terminal tabs survive, and exact shutdown requests are limited to deduplicated current non-runtime PTY bindings of the confirmed surfaces.", "oracle": "Snapshot terminal entity IDs before the first await; mutate ownership, active selection, bindings, and tab presence while daemon management is pending and between bounded close batches; then assert only the immutable targets disappear from both terminal stores, active targets close last with valid editor/browser/deactivated post-state, every captured exact PTY promise settles before callbacks, and no provider inventory sweep or late-tab kill occurs.", "commands": [ - "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/shared/kill-all-terminal-surfaces.test.ts src/renderer/src/components/shared/useDaemonActions.test.tsx src/renderer/src/components/terminal/terminal-tab-actions-kill-all.test.ts src/main/ipc/pty/pty-management.test.ts" + "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/shared/kill-all-terminal-surfaces.test.ts src/renderer/src/components/shared/useDaemonActions.test.tsx src/main/ipc/pty/pty-management.test.ts" ], "testFiles": [ "src/renderer/src/components/shared/kill-all-terminal-surfaces.test.ts", "src/renderer/src/components/shared/useDaemonActions.test.tsx", - "src/renderer/src/components/terminal/terminal-tab-actions-kill-all.test.ts", "src/main/ipc/pty/pty-management.test.ts" ], "assertionRefs": [ @@ -9085,13 +9084,6 @@ "closed terminal tabs report success instead of the no-sessions informational state when daemon management reports zero" ] }, - { - "file": "src/renderer/src/components/terminal/terminal-tab-actions-kill-all.test.ts", - "assertions": [ - "force closes pinned terminals without a second confirmation", - "closing the last active terminal preserves and activates editor or browser content, otherwise deactivates without auto-spawn" - ] - }, { "file": "src/main/ipc/pty/pty-management.test.ts", "assertions": [ @@ -9107,10 +9099,10 @@ "runner": "local", "platform": "macos", "command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/shared/kill-all-terminal-surfaces.test.ts src/renderer/src/components/shared/useDaemonActions.test.tsx src/renderer/src/components/terminal/terminal-tab-actions-kill-all.test.ts src/main/ipc/pty-management.test.ts", - "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/shared/kill-all-terminal-surfaces.test.ts src/renderer/src/components/shared/useDaemonActions.test.tsx src/renderer/src/components/terminal/terminal-tab-actions-kill-all.test.ts src/main/ipc/pty/pty-management.test.ts", + "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/shared/kill-all-terminal-surfaces.test.ts src/renderer/src/components/shared/useDaemonActions.test.tsx src/main/ipc/pty/pty-management.test.ts", "result": "passed", "durationSeconds": 1.18, - "summary": "4 test files and 27 tests passed locally, including the existing daemon handler contract and a real 100-terminal Zustand cleanup fixture." + "summary": "4 test files and 27 tests passed locally, including the existing daemon handler contract and a real 100-terminal Zustand cleanup fixture. Historical four-file record only: its date, result, duration and 27-test count describe the original command. currentCommand maps the three surviving roots; that three-file invocation was not rerun here, and this record does not restore the removed routing alarms." } ], "runtimeBudget": { @@ -9139,7 +9131,8 @@ "Windows Electron screenshots and live process/xterm absence evidence were not produced by this local macOS run.", "Live SSH and WSL process absence, Linux local/daemon behavior, and mobile/relay shutdown remain unproved.", "Runtime-host terminal close is best-effort because closeTerminalTab still discards the existing async host result and its close-intent lifetime is shorter than the possible RPC flow.", - "Daemon adapter listing failures remain suppressed by the existing management API, so reported daemon counts are not authoritative verification of every process." + "Daemon adapter listing failures remain suppressed by the existing management API, so reported daemon counts are not authoritative verification of every process.", + "The audit removed the isolated terminal-tab close routing suite. Forced-pin close-call shape, editor-before-browser fallback, browser fallback, empty-worktree deactivation and no replacement-terminal routing lose their supplied-store spy assertions. Remaining lifecycle/coordinator suites are not claimed to reproduce those isolated routing alarms." ], "demotionRule": "Keep experimental or demote to protection none if the gate flakes, permits a late-created tab or unrelated PTY to close, duplicates provider shutdown, or performs more than one ownership replan per bounded yield." }, diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index 4d25a993120..c47297c661f 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -2654,7 +2654,6 @@ "src/main/github/work-item-search-semantics.test.ts": 473, "src/main/gitlab/client-mr-auth-rate-limit.test.ts": 355, "src/main/gitlab/client-mr-branch-lookup.test.ts": 435, - "src/main/gitlab/client-mr-job-ci.test.ts": 420, "src/main/gitlab/client-mr-listing.test.ts": 388, "src/main/gitlab/client-mr-review-actions.test.ts": 404, "src/main/gitlab/client-work-items.test.ts": 539, @@ -6170,7 +6169,6 @@ "src/main/ssh/orcad-retained-fence-takeover.test.ts": 211, "src/main/ssh/orcad-retained-source-changes.test.ts": 1858, "src/main/ssh/orcad-retained-source-lists.test.ts": 566, - "src/main/ssh/orcad-running-version-adoption.test.ts": 564, "src/main/ssh/orcad-runtime-maintenance.test.ts": 595, "src/main/ssh/orcad-server-project-groups.test.ts": 18, "src/main/ssh/orcad-snapshot-room.test.ts": 312, @@ -7054,9 +7052,7 @@ "src/renderer/src/components/contextual-tours/contextual-tour-floating-position.test.ts": 2004, "src/renderer/src/components/contextual-tours/contextual-tour-gate.test.ts": 47, "src/renderer/src/components/contextual-tours/contextual-tour-overlay-measurement.test.ts": 258, - "src/renderer/src/components/contextual-tours/contextual-tour-step-actions.test.ts": 55, "src/renderer/src/components/contextual-tours/request-contextual-tour-when-ready.test.ts": 1380, - "src/renderer/src/components/contextual-tours/workspace-creation-tour-handoff.test.ts": 1390, "src/renderer/src/components/crash-report/crash-report-submit-notice.test.ts": 124, "src/renderer/src/components/crash-report/use-crash-report-copy.test.tsx": 358, "src/renderer/src/components/dashboard-popout/AgentKanbanBoard.test.tsx": 740, @@ -8014,7 +8010,6 @@ "src/renderer/src/components/quick-open-recent-validation.test.ts": 152, "src/renderer/src/components/quick-open-search.test.ts": 163, "src/renderer/src/components/quick-open-selection-owner.react.test.tsx": 1686, - "src/renderer/src/components/repo/CreateFromPicker.test.tsx": 642, "src/renderer/src/components/repo/NestedRepoChecklist.test.tsx": 182, "src/renderer/src/components/repo/NestedRepoScanLimitNotice.test.ts": 192, "src/renderer/src/components/repo/repo-icon.test.tsx": 303, @@ -8774,7 +8769,6 @@ "src/renderer/src/components/sidebar/worktree-list/rows/host-header-detail.test.ts": 128, "src/renderer/src/components/sidebar/worktree-list/rows/option-dom-host-collision.test.ts": 125, "src/renderer/src/components/sidebar/worktree-list/rows/project-header-host-context.test.ts": 103, - "src/renderer/src/components/sidebar/worktree-list/rows/use-project-group-dialogs-owner-host.test.tsx": 732, "src/renderer/src/components/sidebar/worktree-list/viewport/hard-scroll-up.test.ts": 48, "src/renderer/src/components/sidebar/worktree-list/viewport/scroll-adjustment.test.ts": 1810, "src/renderer/src/components/sidebar/worktree-list/viewport/sticky-headers.test.ts": 195, @@ -8876,7 +8870,6 @@ "src/renderer/src/components/status-bar/status-bar-copy-localization.test.tsx": 885, "src/renderer/src/components/status-bar/status-bar-density.test.ts": 57, "src/renderer/src/components/status-bar/status-bar-pool-countdown.test.tsx": 389, - "src/renderer/src/components/status-bar/status-bar-provider-menu-focus.test.tsx": 941, "src/renderer/src/components/status-bar/status-bar-provider-visibility.test.ts": 42, "src/renderer/src/components/status-bar/status-bar-runtime-groups.test.ts": 237, "src/renderer/src/components/status-bar/status-bar-usage-collapse.test.ts": 159, @@ -9599,7 +9592,6 @@ "src/renderer/src/components/terminal-pane/use-terminal-scroll-visibility-memory.test.ts": 56, "src/renderer/src/components/terminal-pane/use-terminal-tab-cold-parking.test.ts": 461, "src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.test.ts": 333, - "src/renderer/src/components/terminal-pane/useTerminalFontZoom.test.ts": 197, "src/renderer/src/components/terminal-pane/wrapped-terminal-http-link-limits.test.ts": 166, "src/renderer/src/components/terminal-pane/wrapped-terminal-http-link-provider.test.ts": 396, "src/renderer/src/components/terminal-pane/wrapped-terminal-link-ranges.test.ts": 42, @@ -9636,7 +9628,6 @@ "src/renderer/src/components/terminal/terminal-provider-snapshot-bound-pty-ids.test.ts": 1570, "src/renderer/src/components/terminal/terminal-provider-snapshot-capability-resettlement.test.ts": 377, "src/renderer/src/components/terminal/terminal-provider-snapshot-capability.test.ts": 43, - "src/renderer/src/components/terminal/terminal-tab-actions-kill-all.test.ts": 602, "src/renderer/src/components/terminal/terminal-tab-actions-pinned-server-close.test.ts": 269, "src/renderer/src/components/terminal/terminal-tab-actions-unified-close.test.ts": 1809, "src/renderer/src/components/terminal/terminal-tab-actions-unrouted-close.test.ts": 474, @@ -10273,7 +10264,6 @@ "src/renderer/src/lib/pane-manager/terminal-kitty-file-reader.test.ts": 68, "src/renderer/src/lib/pane-manager/terminal-ligatures-addon.test.ts": 38, "src/renderer/src/lib/pane-manager/terminal-link-provider-guard.test.ts": 52, - "src/renderer/src/lib/pane-manager/terminal-linkifier-hover-reset-on-mouseleave.test.ts": 34, "src/renderer/src/lib/pane-manager/terminal-linkifier-hover-reset-on-write.test.ts": 57, "src/renderer/src/lib/pane-manager/terminal-mouse-encoding-tracker.test.ts": 71, "src/renderer/src/lib/pane-manager/terminal-render-pause-release-parked-resize.test.ts": 50, @@ -11384,7 +11374,6 @@ "src/renderer/src/web/preload-api/web-ui-compact-notice.test.ts": 569, "src/renderer/src/web/preload-api/web-ui-explorer-root.test.ts": 614, "src/renderer/src/web/preload-api/web-worktree-attachment-mutations.test.ts": 130, - "src/renderer/src/web/web-clipboard-copy-fallback.test.ts": 34, "src/renderer/src/web/web-clipboard-copy-terminal-selection.test.ts": 152, "src/renderer/src/web/web-clipboard-image-presence.test.ts": 933, "src/renderer/src/web/web-device-key.test.ts": 86, diff --git a/src/main/agent-hooks/wsl-hook-relay-idle-stop.test.ts b/src/main/agent-hooks/wsl-hook-relay-idle-stop.test.ts index ebd2af90926..99af22dfaf4 100644 --- a/src/main/agent-hooks/wsl-hook-relay-idle-stop.test.ts +++ b/src/main/agent-hooks/wsl-hook-relay-idle-stop.test.ts @@ -107,41 +107,4 @@ describe('the WSL hook relay idle stop', () => { await vi.advanceTimersByTimeAsync(1_000) expect(states.has('ubuntu')).toBe(false) }) - - it('brings a stopped relay back with its config, and a quit forgets it', async () => { - const state = running() - states.set('ubuntu', state) - idle.update(state) - await vi.advanceTimersByTimeAsync(1_000) - - idle.restoreParked('Ubuntu') - expect(states.get('ubuntu')).toMatchObject({ - phase: 'failed', - cooldownUntil: 0, - codexHomePath: '/home/u/.codex' - }) - - states.clear() - const again = running() - states.set('ubuntu', again) - idle.update(again) - await vi.advanceTimersByTimeAsync(1_000) - idle.dispose(true) - idle.restoreParked('Ubuntu') - expect(states.has('ubuntu')).toBe(false) - }) - - it('never stops a relay that is not running, or one a newer state replaced', async () => { - const starting = { ...running(), phase: 'starting' as const } - states.set('ubuntu', starting) - idle.update(starting) - await vi.advanceTimersByTimeAsync(5_000) - expect(states.get('ubuntu')).toBe(starting) - - const old = running() - idle.update(old) - await vi.advanceTimersByTimeAsync(1_000) - expect(states.get('ubuntu')).toBe(starting) - expect(old.child?.kill).not.toHaveBeenCalled() - }) }) diff --git a/src/main/browser/browser-network-tunnel-session-stream-scoped-failure.test.ts b/src/main/browser/browser-network-tunnel-session-stream-scoped-failure.test.ts index ba01fced18e..bd917c29b44 100644 --- a/src/main/browser/browser-network-tunnel-session-stream-scoped-failure.test.ts +++ b/src/main/browser/browser-network-tunnel-session-stream-scoped-failure.test.ts @@ -98,19 +98,6 @@ describe('BrowserNetworkTunnelSession stream-scoped failures', () => { expect(tunnel.sockets[0]!.destroyed).toBe(false) }) - it('fails only the pending stream when the destination ends before connecting', async () => { - const tunnel = createPairedTunnel() - const healthy = await openConnectedStream(tunnel, 'healthy.internal') - - const pending = tunnel.client.open({ host: 'never-connects.internal', port: 443 }) - tunnel.sockets[1]!.emit('end') - - await expect(pending).rejects.toThrow('destination_closed_before_connect') - expect(tunnel.closures).toEqual([]) - expect(healthy.destroyed).toBe(false) - expect(tunnel.sockets[0]!.destroyed).toBe(false) - }) - it('fails only the receiving stream when destination bytes exhaust the retained budget', async () => { const tunnel = createPairedTunnel(() => null) const healthy = await openConnectedStream(tunnel, 'healthy.internal') diff --git a/src/main/browser/extensions/browser-extension-tab-model.test.ts b/src/main/browser/extensions/browser-extension-tab-model.test.ts index f52de442a3a..e8a0edaa351 100644 --- a/src/main/browser/extensions/browser-extension-tab-model.test.ts +++ b/src/main/browser/extensions/browser-extension-tab-model.test.ts @@ -44,8 +44,7 @@ vi.mock('./browser-extension-window-model', () => ({ getBrowserExtensionMainWindow: () => ({ id: 1 }) })) -const { openBrowserExtensionTab, recentWorktreeIdForBrowserProfile, recordActiveBrowserPage } = - await import('./browser-extension-tab-model') +const { openBrowserExtensionTab } = await import('./browser-extension-tab-model') const { watchBrowserMainWindowReadiness } = await import('../browser-main-window-readiness') /** Orca's main window; its renderer reports readiness on the per-window IPC channel. */ @@ -66,43 +65,6 @@ beforeEach(() => { mainWindow().rendererReady() }) -describe('where an extension tab goes when no workspace is active', () => { - it("prefers the active page's workspace when that page is in the profile", () => { - mocks.pages.set('a', { id: 11, session: mocks.workSession, worktreeId: 'wt-a' }) - mocks.pages.set('b', { id: 12, session: mocks.workSession, worktreeId: 'wt-b' }) - recordActiveBrowserPage('b') - expect(recentWorktreeIdForBrowserProfile('work')).toBe('wt-b') - }) - - it("otherwise uses any open tab in the profile, never another profile's workspace", () => { - mocks.pages.set('a', { id: 11, session: mocks.otherSession, worktreeId: 'wt-other' }) - mocks.pages.set('b', { id: 12, session: mocks.workSession, worktreeId: 'wt-work' }) - recordActiveBrowserPage('a') - expect(recentWorktreeIdForBrowserProfile('work')).toBe('wt-work') - mocks.pages.delete('b') - expect(recentWorktreeIdForBrowserProfile('work')).toBeUndefined() - }) - - it('passes that workspace to the renderer as the fallback', async () => { - mocks.pages.set('a', { id: 11, session: mocks.workSession, worktreeId: 'wt-a' }) - mocks.createTab.mockImplementation(async () => { - mocks.pages.set('new', { id: 13, session: mocks.workSession, worktreeId: 'wt-a' }) - return { browserPageId: 'new' } - }) - expect(await openBrowserExtensionTab('work', 'https://welcome.test', true)).toBe(13) - expect(mocks.createTab).toHaveBeenCalledWith( - expect.anything(), - 'https://welcome.test', - undefined, - 'work', - 'persist:work', - true, - undefined, - 'wt-a' - ) - }) -}) - describe('an extension tab opened while Orca starts', () => { afterEach(() => { vi.useRealTimers() @@ -128,14 +90,4 @@ describe('an extension tab opened while Orca starts', () => { window.rendererReady() await expect(reopening).resolves.toBe(21) }) - - it('fails cleanly when the window never becomes ready', async () => { - vi.useFakeTimers() - mainWindow() - const opening = openBrowserExtensionTab('work', 'https://welcome.test', true) - const failed = expect(opening).rejects.toThrow('Orca has no window to open a tab in') - await vi.advanceTimersByTimeAsync(30_000) - await failed - expect(mocks.createTab).not.toHaveBeenCalled() - }) }) diff --git a/src/main/claude-accounts/claude-login-session.test.ts b/src/main/claude-accounts/claude-login-session.test.ts index 50097fb693d..bbb91ad1b5b 100644 --- a/src/main/claude-accounts/claude-login-session.test.ts +++ b/src/main/claude-accounts/claude-login-session.test.ts @@ -1,4 +1,5 @@ -import { existsSync, mkdtempSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs' +import { mkdtempSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs' + import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it, vi } from 'vitest' @@ -67,31 +68,6 @@ describe('runClaudeLoginSession', () => { expect(openWslSignInLink).not.toHaveBeenCalled() }) - it('leaves a local login to open its own browser', async () => { - const config = folder('host') - const openWslSignInLink = vi.fn() - const runCommand = vi.fn(async (_args, _config, _ms, options) => { - expect(options?.browser).toBeUndefined() - expect(existsSync(join(config.windowsPath, '.orca-sign-in-browser'))).toBe(false) - return '' - }) - await runClaudeLoginSession(config, { runCommand, setCancel: vi.fn(), openWslSignInLink }) - expect(runCommand).toHaveBeenCalledTimes(1) - expect(openWslSignInLink).not.toHaveBeenCalled() - }) - - it('changes nothing for a WSL login where nothing can open a link', async () => { - const config = folder('wsl') - const runCommand = vi.fn(async () => '') - await runClaudeLoginSession(config, { runCommand, setCancel: vi.fn() }) - expect(runCommand.mock.calls[0]).toEqual([ - ['auth', 'login', '--claudeai'], - config, - expect.any(Number), - expect.objectContaining({ browser: undefined }) - ]) - }) - it('never opens a link that is not a Claude sign-in, and keeps the login going', async () => { const config = folder('wsl') const openWslSignInLink = vi.fn() @@ -129,19 +105,4 @@ describe('runClaudeLoginSession', () => { ).rejects.toThrow('cancelled') expect(openWslSignInLink).not.toHaveBeenCalled() }) - - it('runs a WSL login unchanged when the stand-in cannot be written', async () => { - const config = { ...folder('wsl'), windowsPath: join(tmpdir(), 'missing-claude-folder', 'x') } - const openWslSignInLink = vi.fn() - const runCommand = vi.fn(async () => '') - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - await runClaudeLoginSession(config, { runCommand, setCancel: vi.fn(), openWslSignInLink }) - warn.mockRestore() - expect(runCommand.mock.calls[0]).toEqual([ - ['auth', 'login', '--claudeai'], - config, - expect.any(Number), - expect.objectContaining({ browser: undefined }) - ]) - }) }) diff --git a/src/main/daemon/daemon-server-attachment-lifecycle.test.ts b/src/main/daemon/daemon-server-attachment-lifecycle.test.ts index 61243f56920..2739bf4ac9b 100644 --- a/src/main/daemon/daemon-server-attachment-lifecycle.test.ts +++ b/src/main/daemon/daemon-server-attachment-lifecycle.test.ts @@ -91,19 +91,6 @@ describe('DaemonServer attachment lifecycle', () => { expect(daemon.attachments.tokenBySessionId.has('transport-owned-session')).toBe(false) }) - it('parks only the requesting client attachment on explicit detach', async () => { - await createSession('explicitly-detached-session') - const daemon = server as unknown as DaemonAttachmentPrivate - const detach = vi.spyOn(daemon.host, 'detach') - - await client.request('detach', { sessionId: 'explicitly-detached-session' }) - - expect(detach).toHaveBeenCalledWith('explicitly-detached-session', expect.any(Symbol)) - expect(daemon.host.sessions.get('explicitly-detached-session')?.hasAttachedClients).toBe(false) - expect(daemon.attachments.clientIdBySessionId.has('explicitly-detached-session')).toBe(false) - expect(daemon.attachments.tokenBySessionId.has('explicitly-detached-session')).toBe(false) - }) - it('parks an attachment completed after its transport already closed', async () => { const daemon = server as unknown as DaemonAttachmentPrivate const originalCreateOrAttach = daemon.host.createOrAttach.bind(daemon.host) diff --git a/src/main/daemon/daemon-wsl-bridge-exit.test.ts b/src/main/daemon/daemon-wsl-bridge-exit.test.ts index d0964d2fd76..c82a3940270 100644 --- a/src/main/daemon/daemon-wsl-bridge-exit.test.ts +++ b/src/main/daemon/daemon-wsl-bridge-exit.test.ts @@ -4,7 +4,7 @@ import { _resetRunningWslDistroCacheForTests, resolveRunningWslDistros } from '../wsl-running-distro-cache' -import { checkWslBridgeDistro, classifyWslBridgeExit } from './daemon-wsl-bridge-exit' +import { checkWslBridgeDistro } from './daemon-wsl-bridge-exit' afterEach(() => { _resetRunningWslDistroCacheForTests() @@ -37,52 +37,6 @@ describe('checkWslBridgeDistro', () => { expect(probe).toHaveBeenCalledOnce() }) - it('matches the distro case-insensitively, and a running distro is not lost', async () => { - await expect( - checkWslBridgeDistro('ubuntu', 0, async () => ({ status: 'confirmed', distros: ['Ubuntu'] })) - ).resolves.toBe('running') - }) - - it('treats a hung check as unanswered, so the exit stays an exit', async () => { - await expect( - checkWslBridgeDistro('Ubuntu', 0, () => new Promise(() => {}), { firstMs: 10, totalMs: 30 }) - ).resolves.toBe('unavailable') - }) - - // 5e hardening (b): a slow or failing first read is asked once more before the exit stays an exit. - it('retries an unanswered read once, past the failure backoff, within the total budget', async () => { - const reads: { bypassFailureBackoff?: boolean }[] = [] - const answers: WslRunningDistrosRead[] = [ - { status: 'unavailable' }, - { status: 'confirmed', distros: [] } - ] - await expect( - checkWslBridgeDistro('Ubuntu', 0, async (options) => { - reads.push({ bypassFailureBackoff: options.bypassFailureBackoff }) - return answers.shift() ?? { status: 'unavailable' } - }) - ).resolves.toBe('lost') - expect(reads).toEqual([{ bypassFailureBackoff: undefined }, { bypassFailureBackoff: true }]) - }) - - it('gives the retry only what is left of the total budget after a hung first read', async () => { - const reads = vi.fn(() => new Promise(() => {})) - const startedAt = performance.now() - await expect( - checkWslBridgeDistro('Ubuntu', 0, reads, { firstMs: 20, totalMs: 60 }) - ).resolves.toBe('unavailable') - expect(reads).toHaveBeenCalledTimes(2) - expect(performance.now() - startedAt).toBeLessThan(500) - }) - - it('treats a failed check as unanswered', async () => { - await expect( - checkWslBridgeDistro('Ubuntu', 0, async () => { - throw new Error('wsl.exe missing') - }) - ).resolves.toBe('unavailable') - }) - it('never trusts a probe already in flight before the exit: one fresh probe follows it', async () => { // A background probe started before the pane's shell booted the distro, so it lacks it. const answers: ((distros: string[]) => void)[] = [] @@ -105,35 +59,3 @@ describe('checkWslBridgeDistro', () => { await expect(Promise.all(checks)).resolves.toEqual(['running', 'running']) }) }) - -describe('classifyWslBridgeExit', () => { - const running = async (): Promise => ({ - status: 'confirmed', - distros: ['Ubuntu'] - }) - - it('calls a running distro that booted again since the spawn rebooted, not an exit', async () => { - await expect(classifyWslBridgeExit('Ubuntu', 0, running, async () => true)).resolves.toBe( - 'rebooted' - ) - await expect(classifyWslBridgeExit('Ubuntu', 0, running, async () => false)).resolves.toBe( - 'running' - ) - }) - - it('asks about the boot id only when the running list says running', async () => { - const rebooted = vi.fn(async () => true) - await expect( - classifyWslBridgeExit( - 'Ubuntu', - 0, - async () => ({ status: 'confirmed', distros: [] }), - rebooted - ) - ).resolves.toBe('lost') - await expect( - classifyWslBridgeExit('Ubuntu', 0, async () => ({ status: 'unavailable' }), rebooted) - ).resolves.toBe('unavailable') - expect(rebooted).not.toHaveBeenCalled() - }) -}) diff --git a/src/main/gitlab/client-mr-job-ci.test.ts b/src/main/gitlab/client-mr-job-ci.test.ts deleted file mode 100644 index c866c5951be..00000000000 --- a/src/main/gitlab/client-mr-job-ci.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type * as GlUtils from './gl-utils' - -const { - glabExecFileAsyncMock, - glabApiWithHeadersMock, - getGlabKnownHostsMock, - getProjectRefMock, - resolveIssueSourceMock, - acquireMock, - releaseMock, - gitExecFileAsyncMock -} = vi.hoisted(() => ({ - glabExecFileAsyncMock: vi.fn(), - glabApiWithHeadersMock: vi.fn(), - getGlabKnownHostsMock: vi.fn(), - getProjectRefMock: vi.fn(), - resolveIssueSourceMock: vi.fn(), - acquireMock: vi.fn(), - releaseMock: vi.fn(), - gitExecFileAsyncMock: vi.fn() -})) - -// Why: the #9171 default-branch guard resolves the repo default branch via -// git; keep those probes hermetic instead of spawning real git processes. -vi.mock('../git/runner', () => ({ - gitExecFileAsync: gitExecFileAsyncMock -})) - -vi.mock('./gl-utils', async () => { - const actual = await vi.importActual('./gl-utils') - return { - ...actual, - glabExecFileAsync: glabExecFileAsyncMock, - glabApiWithHeaders: glabApiWithHeadersMock, - getGlabKnownHosts: getGlabKnownHostsMock, - getProjectRef: getProjectRefMock, - resolveIssueSource: resolveIssueSourceMock, - acquire: acquireMock, - release: releaseMock - } -}) - -import { getJobTrace, retryJob } from './client' -import { resetGitLabMrMocks } from './client-mr-test-harness' - -describe('gitlab client — MR operations', () => { - beforeEach(() => { - resetGitLabMrMocks({ - glabExecFileAsyncMock, - glabApiWithHeadersMock, - getGlabKnownHostsMock, - getProjectRefMock, - resolveIssueSourceMock, - acquireMock, - releaseMock, - gitExecFileAsyncMock - }) - }) - - describe('job CI operations', () => { - beforeEach(() => { - resolveIssueSourceMock.mockImplementation(async () => ({ - source: { host: 'git.internal', path: 'g/p' }, - fellBack: false - })) - }) - - it('fetches a job trace through the selected SSH GitLab host', async () => { - glabExecFileAsyncMock.mockResolvedValueOnce({ stdout: 'trace output' }) - - await expect(getJobTrace('/repo', 99, 'upstream', 'conn-1')).resolves.toEqual({ - ok: true, - trace: 'trace output' - }) - - expect(glabExecFileAsyncMock).toHaveBeenCalledWith( - ['api', '--hostname', 'git.internal', 'projects/g%2Fp/jobs/99/trace'], - { timeout: 60_000 } - ) - }) - - // Canceled-before-start jobs have no trace; show an empty log rather than issue-edit error copy. - it('reports a 404 trace as an empty log rather than an error', async () => { - glabExecFileAsyncMock.mockRejectedValueOnce(new Error('HTTP 404 Not Found')) - - await expect(getJobTrace('/repo', 99, 'upstream', 'conn-1')).resolves.toEqual({ - ok: true, - trace: '' - }) - }) - - it('keeps a missing project and a scope failure as errors, in job-log wording', async () => { - glabExecFileAsyncMock.mockRejectedValueOnce(new Error('HTTP 404: Project Not Found')) - await expect(getJobTrace('/repo', 99, 'upstream', 'conn-1')).resolves.toMatchObject({ - ok: false, - error: "Could not find this job's GitLab project." - }) - - glabExecFileAsyncMock.mockRejectedValueOnce(new Error('HTTP 403 insufficient_scope')) - const forbidden = await getJobTrace('/repo', 99, 'upstream', 'conn-1') - expect(forbidden).toEqual({ - ok: false, - error: "You don't have permission to read this job's log. Check your GitLab token scopes.", - errorCode: 'gitlab.jobLogPermissionDenied' - }) - }) - - it('retries a job through the selected SSH GitLab host', async () => { - glabExecFileAsyncMock.mockResolvedValueOnce({ - stdout: JSON.stringify({ - id: 100, - pipeline: { id: 50 }, - name: 'test', - stage: 'test', - status: 'pending', - web_url: 'https://git.internal/g/p/-/jobs/100', - duration: null - }) - }) - - await expect(retryJob('/repo', 99, 'upstream', 'conn-1')).resolves.toEqual({ - ok: true, - job: { - id: 100, - pipelineId: 50, - name: 'test', - stage: 'test', - status: 'pending', - webUrl: 'https://git.internal/g/p/-/jobs/100', - duration: null - } - }) - - expect(glabExecFileAsyncMock).toHaveBeenCalledWith( - ['api', '--hostname', 'git.internal', '-X', 'POST', 'projects/g%2Fp/jobs/99/retry'], - {} - ) - }) - }) -}) diff --git a/src/main/ipc/local-worktree-runtime-options.test.ts b/src/main/ipc/local-worktree-runtime-options.test.ts index ea70581bdd7..47e1fcb71d2 100644 --- a/src/main/ipc/local-worktree-runtime-options.test.ts +++ b/src/main/ipc/local-worktree-runtime-options.test.ts @@ -64,12 +64,6 @@ describe('getLocalRepoForRegisteredWorktree', () => { expect(counter.splitCalls).toBe(rowCount) }) - it('never touches the meta table when a repo path matches directly', () => { - const { store, metaScans } = makeStore([{ id: 'repo-a', path: '/repos/a' }], []) - expect(getLocalRepoForRegisteredWorktree(store, '/repos/a', '/repos/a')?.id).toBe('repo-a') - expect(metaScans()).toBe(0) - }) - describe('equivalence with the per-repo scan', () => { const repos: TestRepo[] = [ { id: 'first', path: '/repos/first' }, @@ -77,21 +71,6 @@ describe('getLocalRepoForRegisteredWorktree', () => { { id: 'last', path: '/repos/last' } ] - it('finds a worktree owned by the first repo', () => { - const { store } = makeStore(repos, [worktreeId('first', '/wt/one')]) - expect(getLocalRepoForRegisteredWorktree(store, '/wt/one', '/wt/one')?.id).toBe('first') - }) - - it('finds a worktree owned by the last repo', () => { - const { store } = makeStore(repos, [worktreeId('last', '/wt/one')]) - expect(getLocalRepoForRegisteredWorktree(store, '/wt/one', '/wt/one')?.id).toBe('last') - }) - - it('returns undefined when no repo owns the worktree', () => { - const { store } = makeStore(repos, [worktreeId('other', '/wt/elsewhere')]) - expect(getLocalRepoForRegisteredWorktree(store, '/wt/one', '/wt/one')).toBeUndefined() - }) - it('keeps getRepos precedence when two repos both own the path', () => { const { store } = makeStore(repos, [ worktreeId('last', '/wt/shared'), @@ -123,19 +102,5 @@ describe('getLocalRepoForRegisteredWorktree', () => { const { store } = makeStore(repos, [worktreeId('middle', '/wt/one')]) expect(getLocalRepoForRegisteredWorktree(store, '/wt/other', '/wt/one')?.id).toBe('middle') }) - - it('returns undefined for a folder workspace that is not a registered worktree', () => { - const { store } = makeStore(repos, [worktreeId('middle', '/wt/one')]) - expect( - getLocalRepoForRegisteredWorktree(store, '/folders/notes', '/folders/notes') - ).toBeUndefined() - }) - - it('tolerates a store without getRepos or getAllWorktreeMeta', () => { - expect(getLocalRepoForRegisteredWorktree({} as never, '/wt/one', '/wt/one')).toBeUndefined() - expect( - getLocalRepoForRegisteredWorktree({ getRepos: () => repos } as never, '/wt/one', '/wt/one') - ).toBeUndefined() - }) }) }) diff --git a/src/main/ipc/pty/pty-spawn-cwd-fallback-hung-folder.test.ts b/src/main/ipc/pty/pty-spawn-cwd-fallback-hung-folder.test.ts index 09bb8b6a7bb..fb92beac17b 100644 --- a/src/main/ipc/pty/pty-spawn-cwd-fallback-hung-folder.test.ts +++ b/src/main/ipc/pty/pty-spawn-cwd-fallback-hung-folder.test.ts @@ -1,6 +1,7 @@ import type * as FsPromises from 'node:fs/promises' import { beforeEach, describe, expect, it, vi } from 'vitest' -import { statSyncMock, spawnMock } from './pty-ipc-mock-registry' +import { spawnMock } from './pty-ipc-mock-registry' + import { setupPtyIpcSuite } from './pty-ipc-test-harness' import { registerPtyHandlers } from './register-handlers' @@ -57,13 +58,8 @@ vi.mock('../../codex/codex-state-db-backfill-recovery', () => ) const WORKTREE = '/Volumes/nas/repo' -const SAVED_CWD = `${WORKTREE}/packages/app` const directoryStats = { isDirectory: () => true } -function statAnsweringAfter(ms: number): Promise { - return new Promise((resolve) => setTimeout(() => resolve(directoryStats), ms)) -} - function spawnWithWorktreeFallback(cwd: string): Promise { return Promise.resolve( handlers.get('pty:spawn')!(null, { @@ -85,31 +81,6 @@ beforeEach(() => { }) describe('pty:spawn with the worktree-folder fallback on a mount that stopped answering', () => { - it('waits on a hung saved folder without blocking main or calling statSync', async () => { - statMock.mockReturnValue(new Promise(() => {})) - registerPtyHandlers(mainWindow as never) - - const settled = vi.fn() - void spawnWithWorktreeFallback(SAVED_CWD).then(settled, settled) - const timerFired = await new Promise((resolve) => setTimeout(() => resolve(true), 50)) - - expect(timerFired).toBe(true) - expect(settled).not.toHaveBeenCalled() - expect(spawnMock).not.toHaveBeenCalled() - expect(statSyncMock).not.toHaveBeenCalledWith(SAVED_CWD) - }) - - it('starts in a slow saved folder once it answers', async () => { - statMock.mockImplementation(() => statAnsweringAfter(50)) - registerPtyHandlers(mainWindow as never) - - const result = await spawnWithWorktreeFallback(SAVED_CWD) - - const [, , options] = spawnMock.mock.calls.at(-1) as [string, string[], { cwd: string }] - expect(options.cwd).toBe(SAVED_CWD) - expect(result).toEqual(expect.not.objectContaining({ startupCwdFallback: expect.anything() })) - }) - it('never asks the worktree folder once the saved folder answers', async () => { const outsideCwd = '/Users/alice/scratch' statMock.mockImplementation(async (path) => @@ -123,19 +94,4 @@ describe('pty:spawn with the worktree-folder fallback on a mount that stopped an const [, , options] = spawnMock.mock.calls.at(-1) as [string, string[], { cwd: string }] expect(options.cwd).toBe(outsideCwd) }) - - it('still falls back to the worktree folder when the saved one is gone', async () => { - statMock.mockImplementation(async (path) => { - if (path === SAVED_CWD) { - throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' }) - } - return directoryStats - }) - registerPtyHandlers(mainWindow as never) - - const result = await spawnWithWorktreeFallback(SAVED_CWD) - - expect(statMock.mock.calls.map(([path]) => path)).toEqual([SAVED_CWD, WORKTREE]) - expect(result).toMatchObject({ startupCwdFallback: { kind: 'worktree', cwd: WORKTREE } }) - }) }) diff --git a/src/main/ipc/pty/pty-spawn-wsl-passive-restore-hold.test.ts b/src/main/ipc/pty/pty-spawn-wsl-passive-restore-hold.test.ts index 0695eca8f28..b4652896001 100644 --- a/src/main/ipc/pty/pty-spawn-wsl-passive-restore-hold.test.ts +++ b/src/main/ipc/pty/pty-spawn-wsl-passive-restore-hold.test.ts @@ -72,7 +72,7 @@ const WSL_RUNTIME = { // E9: a restore the user did not ask for must not start a stopped distro, whatever provider runs. describe('a passive WSL restore with a stopped distro', () => { - const { handlers, mainWindow, installDaemonTestProvider } = setupPtyIpcSuite() + const { handlers, mainWindow } = setupPtyIpcSuite() const originalPlatform = process.platform beforeEach(() => { @@ -108,15 +108,6 @@ describe('a passive WSL restore with a stopped distro', () => { expect(spawnMock).not.toHaveBeenCalled() }) - it('holds with the daemon provider too, before its cwd probe', async () => { - const providerSpawn = vi.fn().mockResolvedValue({ id: 'pty-daemon' }) - installDaemonTestProvider({ spawn: providerSpawn }) - register() - await expect(restore()).rejects.toThrow() - expect(wslUncDirectoryExistsAsyncMock).not.toHaveBeenCalled() - expect(providerSpawn).not.toHaveBeenCalled() - }) - it('restores once the distro runs, and never holds a restore the user asked for', async () => { register() runningStateMock.mockResolvedValue('running') @@ -129,18 +120,4 @@ describe('a passive WSL restore with a stopped distro', () => { expect(runningStateMock).not.toHaveBeenCalled() expect(spawnMock).toHaveBeenCalledTimes(2) }) - - it("hands the provider this spawn's read anchor, so its hold reuses only that read", async () => { - const providerSpawn = vi.fn().mockResolvedValue({ id: 'pty-daemon' }) - installDaemonTestProvider({ spawn: providerSpawn }) - register() - runningStateMock.mockResolvedValue('running') - await restore() - - const anchor = runningStateMock.mock.calls[0]?.[1]?.startedAfter - expect(anchor).toEqual(expect.any(Number)) - expect(providerSpawn).toHaveBeenCalledWith( - expect.objectContaining({ wslHoldReadAnchor: anchor }) - ) - }) }) diff --git a/src/main/jira/jira-transition-required-fields.test.ts b/src/main/jira/jira-transition-required-fields.test.ts index 542c455a68b..79a37fb4be6 100644 --- a/src/main/jira/jira-transition-required-fields.test.ts +++ b/src/main/jira/jira-transition-required-fields.test.ts @@ -51,21 +51,6 @@ describe('required Jira transition fields', () => { getClients.mockReturnValue([entry]) }) - it('loads required resolution metadata rather than hiding it from the form', async () => { - jiraRequest.mockResolvedValue({ transitions: [transition] }) - const { listTransitions } = await import('./jira-transition-queries') - expect(await listTransitions('APP-1', 'site-1')).toMatchObject([ - { - id: '31', - fields: [{ key: 'resolution', required: true, allowedValues: [{ id: '1', name: 'Fixed' }] }] - } - ]) - expect(jiraRequest).toHaveBeenCalledWith( - entry, - '/rest/api/3/issue/APP-1/transitions?expand=transitions.fields' - ) - }) - it('posts resolution and comment in the same request Jira validates', async () => { jiraRequest.mockImplementation(async (_entry, _path, options) => { const payload = JSON.parse(options.body) @@ -107,23 +92,6 @@ describe('required Jira transition fields', () => { expect(clearToken).not.toHaveBeenCalled() }) - it('preserves the legacy empty list on operational failures without strict reads', async () => { - jiraRequest.mockRejectedValue(new JiraApiError('Service unavailable', 503)) - const { listTransitions } = await import('./jira-transition-queries') - expect(await listTransitions('APP-1', 'site-1')).toEqual([]) - expect(jiraRequest).toHaveBeenCalledTimes(1) - expect(release).toHaveBeenCalledOnce() - expect(clearToken).not.toHaveBeenCalled() - }) - - it('does not retry expired credentials', async () => { - jiraRequest.mockRejectedValue(new JiraApiError('Unauthorized', 401)) - const { listTransitions } = await import('./jira-transition-queries') - await expect(listTransitions('APP-1')).rejects.toThrow('Unauthorized') - expect(jiraRequest).toHaveBeenCalledTimes(1) - expect(clearToken).toHaveBeenCalledWith('site-1') - }) - it('keeps old-client bare transitions and Server comments compatible', async () => { jiraRequest.mockResolvedValue(undefined) const { updateIssue } = await import('./jira-issue-mutations') diff --git a/src/main/managed-server/lifecycle/orcad-managed-tunnel-peer-restart.test.ts b/src/main/managed-server/lifecycle/orcad-managed-tunnel-peer-restart.test.ts index 968f6d19a1e..61ec32fb2fd 100644 --- a/src/main/managed-server/lifecycle/orcad-managed-tunnel-peer-restart.test.ts +++ b/src/main/managed-server/lifecycle/orcad-managed-tunnel-peer-restart.test.ts @@ -131,16 +131,4 @@ describe("a managed tunnel follows another desktop's restart of the server", () }) expect([...state.forwardedTo.values()]).toEqual([6_768]) }) - - it('keeps the tunnel when the host still names the forwarded port', async () => { - const state = setup() - const environment = createEnvironment() - await state.manager.ensure(environment) - state.ensureServing.mockResolvedValueOnce({ state: 'unverifiable', detail: 'not answering' }) - - await expect(state.manager.verifyServing(environment)).resolves.toMatchObject({ - state: 'unverifiable' - }) - expect([...state.forwardedTo.values()]).toEqual([6_768]) - }) }) diff --git a/src/main/media/remote-file-preview-reader.test.ts b/src/main/media/remote-file-preview-reader.test.ts index 3bccae0f633..02f087e0688 100644 --- a/src/main/media/remote-file-preview-reader.test.ts +++ b/src/main/media/remote-file-preview-reader.test.ts @@ -9,12 +9,9 @@ vi.mock('../ipc/runtime-environments/runtime-environment-registry-root', () => ( })) import { - REMOTE_PREVIEW_UPDATE_REQUIRED_MESSAGE, - RemotePreviewPairingChangedError, assertRemoteChunkReadsSupported, readRemotePreviewRange, - readRemotePreviewChunk, - statRemotePreviewFile + readRemotePreviewChunk } from './remote-file-preview-reader' const address = { @@ -24,38 +21,11 @@ const address = { expectedEnvironmentPairingRevision: 7 } const ok = (result: unknown) => ({ ok: true, result }) -const fail = (code: string, message: string) => ({ ok: false, error: { code, message } }) describe('remote file preview reader', () => { const signal = new AbortController().signal beforeEach(() => callRuntimeEnvironment.mockReset()) - it('stats the file through its pinned pairing', async () => { - callRuntimeEnvironment.mockResolvedValue( - ok({ size: 6, isDirectory: false, mtime: 1, ctime: 2 }) - ) - await expect(statRemotePreviewFile(address, signal)).resolves.toEqual({ - size: 6, - mtime: 1, - ctime: 2 - }) - expect(callRuntimeEnvironment).toHaveBeenCalledWith( - '/user-data', - 'env-1', - 'files.stat', - { worktree: 'id:wt-1', relativePath: 'out/clip.mp4' }, - 15_000, - 7, - undefined, - { signal } - ) - }) - - it('refuses a directory', async () => { - callRuntimeEnvironment.mockResolvedValue(ok({ size: 0, isDirectory: true, mtime: 1 })) - await expect(statRemotePreviewFile(address, signal)).rejects.toThrow('directory') - }) - it('decodes a chunk and refuses one whose length disagrees', async () => { callRuntimeEnvironment.mockResolvedValueOnce( ok({ contentBase64: 'AAECAw==', bytesRead: 4, eof: false }) @@ -69,20 +39,6 @@ describe('remote file preview reader', () => { await expect(readRemotePreviewChunk(address, 0, 4, signal)).rejects.toThrow('corrupted') }) - it('tells the user to update a host without range reads', async () => { - callRuntimeEnvironment.mockResolvedValue(fail('method_not_found', 'x')) - await expect(statRemotePreviewFile(address, signal)).rejects.toThrow( - REMOTE_PREVIEW_UPDATE_REQUIRED_MESSAGE - ) - }) - - it('reports a re-paired environment as a dead pairing, not a transient failure', async () => { - callRuntimeEnvironment.mockResolvedValue(fail('runtime_environment_changed', 'changed')) - await expect(readRemotePreviewChunk(address, 0, 1, signal)).rejects.toBeInstanceOf( - RemotePreviewPairingChangedError - ) - }) - it('proves chunk reads with one byte once per pairing, and re-proves after a re-pair', async () => { callRuntimeEnvironment.mockResolvedValue( ok({ contentBase64: 'AA==', bytesRead: 1, eof: false }) @@ -108,23 +64,6 @@ describe('remote file preview reader', () => { expect(callRuntimeEnvironment).toHaveBeenCalledTimes(2) }) - it('never caches a verdict no pairing revision pins', async () => { - callRuntimeEnvironment.mockResolvedValue( - ok({ contentBase64: 'AA==', bytesRead: 1, eof: false }) - ) - const unpinned = { environmentId: 'env-unpinned', worktree: 'id:wt-1', relativePath: 'a.mp4' } - await assertRemoteChunkReadsSupported(unpinned, signal) - await assertRemoteChunkReadsSupported(unpinned, signal) - expect(callRuntimeEnvironment).toHaveBeenCalledTimes(2) - }) - - it('tells the user to update a host that answers files.stat but not files.readChunk', async () => { - callRuntimeEnvironment.mockResolvedValue(fail('method_not_found', 'x')) - await expect( - assertRemoteChunkReadsSupported({ ...address, environmentId: 'env-old' }, signal) - ).rejects.toThrow(REMOTE_PREVIEW_UPDATE_REQUIRED_MESSAGE) - }) - it('reads a range in host-sized chunks, clipped to the file and stopping at EOF', async () => { callRuntimeEnvironment .mockResolvedValueOnce( diff --git a/src/main/native-chat/agent-model-catalog/agent-model-catalog-discovery-freshness.test.ts b/src/main/native-chat/agent-model-catalog/agent-model-catalog-discovery-freshness.test.ts index 9d9dea5de63..2e6181efe8a 100644 --- a/src/main/native-chat/agent-model-catalog/agent-model-catalog-discovery-freshness.test.ts +++ b/src/main/native-chat/agent-model-catalog/agent-model-catalog-discovery-freshness.test.ts @@ -5,11 +5,7 @@ import type { AgentSessionAccountHome } from '../../../shared/agent-session-acco import type { AgentSessionModelOption } from '../../../shared/agent-session-wire' import { agentModelCatalogFingerprintForRecord } from './agent-model-catalog-fingerprint' import { createAgentModelCatalogService } from './agent-model-catalog-service' -import { - AGENT_MODEL_CATALOG_FRESH_MS, - AgentModelCatalogStore, - type AgentModelCatalogSuccess -} from './agent-model-catalog-store' +import { AgentModelCatalogStore, type AgentModelCatalogSuccess } from './agent-model-catalog-store' const HOME: AgentSessionAccountHome = { variable: 'GROK_HOME', path: '/homes/grok' } @@ -105,15 +101,6 @@ describe('a live listing beside the account-level discovery', () => { expect(read?.models[0]?.defaultEffort).toBe('high') }) - it('names the default on a cold account whose first listing came from a live chat', async () => { - const run = harness(async () => discovery('grok-4')) - run.saveLive() - // A live listing alone names no default and does not count as fresh, so explicit demand lists the account. - await run.catalog.demand({ agent: 'grok' }) - await vi.waitFor(() => expect(run.probes).toHaveBeenCalledTimes(1)) - await vi.waitFor(async () => expect(await run.defaultOf()).toBe('grok-4')) - }) - it('still refreshes the account listing after ten minutes of live saves', async () => { let configured = 'grok-4' const run = harness(async () => discovery(configured)) @@ -130,13 +117,4 @@ describe('a live listing beside the account-level discovery', () => { await vi.waitFor(() => expect(run.probes).toHaveBeenCalledTimes(2)) await vi.waitFor(async () => expect(await run.defaultOf()).toBe('grok-3-mini')) }) - - it('does not refresh while the discovery is younger than its freshness window', async () => { - const run = harness(async () => discovery('grok-4')) - await run.read() - run.advance(AGENT_MODEL_CATALOG_FRESH_MS - 1) - run.saveLive() - await run.read() - expect(run.probes).toHaveBeenCalledTimes(1) - }) }) diff --git a/src/main/native-chat/agent-session-wire/structured-agent-session-resolved-append.test.ts b/src/main/native-chat/agent-session-wire/structured-agent-session-resolved-append.test.ts index 16ad4b01f7e..be64172efb0 100644 --- a/src/main/native-chat/agent-session-wire/structured-agent-session-resolved-append.test.ts +++ b/src/main/native-chat/agent-session-wire/structured-agent-session-resolved-append.test.ts @@ -97,17 +97,6 @@ describe('resolved revisions', () => { expect(rowText()).toBe('abc') }) - it('skips a revision that resolves to nothing', async () => { - const deferred = createDeferredStructuredAgentSessionEventSink(testEventSinkLogging()) - deferred.sink.tryReviseResolvedItem?.(1_000, () => null, { - turnScope: AGENT_JOURNAL_THREAD_SCOPE - }) - const before = journal.cursor() - deferred.bind(journalTarget()) - await expect(deferred.drained()).resolves.toEqual({ ok: true }) - expect(journal.cursor()).toEqual(before) - }) - it('publishes a revision in the operation that writes it, within the same reservation', async () => { const deferred = createDeferredStructuredAgentSessionEventSink(testEventSinkLogging()) const bytes = estimateStructuredAgentSessionItemBytes(ROW, text('a')) diff --git a/src/main/network/electron-proxy-request-guard.test.ts b/src/main/network/electron-proxy-request-guard.test.ts index b110551627d..870eed49c3e 100644 --- a/src/main/network/electron-proxy-request-guard.test.ts +++ b/src/main/network/electron-proxy-request-guard.test.ts @@ -35,30 +35,6 @@ function createSession() { describe('default-session proxy request guard', () => { beforeEach(() => vi.clearAllMocks()) - it('holds renderer requests until a delayed proxy transition settles', async () => { - const { proxySession, request } = createSession() - let finishWrite: (() => void) | undefined - proxySession.setProxy.mockImplementationOnce( - () => new Promise((resolve) => (finishWrite = resolve)) - ) - resetSessionProxyApplicationForTests(proxySession) - installElectronProxyRequestGuard(proxySession as never) - - const applying = applyProxySettingsToSession( - proxySession, - { httpProxyUrl: 'http://proxy.example:8080' }, - { env: {} } - ) - const callback = vi.fn() - request(callback) - expect(callback).not.toHaveBeenCalled() - - await vi.waitFor(() => expect(proxySession.setProxy).toHaveBeenCalledOnce()) - finishWrite?.() - await applying - await vi.waitFor(() => expect(callback).toHaveBeenCalledWith({})) - }) - it('keeps renderer requests blocked across a transient retry', async () => { const { proxySession, request } = createSession() let finishRetry: (() => void) | undefined diff --git a/src/main/node-runtime/managed-runtime-installation.test.ts b/src/main/node-runtime/managed-runtime-installation.test.ts index 7694de9b9d1..3a259f74e6e 100644 --- a/src/main/node-runtime/managed-runtime-installation.test.ts +++ b/src/main/node-runtime/managed-runtime-installation.test.ts @@ -1,11 +1,12 @@ import { createHash } from 'node:crypto' -import { mkdir, mkdtemp, readdir, rm, writeFile } from 'node:fs/promises' +import { mkdtemp, readdir, rm, writeFile } from 'node:fs/promises' + import { tmpdir } from 'node:os' import { join } from 'node:path' import { strToU8, zipSync } from 'fflate' import { afterEach, expect, it, vi } from 'vitest' import type * as ReleaseModule from './managed-runtime-release' -import { reclaimRuntimeReleases, RUNTIME_RELEASE_RECEIPT } from './managed-runtime-release' +import { RUNTIME_RELEASE_RECEIPT } from './managed-runtime-release' import { ManagedRuntimeInstallation } from './managed-runtime-installation' vi.mock('./managed-runtime-release', async (importOriginal) => { @@ -90,34 +91,6 @@ it('installs only when asked, with progress, cancel, retry, and a status read fr expect(await installation.status()).toMatchObject({ installedCurrent: true }) }) -it('publishes an update only once the old releases were reclaimed', async () => { - const { root, host, phase } = await rig(['whole']) - for (const version of ['0.8', '0.9']) { - await mkdir(join(root, version), { recursive: true }) - await writeFile(join(root, version, 'server'), 'old server', { mode: 0o755 }) - const members = [{ path: 'server', bytes: 10 }] - const receipt = { releaseId: sha256(version), version, members } - await writeFile( - join(root, version, RUNTIME_RELEASE_RECEIPT), - JSON.stringify({ ...receipt, platform: process.platform, arch: process.arch }) - ) - } - let resume = (): void => {} - const reclaim = vi.mocked(reclaimRuntimeReleases) - const paused = new Promise((resolve) => (resume = resolve)) - const { reclaimRuntimeReleases: reclaimNow } = await vi.importActual( - './managed-runtime-release' - ) - reclaim.mockImplementationOnce(async (...args) => paused.then(() => reclaimNow(...args))) - const installation = host() - installation.install() - await new Promise((resolve) => setTimeout(resolve, 100)) - expect((await installation.status()).operation).toMatchObject({ downloadedBytes: 0 }) - resume() - await vi.waitFor(async () => expect(await phase(installation)).toBe('succeeded')) - expect((await readdir(root)).sort()).toEqual(['0.9', '1.0']) -}) - it('repairs a damaged copy when Install is clicked again', async () => { const { root, host, phase } = await rig(['whole', 'whole']) const installation = host() diff --git a/src/main/orca-profiles/profile-cloud-dev-service.test.ts b/src/main/orca-profiles/profile-cloud-dev-service.test.ts index 65c8d1c1ef5..a4e9ab7d75e 100644 --- a/src/main/orca-profiles/profile-cloud-dev-service.test.ts +++ b/src/main/orca-profiles/profile-cloud-dev-service.test.ts @@ -43,9 +43,7 @@ vi.mock('./profile-cloud-client', () => ({ import { connectCurrentOrcaProfile, - createCloudLinkedOrcaProfile, getCurrentOrcaProfileAuthStatus, - selectCurrentOrcaProfileOrg, signOutCurrentOrcaProfile } from './profile-cloud-service' @@ -73,59 +71,6 @@ describe('Orca cloud dev auth service', () => { vi.unstubAllEnvs() }) - it('connects the active profile without PKCE or cloud endpoints', async () => { - expect(getCurrentOrcaProfileAuthStatus(userDataPath)).toMatchObject({ - configured: true, - state: 'local' - }) - - const result = await connectCurrentOrcaProfile(userDataPath) - - expect(result.status).toBe('connected') - expect(beginOrcaCloudPkceFlowMock).not.toHaveBeenCalled() - expect(exchangeOrcaCloudAuthCodeMock).not.toHaveBeenCalled() - expect(getCurrentOrcaProfileAuthStatus(userDataPath)).toMatchObject({ - configured: true, - state: 'connected', - persistence: 'encrypted', - cloud: { - cloudProfileId: 'dev-cloud-local-default', - email: 'dev@orca.local' - }, - capabilities: { - flags: expect.objectContaining({ 'share.create': true }) - } - }) - expect(getCurrentOrcaProfileAuthStatus(userDataPath).organizations).toHaveLength(2) - }) - - it('selects dev organizations and creates org-scoped cloud profiles locally', async () => { - await connectCurrentOrcaProfile(userDataPath) - - const selected = await selectCurrentOrcaProfileOrg(userDataPath, 'dev-acme') - const created = await createCloudLinkedOrcaProfile(userDataPath, { - orgId: 'dev-acme', - name: 'Acme Dev' - }) - - expect(selected.status).toBe('selected') - expect(getCurrentOrcaProfileAuthStatus(userDataPath).cloud).toMatchObject({ - activeOrgId: 'dev-acme', - activeOrgName: 'Acme Dev' - }) - expect(created.status).toBe('created') - if (created.status === 'created') { - expect(created.profile).toMatchObject({ - name: 'Acme Dev', - kind: 'cloud-linked', - cloud: expect.objectContaining({ - activeOrgId: 'dev-acme', - activeOrgName: 'Acme Dev' - }) - }) - } - }) - it('signs out locally without calling the cloud logout endpoint', async () => { await connectCurrentOrcaProfile(userDataPath) const regionCache = join(userDataPath, 'orca-relay-region-preference.json') diff --git a/src/main/orcad/electron-serve-provider-selection.test.ts b/src/main/orcad/electron-serve-provider-selection.test.ts index ca401e45198..47de8507731 100644 --- a/src/main/orcad/electron-serve-provider-selection.test.ts +++ b/src/main/orcad/electron-serve-provider-selection.test.ts @@ -92,20 +92,6 @@ afterEach(async () => { }) describe('resolveOrcadBrowserProvider Electron preference', () => { - it('uses the installed Electron app even when a Chromium executable is configured', async () => { - provider = await resolveOrcadBrowserProvider({ - userDataPath: join(harnessRoot, 'state'), - environment: { ORCA_BROWSER_EXECUTABLE: chromiumExecutable }, - resolveInstalledElectronExecutable: async () => INSTALLED_EXECUTABLE, - resolveAgentBrowserBinary: () => '/agent-browser' - }) - - expect(provider?.kind).toBe('electron') - expect(provider?.isAvailable()).toBe(true) - expect(spawnProcessMock.mock.calls[0][0].program).toBe(INSTALLED_EXECUTABLE) - expect(runProcessMock).not.toHaveBeenCalled() - }) - it('falls back to the operator Chromium when the installed Electron app cannot start', async () => { sidecarMode = 'exit-before-ready' const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) @@ -124,19 +110,4 @@ describe('resolveOrcadBrowserProvider Electron preference', () => { expect.anything() ) }) - - it('returns no provider when Electron fails and no Chromium is configured', async () => { - sidecarMode = 'exit-before-ready' - vi.spyOn(console, 'warn').mockImplementation(() => undefined) - - provider = await resolveOrcadBrowserProvider({ - userDataPath: join(harnessRoot, 'state'), - environment: {}, - resolveInstalledElectronExecutable: async () => INSTALLED_EXECUTABLE, - resolveAgentBrowserBinary: () => '/agent-browser' - }) - - expect(provider).toBeNull() - expect(runProcessMock).not.toHaveBeenCalled() - }) }) diff --git a/src/main/orcad/orcad-daemon-teardown-pid-reuse.test.ts b/src/main/orcad/orcad-daemon-teardown-pid-reuse.test.ts index 2c1f8350461..a93a639b792 100644 --- a/src/main/orcad/orcad-daemon-teardown-pid-reuse.test.ts +++ b/src/main/orcad/orcad-daemon-teardown-pid-reuse.test.ts @@ -76,16 +76,6 @@ describe('daemon descendant walk under Windows pid reuse', () => { expect(await teardown(root, [daemon, ...system], system)).toBeNull() }) - it('still fails on a child the daemon started and left running', async ({ onTestFinished }) => { - const daemonPid = await exitedPid() - const daemon = { pid: daemonPid, ppid: 1, name: 'node.exe', creationTimeMs: 5_000 } - const child = { pid: 77, ppid: daemonPid, name: 'OpenConsole.exe', creationTimeMs: 6_000 } - const root = profile(daemonPid, onTestFinished) - expect(await teardown(root, [daemon, child], [child])).toMatchObject( - leaked(`OpenConsole.exe 77 (ppid ${daemonPid}, created 6000)`) - ) - }) - it('still fails on a grandchild chain created after the daemon', async ({ onTestFinished }) => { const daemonPid = await exitedPid() const daemon = { pid: daemonPid, ppid: 1, name: 'node.exe', creationTimeMs: 5_000 } @@ -97,17 +87,6 @@ describe('daemon descendant walk under Windows pid reuse', () => { ) }) - it('ignores an older process whose reused parent pid belongs to an exited daemon child', async ({ - onTestFinished - }) => { - const daemonPid = await exitedPid() - const daemon = { pid: daemonPid, ppid: 1, name: 'node.exe', creationTimeMs: 5_000 } - const child = { pid: 2_000, ppid: daemonPid, name: 'OpenConsole.exe', creationTimeMs: 6_000 } - const telemetry = { pid: 2_904, ppid: 2_000, name: 'vctip.exe', creationTimeMs: 1_000 } - const root = profile(daemonPid, onTestFinished) - expect(await teardown(root, [daemon, child, telemetry], [telemetry])).toBeNull() - }) - it('ignores children of a newer process that reused the exited daemon pid', async ({ onTestFinished }) => { @@ -121,25 +100,4 @@ describe('daemon descendant walk under Windows pid reuse', () => { leaked(`OpenConsole.exe 77 (ppid ${daemonPid}, created 6000)`) ) }) - - it.for([ - { daemonCreated: undefined, childCreated: 10 }, - { daemonCreated: 5_000, childCreated: undefined } - ])( - 'keeps a child when a creation time is unknown (%o)', - async ({ daemonCreated, childCreated }, { onTestFinished }) => { - const daemonPid = await exitedPid() - const daemon = { pid: daemonPid, ppid: 1, name: 'node.exe', creationTimeMs: daemonCreated } - const child = { - pid: 77, - ppid: daemonPid, - name: 'OpenConsole.exe', - creationTimeMs: childCreated - } - const root = profile(daemonPid, onTestFinished) - expect(await teardown(root, [daemon, child], [child])).toMatchObject( - leaked(`OpenConsole.exe 77 (ppid ${daemonPid}, created ${childCreated})`) - ) - } - ) }) diff --git a/src/main/plugins/plugin-service-housekeeping.test.ts b/src/main/plugins/plugin-service-housekeeping.test.ts index eae92248f37..2703056d07e 100644 --- a/src/main/plugins/plugin-service-housekeeping.test.ts +++ b/src/main/plugins/plugin-service-housekeeping.test.ts @@ -45,16 +45,6 @@ function failRegistration(): void { } describe('PluginServiceHousekeeping', () => { - it('checks bindings and reaps healthy workers without refreshing or restarting watchers', async () => { - const { reapIdle, refresh } = sync() - await vi.advanceTimersByTimeAsync(180_000) - expect(reapIdle).toHaveBeenCalledTimes(3) - expect(refresh).not.toHaveBeenCalled() - expect(watcher().start).toHaveBeenCalledOnce() - expect(watcher().checkRootBindings).toHaveBeenCalledTimes(3) - expect(vi.getTimerCount()).toBe(1) - }) - it('retries failed registration using the existing maintenance interval', async () => { const { reapIdle, refresh } = sync() failRegistration() @@ -70,15 +60,6 @@ describe('PluginServiceHousekeeping', () => { expect(vi.getTimerCount()).toBe(1) }) - it('retains a synchronous startup failure for the next bounded retry', async () => { - vi.mocked(watcher().start).mockImplementation((_paths, _refresh, onWatcherError) => { - onWatcherError?.() - }) - const { refresh } = sync() - await vi.advanceTimersByTimeAsync(60_000) - expect(refresh).toHaveBeenCalledOnce() - }) - it('keeps unchanged missing roots idle and refreshes when their binding appears', async () => { const { refresh, reapIdle } = sync() const onWatcherError = vi.mocked(watcher().start).mock.calls[0]?.[2] @@ -125,20 +106,4 @@ describe('PluginServiceHousekeeping', () => { await Promise.resolve() expect(refresh).toHaveBeenCalledOnce() }) - - it('stops retries and releases subscriptions when disabled or disposed', () => { - const { reapIdle, refresh } = sync() - failRegistration() - housekeeping.sync({ enabled: false, devPaths: ['plugin'], reapIdle, refresh }) - vi.advanceTimersByTime(120_000) - expect(reapIdle).not.toHaveBeenCalled() - expect(refresh).not.toHaveBeenCalled() - expect(vi.getTimerCount()).toBe(0) - sync() - failRegistration() - housekeeping.dispose() - vi.advanceTimersByTime(120_000) - expect(refresh).not.toHaveBeenCalled() - expect(vi.getTimerCount()).toBe(0) - }) }) diff --git a/src/main/rate-limits/codex-fetcher-runtime-pairing.test.ts b/src/main/rate-limits/codex-fetcher-runtime-pairing.test.ts index 6332071fe12..5cffb5dc263 100644 --- a/src/main/rate-limits/codex-fetcher-runtime-pairing.test.ts +++ b/src/main/rate-limits/codex-fetcher-runtime-pairing.test.ts @@ -123,19 +123,4 @@ describe('codex rate-limit spawn runtime pairing', () => { } } }) - - it('leaves PATH alone when the CLI resolves to a bare command name', async () => { - resolveCodexCommandMock.mockReturnValue('codex') - const rpcChild = makeRpcChild() - childSpawnMock.mockReturnValue(rpcChild) - - const resultPromise = fetchCodexRateLimits() - await vi.advanceTimersByTimeAsync(0) - - const spawnEnv = childSpawnMock.mock.calls[0]?.[2]?.env as NodeJS.ProcessEnv - expect(spawnEnv.PATH).toBe(process.env.PATH) - - rpcChild.emit('close') - await resultPromise - }) }) diff --git a/src/main/runtime/orca-runtime-files-preview-budget.test.ts b/src/main/runtime/orca-runtime-files-preview-budget.test.ts index 61161358104..ffbfb04ebe4 100644 --- a/src/main/runtime/orca-runtime-files-preview-budget.test.ts +++ b/src/main/runtime/orca-runtime-files-preview-budget.test.ts @@ -49,19 +49,6 @@ describe('RuntimeFileCommands', () => { return dir } - it('stays inside the transport ceiling once base64-inflated', () => { - const result = { - content: Buffer.alloc(RUNTIME_PREVIEWABLE_BINARY_MAX_BYTES).toString('base64'), - isBinary: true, - isImage: true, - mimeType: 'image/png' - } - - expect(Buffer.byteLength(JSON.stringify(result), 'utf8')).toBeLessThanOrEqual( - REMOTE_RPC_MAX_CONTENT_BYTES - ) - }) - it('rejects a previewable image one byte above the cap', async () => { const dir = await previewFixture(RUNTIME_PREVIEWABLE_BINARY_MAX_BYTES + 1) const { commands } = createRuntimeFileCommands({ path: dir }) @@ -72,21 +59,6 @@ describe('RuntimeFileCommands', () => { ).rejects.toThrow('file_too_large') }) - it('returns full base64 for a previewable image at the cap', async () => { - const dir = await previewFixture(RUNTIME_PREVIEWABLE_BINARY_MAX_BYTES) - const { commands } = createRuntimeFileCommands({ path: dir }) - resolveAuthorizedPathMock.mockImplementation(async (p: string) => p) - - await expect( - commands.readFileExplorerPreview('id:wt-1', 'logo.png', REMOTE_RPC_MAX_CONTENT_BYTES) - ).resolves.toEqual({ - content: Buffer.alloc(RUNTIME_PREVIEWABLE_BINARY_MAX_BYTES, 0x61).toString('base64'), - isBinary: true, - isImage: true, - mimeType: 'image/png' - }) - }) - it('keeps local previews above the remote cap available without a request budget', async () => { const size = RUNTIME_PREVIEWABLE_BINARY_MAX_BYTES + 1 const dir = await previewFixture(size) diff --git a/src/main/runtime/orca-runtime-migration-deferred-shell-history.test.ts b/src/main/runtime/orca-runtime-migration-deferred-shell-history.test.ts index f4d90cb9673..d01d29e157e 100644 --- a/src/main/runtime/orca-runtime-migration-deferred-shell-history.test.ts +++ b/src/main/runtime/orca-runtime-migration-deferred-shell-history.test.ts @@ -85,60 +85,4 @@ describe('a moved shell history that waited on live shells', () => { expect(options.worktreeHasLiveShells(TEST_WORKTREE_ID)).toBe(true) expect(options.worktreeHasLiveShells('repo-1::/srv/other')).toBe(false) }) - - it('waits when the terminal inventory does not answer', async () => { - const installPending = vi.fn() - const runtime = new OrcaRuntimeService({ - ...store, - installPendingOrcadMigrationReceivedBlobs: installPending, - hasPendingOrcadMigrationShellHistory: () => true - }) - runtime.setPtyController({ - write: () => true, - kill: () => true, - getForegroundProcess: async () => null, - listProcesses: vi.fn(async () => { - throw new Error('daemon unreachable') - }) - }) - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - await runtime.installPendingOrcadMigrationShellHistory() - - expect(installPending).not.toHaveBeenCalled() - warn.mockRestore() - }) - - it('costs a start with nothing pending no inventory and keeps restored receipts', async () => { - const installPending = vi.fn() - const runtime = new OrcaRuntimeService({ - ...store, - installPendingOrcadMigrationReceivedBlobs: installPending, - hasPendingOrcadMigrationShellHistory: () => false - }) - const listProcesses = vi.fn(async () => [{ id: 'daemon-pty', cwd: '/elsewhere', title: 'zsh' }]) - runtime.setPtyController({ - write: () => true, - kill: () => true, - getForegroundProcess: async () => null, - listProcesses - }) - const receipt = { - ptyId: 'daemon-pty', - worktreeId: TEST_WORKTREE_ID, - terminalHandle: 'term_daemon', - paneKey: 'tab-1:1', - processIncarnation: 'daemon-pty:inc-1', - hostScope: { kind: 'local' } - } - internalsOf(runtime).restoredOrchestrationAuthorityByPtyId.set('daemon-pty', receipt) - - await runtime.installPendingOrcadMigrationShellHistory() - - expect(listProcesses).not.toHaveBeenCalled() - expect(installPending).not.toHaveBeenCalled() - expect(internalsOf(runtime).restoredOrchestrationAuthorityByPtyId.get('daemon-pty')).toBe( - receipt - ) - }) }) diff --git a/src/main/runtime/repo-icon-fork-backfill.test.ts b/src/main/runtime/repo-icon-fork-backfill.test.ts index f2ac39119e6..a20d1506063 100644 --- a/src/main/runtime/repo-icon-fork-backfill.test.ts +++ b/src/main/runtime/repo-icon-fork-backfill.test.ts @@ -47,54 +47,6 @@ afterEach(() => { }) describe('startup fork-upstream backfill', () => { - it('keeps a renamed fork own owner avatar while recording the upstream', async () => { - const runtime = new OrcaRuntimeService() - const updateRepo = attachStore(runtime, [makeRepo()]) - getRepoUpstream.mockResolvedValue({ owner: 'upstream-org', repo: 'rocket' }) - getRepoSlug.mockResolvedValue({ owner: 'acme', repo: 'rocket-pro' }) - - await (runtime as unknown as BackfillInternals).repositoryForkBackfill.run() - - expect(updateRepo).toHaveBeenCalledExactlyOnceWith(repoTarget('repo-1'), { - upstream: { owner: 'upstream-org', repo: 'rocket' }, - repoIcon: { - type: 'image', - src: 'https://github.com/acme.png?size=64', - source: 'github', - label: 'acme/rocket-pro' - } - }) - }) - - it('migrates a same-name fork to the upstream owner avatar', async () => { - const runtime = new OrcaRuntimeService() - const updateRepo = attachStore(runtime, [ - makeRepo({ - path: '/workspace/rocket', - repoIcon: { - type: 'image', - src: 'https://github.com/acme.png?size=64', - source: 'github', - label: 'acme/rocket' - } - }) - ]) - getRepoUpstream.mockResolvedValue({ owner: 'upstream-org', repo: 'rocket' }) - getRepoSlug.mockResolvedValue({ owner: 'acme', repo: 'rocket' }) - - await (runtime as unknown as BackfillInternals).repositoryForkBackfill.run() - - expect(updateRepo).toHaveBeenCalledExactlyOnceWith(repoTarget('repo-1'), { - upstream: { owner: 'upstream-org', repo: 'rocket' }, - repoIcon: { - type: 'image', - src: 'https://github.com/upstream-org.png?size=64', - source: 'github', - label: 'upstream-org/rocket' - } - }) - }) - it('skips every row whose files sit on an SSH host', async () => { // Unified remote ownership, including nested runtime targets, must not read this client's same-path files. const runtime = new OrcaRuntimeService() diff --git a/src/main/runtime/rpc/methods/orchestration/worker/worker-observation.test.ts b/src/main/runtime/rpc/methods/orchestration/worker/worker-observation.test.ts index 1bf76e0cce8..39dce2bcc4b 100644 --- a/src/main/runtime/rpc/methods/orchestration/worker/worker-observation.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/worker/worker-observation.test.ts @@ -1,9 +1,10 @@ import { describe, expect, it, vi } from 'vitest' import type { OrcaRuntimeService } from '../../../../orca-runtime' import type { OrchestrationDb } from '../../../../orchestration/db' -import { exposeWorker, inspectWorkerTerminal } from './worker-observation' +import { inspectWorkerTerminal } from './worker-observation' + import { exposeDispatchContext } from './dispatch-context-exposure' -import type { DispatchContextRow, WorkerDispatchRow } from '../../../../orchestration/types' +import type { DispatchContextRow } from '../../../../orchestration/types' const DISPATCH_ID = 'ctx-worker' const TERMINAL_HANDLE = 'term-worker' @@ -28,18 +29,6 @@ function createHarness(args: { } describe('inspectWorkerTerminal missing liveness verdict', () => { - it('keeps a connected local worker live', async () => { - const { runtime, db } = createHarness({ - connected: true, - hostScope: { kind: 'local', hostId: 'local' } - }) - - await expect(inspectWorkerTerminal(runtime, db, DISPATCH_ID)).resolves.toMatchObject({ - exact: true, - status: 'live' - }) - }) - it('keeps a disconnected local worker exited', async () => { const { runtime, db } = createHarness({ connected: false, @@ -67,40 +56,6 @@ describe('inspectWorkerTerminal missing liveness verdict', () => { }) describe('worker-show receipt shape', () => { - it('parses the JSON columns once and emits one casing', () => { - const exposed = exposeWorker({ - dispatch_id: DISPATCH_ID, - runtime_epoch: 'epoch-1', - state: 'ready', - stage: 'input_accepted', - worktree_id: 'repo::/tmp/wt', - agent_terminal_handle: TERMINAL_HANDLE, - setup_state: 'ran', - effects: '[{"kind":"setup"}]', - residual_resources: '["res-1"]', - start_options: '{"agent":"codex"}', - last_error: null, - created_at: 'now', - updated_at: 'now' - } as WorkerDispatchRow) - - expect(exposed).toEqual({ - dispatchId: DISPATCH_ID, - runtimeEpoch: 'epoch-1', - state: 'ready', - stage: 'input_accepted', - worktreeId: 'repo::/tmp/wt', - agentTerminalHandle: TERMINAL_HANDLE, - setupState: 'ran', - effects: [{ kind: 'setup' }], - residualResources: ['res-1'], - startOptions: { agent: 'codex' }, - lastError: null, - createdAt: 'now', - updatedAt: 'now' - }) - }) - it('parses host_scope and withholds authority hashes from the dispatch row', () => { const exposed = exposeDispatchContext({ id: DISPATCH_ID, @@ -123,25 +78,4 @@ describe('worker-show receipt shape', () => { // Retain only the legacy dispatch spelling older paired CLIs still print beside camelCase worker. expect(Object.keys(exposed).filter((key) => key.includes('_'))).toEqual(['task_id']) }) - - // Mixed-version CLIs still print these legacy fields (cli/handlers/orchestration/worker-observation-handlers.ts). - it('keeps every field an older paired CLI prints', () => { - const dispatch = exposeDispatchContext({ - id: DISPATCH_ID, - run_id: 'run-1', - task_id: 'task-1', - status: 'dispatched' - } as DispatchContextRow) - - expect(dispatch).toMatchObject({ id: DISPATCH_ID, task_id: 'task-1', status: 'dispatched' }) - expect( - exposeWorker({ - state: 'ready', - stage: 'input_accepted', - effects: '[]', - residual_resources: '[]', - start_options: '{}' - } as WorkerDispatchRow) - ).toMatchObject({ state: 'ready', stage: 'input_accepted' }) - }) }) diff --git a/src/main/runtime/rpc/methods/terminal-create-idempotency.test.ts b/src/main/runtime/rpc/methods/terminal-create-idempotency.test.ts index e9f8741e869..7ebf8e3f8ca 100644 --- a/src/main/runtime/rpc/methods/terminal-create-idempotency.test.ts +++ b/src/main/runtime/rpc/methods/terminal-create-idempotency.test.ts @@ -3,67 +3,6 @@ import { eraseRpcMethods, type RpcContext } from '../core' import { TERMINAL_METHODS } from './terminal' describe('terminal.create RPC idempotency', () => { - it('scopes the mutation key to the authenticated paired device and worktree', async () => { - const terminal = { handle: 'terminal-1', worktreeId: 'worktree-1', title: null } - const createTerminal = vi.fn(async () => terminal) - const dedupeTerminalCreate = vi.fn( - async ( - _clientIdentity: string, - _worktree: string | undefined, - _mutationId: string | undefined, - _reconcileExisting: boolean, - run: (worktree: string | undefined, handle: string | undefined) => Promise - ) => run('id:worktree-1', 'term_stable') - ) - const method = eraseRpcMethods(TERMINAL_METHODS).find( - (candidate) => candidate.name === 'terminal.create' - ) - if (!method) { - throw new Error('terminal.create method missing') - } - - const result = await method.handler( - { - worktree: 'id:worktree-1', - clientMutationId: 'mutation-1', - command: 'pwsh', - resumeProviderSession: { - key: 'session_id', - id: 'session-1', - transcriptPath: 'C:\\Users\\example\\.codex\\sessions\\rollout.jsonl' - } - }, - { - runtime: { createTerminal, dedupeTerminalCreate }, - pairedDeviceId: 'device-a', - clientId: 'bearer-token' - } as unknown as RpcContext, - vi.fn() - ) - - expect(dedupeTerminalCreate).toHaveBeenCalledWith( - 'device-a', - 'id:worktree-1', - 'mutation-1', - false, - expect.any(Function), - { wslUserAct: true } - ) - expect(createTerminal).toHaveBeenCalledWith( - 'id:worktree-1', - expect.objectContaining({ - command: 'pwsh', - preAllocatedHandle: 'term_stable', - resumeProviderSession: { - key: 'session_id', - id: 'session-1', - transcriptPath: 'C:\\Users\\example\\.codex\\sessions\\rollout.jsonl' - } - }) - ) - expect(result).toEqual({ terminal }) - }) - it('does not let a paired focused create navigate the host by default', async () => { const terminal = { handle: 'terminal-focused', worktreeId: 'worktree-1', title: null } const createTerminal = vi.fn(async () => terminal) diff --git a/src/main/runtime/rpc/methods/worktree-create-foreign-automation-provenance.test.ts b/src/main/runtime/rpc/methods/worktree-create-foreign-automation-provenance.test.ts index c92046aaf3b..58daf9a6135 100644 --- a/src/main/runtime/rpc/methods/worktree-create-foreign-automation-provenance.test.ts +++ b/src/main/runtime/rpc/methods/worktree-create-foreign-automation-provenance.test.ts @@ -55,22 +55,6 @@ const notFound = (): never => { // Why: only the desktop that owns a run can verify its dispatch token (#34433). describe('worktree.create for another owner’s automation run', () => { - it('creates a plain workspace for a dispatch token it cannot verify', async () => { - const { response, createManagedWorktree } = createOnHost(notFound, { - automationProvenanceRequest: { - automationId: 'desktop-automation', - automationRunId: 'desktop-run', - dispatchToken: 'desktop-token', - createRequestId: 'create-request-desktop' - } - }) - - await expect(response).resolves.toMatchObject({ ok: true }) - expect(createManagedWorktree).toHaveBeenCalledWith( - expect.not.objectContaining({ automationProvenance: expect.anything() }) - ) - }) - // Invariant: a client-origin label never grants ownership, sleep authority or create policy here. it('records the owner’s run as a client-origin label without automation create policy', async () => { const { response, createManagedWorktree } = createOnHost(notFound, { @@ -103,32 +87,6 @@ describe('worktree.create for another owner’s automation run', () => { ) }) - it('drops an oversized label and still creates the workspace', async () => { - // Why fresh modules: the warning is once per process, so this create must be the first to drop one. - vi.resetModules() - const { WORKTREE_METHODS: freshMethods } = await import('./worktree') - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - const { response, createManagedWorktree } = createOnHost( - notFound, - { - clientAutomationProvenance: { - ...CLIENT_PROVENANCE, - automationNameSnapshot: 'x'.repeat(1025) - } - }, - freshMethods - ) - - await expect(response).resolves.toMatchObject({ ok: true }) - expect(createManagedWorktree).toHaveBeenCalledWith( - expect.not.objectContaining({ automationProvenance: expect.anything() }) - ) - expect(warn).toHaveBeenCalledWith( - expect.stringContaining('dropped a malformed client automation label') - ) - warn.mockRestore() - }) - it('surfaces an automation store failure', async () => { const { response, createManagedWorktree } = createOnHost( () => { diff --git a/src/main/runtime/rpc/terminal-multiplex-pty-wait-queue.test.ts b/src/main/runtime/rpc/terminal-multiplex-pty-wait-queue.test.ts index 88b0f516662..1df21d03c38 100644 --- a/src/main/runtime/rpc/terminal-multiplex-pty-wait-queue.test.ts +++ b/src/main/runtime/rpc/terminal-multiplex-pty-wait-queue.test.ts @@ -10,7 +10,7 @@ import { TERMINAL_MULTIPLEX_MAX_PENDING_PTY_WAITS_PER_CONNECTION, TERMINAL_MULTIPLEX_MAX_QUEUED_PTY_WAITS_PER_CONNECTION } from '../../../shared/terminal-multiplex-flow-control' -import { createMultiplexPtyWaitAdmission } from './methods/terminal/terminal-multiplex-pty-wait-admission' + import { startDesktopMultiplexSubscribe } from './terminal-multiplex-test-harness' function startPendingPtyHarness() { @@ -122,20 +122,3 @@ describe('terminal multiplex PTY wait queue', () => { await harness.dispatchPromise }) }) - -describe('createMultiplexPtyWaitAdmission', () => { - it('admits FIFO, skips aborted waiters, and reports room against both bounds', async () => { - const admission = createMultiplexPtyWaitAdmission(1, 2) - const first = await admission.acquire(new AbortController().signal) - expect(first).toBe(true) - const aborted = new AbortController() - const second = admission.acquire(aborted.signal) - const third = admission.acquire(new AbortController().signal) - expect(admission.hasRoom()).toBe(false) - aborted.abort() - await expect(second).resolves.toBe(false) - expect(admission.hasRoom()).toBe(true) - admission.release() - await expect(third).resolves.toBe(true) - }) -}) diff --git a/src/main/runtime/structured-agent-launch-support-probe.test.ts b/src/main/runtime/structured-agent-launch-support-probe.test.ts index 847df2c3a65..3cf5ab71e68 100644 --- a/src/main/runtime/structured-agent-launch-support-probe.test.ts +++ b/src/main/runtime/structured-agent-launch-support-probe.test.ts @@ -96,22 +96,6 @@ describe.skipIf(process.platform === 'win32')( expect((await launchWith(settings)).command).toBe(join(privateBin, 'opencode')) }) - it('keeps a refused release first on PATH on the terminal chat when nothing points elsewhere', async () => { - const settings = { ...base, agentDefaultEnv: {} } - expect( - await structuredAgentSupportsLaunch('opencode', workspace, runtimeWith(settings)) - ).toBe(false) - }) - - it('runs a stable 2.x first on PATH in the structured chat', async () => { - await fakeOpencode(shellBin, '2.0.21') - const settings = { ...base, agentDefaultEnv: {} } - expect( - await structuredAgentSupportsLaunch('opencode', workspace, runtimeWith(settings)) - ).toBe(true) - expect((await launchWith(settings)).command).toBe(join(shellBin, 'opencode')) - }) - it('checks and launches the binary the Command setting names', async () => { const settings = { ...base, diff --git a/src/main/skills/discovery-filter-sharing.test.ts b/src/main/skills/discovery-filter-sharing.test.ts index 14fe9faf580..017134ff3af 100644 --- a/src/main/skills/discovery-filter-sharing.test.ts +++ b/src/main/skills/discovery-filter-sharing.test.ts @@ -62,25 +62,6 @@ it('shares one root read across six concurrent name filters', async () => { }) }) -it('reuses the raw snapshot for a new name and invalidates it on mutation', async () => { - await fixture(async (root) => { - const args = { homeDir: root, repos: [], includeCwd: false, sourceKinds: ['home' as const] } - observed.opens = 0 - await repair.discoverSkills({ ...args, names: ['skill-0'] }) - const next = await repair.discoverSkills({ ...args, names: ['skill-47'] }) - expect(next.skills.map((skill) => skill.name)).toEqual(['skill-47']) - expect(observed.opens).toBe(48) - await writeFile( - join(root, '.agents', 'skills', 'skill-47', 'SKILL.md'), - '---\nname: renamed\n---\n' - ) - repair.clearSkillRootScanCache() - const updated = await repair.discoverSkills({ ...args, names: ['renamed'] }) - expect(updated.skills.map((skill) => skill.name)).toEqual(['renamed']) - expect(observed.opens).toBe(96) - }) -}) - it('retains a newly requested name when its previously observed root becomes unavailable', async () => { await fixture(async (root) => { const args = { homeDir: root, repos: [], includeCwd: false, sourceKinds: ['home' as const] } @@ -108,21 +89,6 @@ it('retains a newly requested name when its previously observed root becomes una }) }) -it('keeps simultaneous forced refreshes independent', async () => { - await fixture(async (root) => { - const args = { homeDir: root, repos: [], includeCwd: false, sourceKinds: ['home' as const] } - await repair.discoverSkills({ ...args, names: ['skill-0'] }) - observed.opens = 0 - const results = await Promise.all( - [0, 1].map((index) => - repair.discoverSkills({ ...args, names: [`skill-${index}`], refresh: true }) - ) - ) - expect(results.map((result) => result.skills[0]?.name)).toEqual(['skill-0', 'skill-1']) - expect(observed.opens).toBe(96) - }) -}) - it('filters aliases before deduplication so excluded bundled roots cannot own home results', async () => { await fixture(async (root) => { const bundled = join(root, '.codex', 'skills', '.system', 'bundle') diff --git a/src/main/ssh/orcad-running-version-adoption.test.ts b/src/main/ssh/orcad-running-version-adoption.test.ts deleted file mode 100644 index 2a0256dee0d..00000000000 --- a/src/main/ssh/orcad-running-version-adoption.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { OrcadActivationRecord } from '../managed-server/activation/orcad-activation-record' -import type { OrcadSlotOptions } from './orcad-recovery-slot' -import { getRemoteHostPlatform } from './ssh-remote-platform' - -const mocks = vi.hoisted(() => ({ - wake: vi.fn(), - readRecord: vi.fn(), - probe: vi.fn(), - launch: vi.fn(), - fenceRefusal: vi.fn() -})) - -vi.mock('../managed-server/activation/orcad-activation-fence-hold', () => ({ - orcadActivationFenceRefusal: mocks.fenceRefusal -})) - -vi.mock('../managed-server/lifecycle/orcad-managed-wake', () => ({ - wakeStoppedManagedOrcad: mocks.wake -})) -vi.mock('../managed-server/activation/orcad-activation-record-store', () => ({ - readOrcadActivationRecord: mocks.readRecord -})) -vi.mock('../managed-server/install/orcad-remote-build-hash', () => ({ - readRemoteOrcadBuildHash: async () => 'hash' -})) -vi.mock('./orcad-active-readiness', async (importOriginal) => ({ - ...(await importOriginal>()), - probeActiveOrcadReadiness: mocks.probe, - launchOrcadSlotAndAwaitReadiness: mocks.launch -})) - -const { adoptRunningOrcadVersion } = await import('./orcad-running-version-adoption') - -const LOCAL = '1.5.0+new' -const RUNNING = '1.4.0+old' -const READINESS = { runtimeId: 'r' } - -// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: every remote step is mocked, so only the paths are read. -const slot = { - host: getRemoteHostPlatform('linux-x64'), - remoteHome: '/home/u', - userDataDir: '/home/u/.orca' -} as OrcadSlotOptions - -const record = (active: string | null): OrcadActivationRecord => ({ - schemaVersion: 1, - active, - previous: null, - activatedAt: null, - snapshot: null -}) - -beforeEach(() => { - vi.resetAllMocks() - mocks.readRecord.mockResolvedValue(record(RUNNING)) - mocks.probe.mockResolvedValue(READINESS) -}) - -describe('adoptRunningOrcadVersion', () => { - it('leaves an empty host, or one on this build, to the deploy', async () => { - await expect(adoptRunningOrcadVersion(slot, record(null), LOCAL)).resolves.toBeNull() - await expect(adoptRunningOrcadVersion(slot, record(LOCAL), LOCAL)).resolves.toBeNull() - expect(mocks.wake).not.toHaveBeenCalled() - }) - - it('adopts the serving build, proven against its own installed bytes', async () => { - mocks.wake.mockResolvedValue({ outcome: 'serving' }) - await expect(adoptRunningOrcadVersion(slot, record(RUNNING), LOCAL)).resolves.toMatchObject({ - version: RUNNING, - readiness: READINESS - }) - expect(mocks.probe).toHaveBeenCalledWith( - expect.objectContaining({ remoteInstallDir: expect.stringContaining(RUNNING) }), - { buildHash: 'hash', fullVersion: RUNNING } - ) - expect(mocks.launch).not.toHaveBeenCalled() - }) - - it('adopts a stopped build the wake restarted', async () => { - mocks.wake.mockResolvedValue({ outcome: 'started', readiness: READINESS }) - await expect(adoptRunningOrcadVersion(slot, record(RUNNING), LOCAL)).resolves.toMatchObject({ - version: RUNNING - }) - }) - - it('defers to the deploy when the host was stopped, or moved to this build meanwhile', async () => { - mocks.wake.mockResolvedValueOnce({ outcome: 'not-activated' }) - await expect(adoptRunningOrcadVersion(slot, record(RUNNING), LOCAL)).resolves.toBeNull() - mocks.wake.mockResolvedValueOnce({ outcome: 'serving' }) - mocks.readRecord.mockResolvedValueOnce(record(LOCAL)) - await expect(adoptRunningOrcadVersion(slot, record(RUNNING), LOCAL)).resolves.toBeNull() - expect(mocks.probe).not.toHaveBeenCalled() - }) - - // #28833: peer startup must defer adoption; an unpaired deploy cannot census the running host. - it('waits on a fence another run holds instead of deploying over an uncounted host', async () => { - mocks.wake.mockResolvedValueOnce({ outcome: 'fenced' }) - mocks.fenceRefusal.mockResolvedValueOnce({ - code: 'orcad_activation_fence_busy', - reason: 'busy' - }) - await expect(adoptRunningOrcadVersion(slot, record(RUNNING), LOCAL)).resolves.toEqual({ - outcome: 'deferred', - candidateVersion: LOCAL, - code: 'orcad_activation_fence_busy', - reason: 'busy', - forceable: false - }) - expect(mocks.probe).not.toHaveBeenCalled() - }) - - it('retries the wake once after clearing an abandoned fence', async () => { - mocks.wake.mockResolvedValueOnce({ outcome: 'fenced' }) - mocks.fenceRefusal.mockResolvedValueOnce({ code: 'c', reason: 'cleared', cleared: true }) - mocks.wake.mockResolvedValueOnce({ outcome: 'started', readiness: READINESS }) - await expect(adoptRunningOrcadVersion(slot, record(RUNNING), LOCAL)).resolves.toMatchObject({ - version: RUNNING - }) - mocks.wake.mockResolvedValue({ outcome: 'fenced' }) - mocks.fenceRefusal.mockResolvedValue({ code: 'c', reason: 'cleared', cleared: true }) - await expect(adoptRunningOrcadVersion(slot, record(RUNNING), LOCAL)).resolves.toMatchObject({ - outcome: 'deferred' - }) - expect(mocks.wake).toHaveBeenCalledTimes(4) - }) - - it('reports an unverifiable or unrecoverable host without touching it', async () => { - mocks.wake.mockResolvedValueOnce({ outcome: 'unverifiable' }) - await expect(adoptRunningOrcadVersion(slot, record(RUNNING), LOCAL)).rejects.toMatchObject({ - verdict: 'unverifiable' - }) - mocks.wake.mockResolvedValueOnce({ outcome: 'recovery-refused', reason: 'Recover it first.' }) - await expect(adoptRunningOrcadVersion(slot, record(RUNNING), LOCAL)).rejects.toThrow( - 'Recover it first.' - ) - expect(mocks.launch).not.toHaveBeenCalled() - }) -}) diff --git a/src/main/tray/tray-attention-icon.test.ts b/src/main/tray/tray-attention-icon.test.ts index efb81a03e61..cb92eb09da9 100644 --- a/src/main/tray/tray-attention-icon.test.ts +++ b/src/main/tray/tray-attention-icon.test.ts @@ -31,23 +31,6 @@ function fakeBase(width: number, height: number): FakeImage { const AMBER = { b: 0x0b, g: 0x9e, r: 0xf5 } describe('composeTrayAttentionIcon', () => { - it('returns the base unchanged when it has no pixels', () => { - const base = { getSize: () => ({ width: 0, height: 0 }), toBitmap: () => Buffer.alloc(0) } - - expect(composeTrayAttentionIcon(base as never)).toBe(base) - expect(createFromBitmapMock).not.toHaveBeenCalled() - }) - - it('builds a new image of the same size', () => { - createFromBitmapMock.mockClear() - const result = composeTrayAttentionIcon(fakeBase(16, 16) as never) - - expect(createFromBitmapMock).toHaveBeenCalledTimes(1) - const [, options] = createFromBitmapMock.mock.calls[0] - expect(options).toEqual({ width: 16, height: 16 }) - expect(result).toMatchObject({ __image: true, width: 16, height: 16 }) - }) - it('paints an amber dot in the top-right corner and leaves the rest untouched', () => { createFromBitmapMock.mockClear() const width = 16 @@ -86,33 +69,6 @@ describe('composeTrayAttentionIcon', () => { }) describe('tintTrayTemplateForAttention', () => { - it('preserves alpha while selecting literal black pixels for a light menu bar', () => { - createFromBitmapMock.mockClear() - const bitmap = Buffer.from([0x22, 0x33, 0x44, 0x80, 0xaa, 0xbb, 0xcc, 0x00]) - const base = { - getSize: () => ({ width: 2, height: 1 }), - toBitmap: () => bitmap - } - - tintTrayTemplateForAttention(base as never, false) - - expect(createFromBitmapMock.mock.calls[0][0]).toEqual( - Buffer.from([0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00]) - ) - }) - - it('selects literal white pixels for a dark menu bar', () => { - createFromBitmapMock.mockClear() - const base = { - getSize: () => ({ width: 1, height: 1 }), - toBitmap: () => Buffer.from([0x00, 0x00, 0x00, 0xff]) - } - - tintTrayTemplateForAttention(base as never, true) - - expect(createFromBitmapMock.mock.calls[0][0]).toEqual(Buffer.from([0xff, 0xff, 0xff, 0xff])) - }) - it('keeps white edge pixels premultiplied-valid at partial alpha', () => { createFromBitmapMock.mockClear() const base = { @@ -125,18 +81,4 @@ describe('tintTrayTemplateForAttention', () => { // Premultiplied alpha requires 0x80 channels for half-covered white, not 0xff. expect(createFromBitmapMock.mock.calls[0][0]).toEqual(Buffer.from([0x80, 0x80, 0x80, 0x80])) }) - - it('reads and sizes the requested scale factor', () => { - createFromBitmapMock.mockClear() - const toBitmap = vi.fn(() => Buffer.from([0x00, 0x00, 0x00, 0xff])) - const base = { - getSize: () => ({ width: 1, height: 1 }), - toBitmap - } - - tintTrayTemplateForAttention(base as never, true, 2) - - expect(toBitmap).toHaveBeenCalledWith({ scaleFactor: 2 }) - expect(createFromBitmapMock.mock.calls[0][1]).toEqual({ width: 2, height: 2 }) - }) }) diff --git a/src/main/updater.linux-externally-managed.test.ts b/src/main/updater.linux-externally-managed.test.ts index ec05ad0e48c..0237fb68463 100644 --- a/src/main/updater.linux-externally-managed.test.ts +++ b/src/main/updater.linux-externally-managed.test.ts @@ -8,7 +8,6 @@ const { autoUpdaterMock, getLinuxRootPackageTypeMock, isExternallyManagedLinuxInstallMock, - recordUpdaterLifecycleMock, fetchNewerReleaseTagsMock, moduleFactories, resetUpdaterMocks @@ -64,29 +63,6 @@ describe('updater externally managed Linux installs', () => { return send.mock.calls.findLast(([channel]) => channel === 'updater:status')?.[1] } - it('still reports the available release so the user can update through their distribution', async () => { - const { send, updater } = await startUpdater({ packageType: 'deb', externallyManaged: true }) - updater.checkForUpdatesFromMenu() - await vi.advanceTimersByTimeAsync(0) - - expect(lastStatus(send)).toEqual({ - state: 'available', - version: '1.0.61', - changelog: null, - externallyManaged: true - }) - }) - - it('does not flag a real deb host', async () => { - const { send, updater } = await startUpdater({ packageType: 'deb', externallyManaged: false }) - updater.checkForUpdatesFromMenu() - await vi.advanceTimersByTimeAsync(0) - - const status = lastStatus(send) - expect(status).toEqual({ state: 'available', version: '1.0.61', changelog: null }) - expect(status && 'externallyManaged' in status).toBe(false) - }) - it('refuses the download instead of spending it on a package it can never install', async () => { const { send, updater } = await startUpdater({ packageType: 'deb', externallyManaged: true }) updater.checkForUpdatesFromMenu() @@ -105,19 +81,6 @@ describe('updater externally managed Linux installs', () => { }) }) - it('records the blocked download for field diagnosis', async () => { - const { updater } = await startUpdater({ packageType: 'deb', externallyManaged: true }) - updater.checkForUpdatesFromMenu() - await vi.advanceTimersByTimeAsync(0) - updater.downloadUpdate() - await vi.advanceTimersByTimeAsync(0) - - expect(recordUpdaterLifecycleMock).toHaveBeenCalledWith( - 'linux_package_externally_managed_download_blocked', - { version: '1.0.61' } - ) - }) - it('leaves an ordinary deb host able to download', async () => { const { updater } = await startUpdater({ packageType: 'deb', externallyManaged: false }) updater.checkForUpdatesFromMenu() @@ -129,16 +92,4 @@ describe('updater externally managed Linux installs', () => { expect(autoUpdaterMock.downloadUpdate).toHaveBeenCalled() }) - - it('leaves an AppImage host able to download', async () => { - const { updater } = await startUpdater({ packageType: null, externallyManaged: false }) - updater.checkForUpdatesFromMenu() - await vi.advanceTimersByTimeAsync(0) - autoUpdaterMock.downloadUpdate.mockResolvedValue([]) - - updater.downloadUpdate() - await vi.advanceTimersByTimeAsync(0) - - expect(autoUpdaterMock.downloadUpdate).toHaveBeenCalled() - }) }) diff --git a/src/main/window/terminal-tab-close-request-relay.test.ts b/src/main/window/terminal-tab-close-request-relay.test.ts index 7f2c9213125..fbf5ed7c9c4 100644 --- a/src/main/window/terminal-tab-close-request-relay.test.ts +++ b/src/main/window/terminal-tab-close-request-relay.test.ts @@ -60,25 +60,6 @@ describe('requestTerminalTabCloseFromRenderer', () => { await expect(pending).resolves.toBeUndefined() }) - it('propagates renderer cancellation instead of reporting success', async () => { - const { requestTerminalTabCloseFromRenderer } = - await import('./terminal-tab-close-request-relay') - const webContents = { isDestroyed: () => false, send: vi.fn() } - const pending = requestTerminalTabCloseFromRenderer( - { isDestroyed: () => false, webContents } as never, - 'tab-pinned' - ) - const request = webContents.send.mock.calls[0]?.[1] as { requestId: string } - - ipcEmitter.emit( - 'ui:terminalTabCloseResponse', - { sender: webContents }, - { requestId: request.requestId, error: 'terminal_tab_pinned' } - ) - - await expect(pending).rejects.toThrow('terminal_tab_pinned') - }) - it('rejects and cleans up when the BrowserWindow closes and webContents becomes unavailable', async () => { const { requestTerminalTabCloseFromRenderer } = await import('./terminal-tab-close-request-relay') diff --git a/src/main/wsl/wsl-guest-pane-processes.test.ts b/src/main/wsl/wsl-guest-pane-processes.test.ts index c38553ff9ef..5b96d3251b9 100644 --- a/src/main/wsl/wsl-guest-pane-processes.test.ts +++ b/src/main/wsl/wsl-guest-pane-processes.test.ts @@ -73,35 +73,6 @@ describe('the guest process table', () => { expect(readWslGuestPaneShellCwd(twoRoots ?? [], KEY)).toBeNull() }) - it('never starts a distro that is not running', async () => { - isWslDistroRunning.mockResolvedValue(false) - expect(await readWslGuestPaneProcesses('Ubuntu', 3_000)).toBeNull() - expect(runWslProcess).not.toHaveBeenCalled() - }) - - it('skips the running check for a live pane whose distro was last seen running, settled', async () => { - wasWslDistroLastSeenRunningSettled.mockReturnValue(true) - runWslProcess.mockResolvedValue({ - environmentResolved: true, - code: 0, - stdout: fenced(), - stderr: '', - timedOut: false - }) - expect(await readWslGuestPaneProcesses('Ubuntu', 3_000, { running: true })).toEqual([]) - expect(wasWslDistroLastSeenRunningSettled).toHaveBeenCalledWith('Ubuntu') - expect(isWslDistroRunning).not.toHaveBeenCalled() - }) - - // Review of #33398: a session the client still holds as live may be in a distro terminated since. - it('still checks a live pane whose distro was not last seen running after any stop settled', async () => { - wasWslDistroLastSeenRunningSettled.mockReturnValue(false) - isWslDistroRunning.mockResolvedValue(false) - expect(await readWslGuestPaneProcesses('Ubuntu', 3_000, { running: true })).toBeNull() - expect(isWslDistroRunning).toHaveBeenCalledWith('Ubuntu') - expect(runWslProcess).not.toHaveBeenCalled() - }) - it('runs one fixed script with no login shell, shared by callers asking at once', async () => { runWslProcess.mockResolvedValue({ environmentResolved: true, @@ -125,17 +96,4 @@ describe('the guest process table', () => { }) ) }) - - it('answers null when the read fails or times out', async () => { - runWslProcess.mockResolvedValue({ - environmentResolved: true, - code: null, - stdout: '', - stderr: '', - timedOut: true - }) - expect(await readWslGuestPaneProcesses('Ubuntu', 3_000)).toBeNull() - runWslProcess.mockRejectedValue(new Error('wsl.exe missing')) - expect(await readWslGuestPaneProcesses('Ubuntu', 3_000)).toBeNull() - }) }) diff --git a/src/main/wsl/wsl-orcad-update-admission.test.ts b/src/main/wsl/wsl-orcad-update-admission.test.ts index 21a01fab5f5..91ae20b0f95 100644 --- a/src/main/wsl/wsl-orcad-update-admission.test.ts +++ b/src/main/wsl/wsl-orcad-update-admission.test.ts @@ -1,12 +1,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -import { ORCAD_HOST_NEWER_DEFERRAL_CODE } from '../../shared/wsl-host-types' + import type { OrcadActivationRecord } from '../managed-server/activation/orcad-activation-record' import { recordOrcadUpdateFailedThisLaunch } from '../managed-server/lifecycle/orcad-managed-update-failure-memory' import type { ServeReadiness } from '../server/serve-readiness' -import { ORCAD_ACTIVATION_POLICY_REFUSED_CODE } from '../ssh/orcad-installed-activation' + import type { OrcadSlotOptions } from '../ssh/orcad-recovery-slot' import { getRemoteHostPlatform } from '../ssh/ssh-remote-platform' -import { admitWslOrcadUpdate, wslOrcadDeferralCode } from './wsl-orcad-update-admission' +import { admitWslOrcadUpdate } from './wsl-orcad-update-admission' const { adoptRunningOrcadVersion, readOrcadActivationRecord } = vi.hoisted(() => ({ adoptRunningOrcadVersion: vi.fn(), @@ -86,38 +86,11 @@ describe('WSL orcad admission across desktop and host versions', () => { } expect(adoptRunningOrcadVersion).toHaveBeenCalledWith(slot, running(NEWER, '1.6.0'), OLDER) }) - - it('deploys over it only when the user forces the update', async () => { - expect(await admit(running(NEWER, '1.6.0'), olderDesktop, { intent: 'force' })).toEqual({ - action: 'deploy', - record: running(NEWER, '1.6.0') - }) - expect(adoptRunningOrcadVersion).not.toHaveBeenCalled() - }) - - it('names a policy refusal over the newer host, which Update cannot pass', async () => { - readOrcadActivationRecord.mockResolvedValue(running(NEWER, '1.6.0')) - expect( - await wslOrcadDeferralCode( - slot, - { code: ORCAD_ACTIVATION_POLICY_REFUSED_CODE, fullVersion: OLDER }, - '1.5.0' - ) - ).toBe(ORCAD_HOST_NEWER_DEFERRAL_CODE) - }) }) describe('a newer desktop meets the orcad an older desktop deployed', () => { const newerDesktop = { candidateVersion: NEWER, appVersion: '1.6.0' } - it('updates it on connect', async () => { - expect(await admit(running(OLDER, '1.5.0'), newerDesktop)).toEqual({ - action: 'deploy', - record: running(OLDER, '1.5.0') - }) - expect(adoptRunningOrcadVersion).not.toHaveBeenCalled() - }) - it('keeps the older orcad serving after a failed update, and offers Force', async () => { const host = { kind: 'wsl' as const, id: 'Ubuntu-failed' } recordOrcadUpdateFailedThisLaunch(host, { code: 'orcad_activation_failed', reason: 'boom' }) @@ -134,10 +107,5 @@ describe('WSL orcad admission across desktop and host versions', () => { } }) }) - - it('treats an orcad from before app provenance as updatable, not as newer', async () => { - const { activeAppVersion: _omitted, ...legacy } = running(OLDER, '1.5.0') - expect(await admit(legacy, newerDesktop)).toEqual({ action: 'deploy', record: legacy }) - }) }) }) diff --git a/src/preload/browser-extension-inert-apis.test.ts b/src/preload/browser-extension-inert-apis.test.ts index 01c03e3e34d..f54dba1de67 100644 --- a/src/preload/browser-extension-inert-apis.test.ts +++ b/src/preload/browser-extension-inert-apis.test.ts @@ -64,77 +64,6 @@ describe('installBrowserExtensionInertApis', () => { } }) - it('leaves storage alone when the extension has no storage permission', () => { - const realm = { chrome: { runtime: { getManifest: () => ({}) } } } - runInNewContext(`(${installBrowserExtensionInertApis.toString()})('page')`, { - globalThis: realm, - Promise - }) - expect(member(realm.chrome, 'storage')).toBeUndefined() - }) - - it('gives content scripts chrome.extension.inIncognitoContext and dom.openOrClosedShadowRoot', () => { - const { chrome } = install('content-script') - expect(member(chrome, 'extension.inIncognitoContext')).toBe(false) - const shadowRoot = { mode: 'open' } - expect(call(chrome, 'dom.openOrClosedShadowRoot', { shadowRoot })).toBe(shadowRoot) - expect(call(chrome, 'dom.openOrClosedShadowRoot', { shadowRoot: null })).toBeNull() - expect(member(chrome, 'extension.getViews')).toBeUndefined() - expect(member(chrome, 'runtime.onPerformanceWarning')).toBeUndefined() - }) - - it('adds getViews to pages only, and inert worker and page APIs on chrome and browser', async () => { - const page = install('page', {}, { extension: { getBackgroundPage: () => null } }) - expect(call(page.chrome, 'extension.getViews', { type: 'popup' })).toEqual([]) - expect(typeof member(page.chrome, 'extension.getBackgroundPage')).toBe('function') - - const { chrome, browser } = install('worker', {}, { permissions: {} }) - expect(member(chrome, 'extension.getViews')).toBeUndefined() - expect(member(chrome, 'dom')).toBeUndefined() - for (const api of [chrome, browser]) { - const onWarning = member(api, 'runtime.onPerformanceWarning') - const listener = vi.fn() - call(api, 'runtime.onPerformanceWarning.addListener', listener) - expect(call(api, 'runtime.onPerformanceWarning.hasListener', listener)).toBe(true) - expect(onWarning).toBe(member(chrome, 'runtime.onPerformanceWarning')) - await expect(call(api, 'permissions.addHostAccessRequest', { tabId: 1 })).resolves.toBe( - undefined - ) - await expect(call(api, 'permissions.removeHostAccessRequest', { tabId: 1 })).resolves.toBe( - undefined - ) - } - }) - - it('installs omnibox for a manifest omnibox key and fontSettings for its permission', async () => { - const without = install('worker') - expect(without.chrome.omnibox).toBeUndefined() - expect(without.chrome.fontSettings).toBeUndefined() - - const { chrome } = install('page', { - omnibox: { keyword: 'lp' }, - permissions: ['fontSettings'] - }) - expect(typeof member(chrome, 'omnibox.onInputEntered.addListener')).toBe('function') - expect(member(chrome, 'omnibox.OnInputEnteredDisposition.NEW_FOREGROUND_TAB')).toBe( - 'newForegroundTab' - ) - const suggested = await new Promise((resolve) => - call(chrome, 'omnibox.setDefaultSuggestion', { description: 'Search' }, () => - resolve('called') - ) - ) - expect(suggested).toBe('called') - const fonts = await new Promise((resolve) => call(chrome, 'fontSettings.getFontList', resolve)) - expect(fonts).toContainEqual({ fontId: 'sans-serif', displayName: 'sans-serif' }) - await expect( - call(chrome, 'fontSettings.getFont', { genericFamily: 'standard' }) - ).resolves.toEqual({ fontId: '', levelOfControl: 'not_controllable' }) - await expect( - call(chrome, 'fontSettings.setFont', { genericFamily: 'standard', fontId: 'serif' }) - ).resolves.toBeUndefined() - }) - it('keeps members Electron already provides', () => { const native = vi.fn() const { chrome } = install('page', {}, { extension: { getViews: native } }) diff --git a/src/renderer/src/components/contextual-tours/contextual-tour-step-actions.test.ts b/src/renderer/src/components/contextual-tours/contextual-tour-step-actions.test.ts deleted file mode 100644 index 4c5e4f8972b..00000000000 --- a/src/renderer/src/components/contextual-tours/contextual-tour-step-actions.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { performContextualTourStepAction } from './contextual-tour-step-actions' - -describe('performContextualTourStepAction', () => { - it('opens Tasks after detaching the terminal-owned tour source', () => { - const finishTour = vi.fn() - const advanceContextualTour = vi.fn() - const detachContextualTourSource = vi.fn() - const openTaskPage = vi.fn() - - performContextualTourStepAction({ - action: { kind: 'open-tasks', label: 'Show tasks' }, - activeTabId: 'tab-1', - isLastStep: false, - finishTour, - advanceContextualTour, - detachContextualTourSource, - setSidebarOpen: vi.fn(), - openTaskPage, - openModal: vi.fn(), - openClientHostedBrowserSettings: vi.fn(), - openWorkspaceComposer: vi.fn(), - dispatchTerminalPaneSplit: vi.fn(), - schedule: vi.fn() - }) - - expect(detachContextualTourSource).toHaveBeenCalledTimes(1) - expect(openTaskPage).toHaveBeenCalledTimes(1) - expect(advanceContextualTour).toHaveBeenCalledTimes(1) - expect(finishTour).not.toHaveBeenCalled() - }) - - it('dispatches the terminal-pane split action against the active tab', () => { - const dispatchTerminalPaneSplit = vi.fn() - - performContextualTourStepAction({ - action: { kind: 'split-terminal-pane', label: 'Split terminal' }, - activeTabId: 'tab-1', - isLastStep: false, - finishTour: vi.fn(), - advanceContextualTour: vi.fn(), - detachContextualTourSource: vi.fn(), - setSidebarOpen: vi.fn(), - openTaskPage: vi.fn(), - openModal: vi.fn(), - openClientHostedBrowserSettings: vi.fn(), - openWorkspaceComposer: vi.fn(), - dispatchTerminalPaneSplit, - schedule: vi.fn() - }) - - expect(dispatchTerminalPaneSplit).toHaveBeenCalledWith({ - tabId: 'tab-1', - direction: 'vertical' - }) - }) - - it('opens the workspace composer after detaching, without advancing the tour itself', () => { - const detachContextualTourSource = vi.fn() - const openWorkspaceComposer = vi.fn() - const advanceContextualTour = vi.fn() - const finishTour = vi.fn() - - performContextualTourStepAction({ - action: { kind: 'create-worktree', label: 'Create worktree' }, - activeTabId: 'tab-1', - isLastStep: true, - finishTour, - advanceContextualTour, - detachContextualTourSource, - setSidebarOpen: vi.fn(), - openTaskPage: vi.fn(), - openModal: vi.fn(), - openClientHostedBrowserSettings: vi.fn(), - openWorkspaceComposer, - dispatchTerminalPaneSplit: vi.fn(), - schedule: vi.fn() - }) - - // Opening the composer cancels this tour and hands off, so we neither - // advance nor finish it here. - expect(detachContextualTourSource).toHaveBeenCalledTimes(1) - expect(openWorkspaceComposer).toHaveBeenCalledTimes(1) - expect(advanceContextualTour).not.toHaveBeenCalled() - expect(finishTour).not.toHaveBeenCalled() - }) - - it('opens the workspace composer with no projects so the first one can be added there', () => { - const detachContextualTourSource = vi.fn() - const openWorkspaceComposer = vi.fn() - - performContextualTourStepAction({ - action: { kind: 'create-worktree', label: 'Create worktree' }, - activeTabId: 'tab-1', - isLastStep: true, - finishTour: vi.fn(), - advanceContextualTour: vi.fn(), - detachContextualTourSource, - setSidebarOpen: vi.fn(), - openTaskPage: vi.fn(), - openModal: vi.fn(), - openClientHostedBrowserSettings: vi.fn(), - openWorkspaceComposer, - dispatchTerminalPaneSplit: vi.fn(), - schedule: vi.fn() - }) - - expect(detachContextualTourSource).toHaveBeenCalledTimes(1) - expect(openWorkspaceComposer).toHaveBeenCalledTimes(1) - }) - - it('finishes the tour before opening the client-hosted browser settings', () => { - const finishTour = vi.fn() - const openClientHostedBrowserSettings = vi.fn() - const scheduled: (() => void)[] = [] - - performContextualTourStepAction({ - action: { kind: 'open-client-hosted-browser-settings', label: 'Browser settings' }, - activeTabId: 'tab-1', - isLastStep: true, - finishTour, - advanceContextualTour: vi.fn(), - detachContextualTourSource: vi.fn(), - setSidebarOpen: vi.fn(), - openTaskPage: vi.fn(), - openModal: vi.fn(), - openClientHostedBrowserSettings, - openWorkspaceComposer: vi.fn(), - dispatchTerminalPaneSplit: vi.fn(), - schedule: (callback) => { - scheduled.push(callback) - } - }) - - expect(finishTour).toHaveBeenCalledTimes(1) - expect(openClientHostedBrowserSettings).not.toHaveBeenCalled() - for (const callback of scheduled) { - callback() - } - expect(openClientHostedBrowserSettings).toHaveBeenCalledTimes(1) - }) -}) diff --git a/src/renderer/src/components/contextual-tours/workspace-creation-tour-handoff.test.ts b/src/renderer/src/components/contextual-tours/workspace-creation-tour-handoff.test.ts deleted file mode 100644 index cdc151e9ef5..00000000000 --- a/src/renderer/src/components/contextual-tours/workspace-creation-tour-handoff.test.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { useAppStore } from '@/store' -import { openWorkspaceCreationComposerWithTourHandoff } from './workspace-creation-tour-handoff' -import { requestContextualTourWhenReady } from './request-contextual-tour-when-ready' - -vi.mock('./request-contextual-tour-when-ready', () => ({ - requestContextualTourWhenReady: vi.fn() -})) - -describe('openWorkspaceCreationComposerWithTourHandoff', () => { - afterEach(() => { - vi.clearAllMocks() - vi.restoreAllMocks() - }) - - it('opens the workspace composer without a tour handoff outside the agent sessions tour', () => { - const openModal = vi.fn() - const detachContextualTourSource = vi.fn() - vi.spyOn(useAppStore, 'getState').mockImplementation( - () => - ({ - activeContextualTourId: null, - activeContextualTourStepIndex: 0, - activeContextualTourSource: null, - activeModal: 'none', - contextualToursSeenIds: [], - repos: [{ id: 'repo-1' }], - detachContextualTourSource, - openModal - }) as unknown as ReturnType - ) - - openWorkspaceCreationComposerWithTourHandoff() - - expect(openModal).toHaveBeenCalledWith('new-workspace-composer', { - telemetrySource: 'sidebar' - }) - expect(detachContextualTourSource).not.toHaveBeenCalled() - expect(requestContextualTourWhenReady).not.toHaveBeenCalled() - }) - - it('hands off from the agent sessions create-worktree step to the workspace-creation tour', () => { - const openModal = vi.fn() - const detachContextualTourSource = vi.fn() - const completeContextualTour = vi.fn() - let activeModal: ReturnType['activeModal'] = 'none' - vi.spyOn(useAppStore, 'getState').mockImplementation( - () => - ({ - activeContextualTourId: 'workspace-agent-sessions', - activeContextualTourStepIndex: 1, - activeContextualTourSource: 'setup_guide_parallel_work', - activeModal, - contextualToursSeenIds: [], - repos: [{ id: 'repo-1' }], - detachContextualTourSource, - completeContextualTour, - openModal - }) as unknown as ReturnType - ) - - openWorkspaceCreationComposerWithTourHandoff() - - expect(detachContextualTourSource).toHaveBeenCalledWith( - 'workspace-agent-sessions', - 'setup_guide_parallel_work' - ) - expect(completeContextualTour).toHaveBeenCalledWith('workspace-agent-sessions') - expect(openModal).toHaveBeenCalledWith('new-workspace-composer', { - telemetrySource: 'sidebar', - contextualTourSource: 'workspace_creation_modal' - }) - expect(requestContextualTourWhenReady).toHaveBeenCalledWith({ - id: 'workspace-creation', - source: 'workspace_creation_modal', - wasFeaturePreviouslyInteracted: false, - waitForActiveTourToClear: true, - shouldContinue: expect.any(Function) - }) - - const [{ shouldContinue }] = vi.mocked(requestContextualTourWhenReady).mock.calls[0] - activeModal = 'new-workspace-composer' - expect(shouldContinue?.()).toBe(true) - activeModal = 'none' - expect(shouldContinue?.()).toBe(false) - }) - - it('opens the composer without re-showing workspace creation when that tour was already seen', () => { - const openModal = vi.fn() - const detachContextualTourSource = vi.fn() - const completeContextualTour = vi.fn() - vi.spyOn(useAppStore, 'getState').mockImplementation( - () => - ({ - activeContextualTourId: 'workspace-agent-sessions', - activeContextualTourStepIndex: 1, - activeContextualTourSource: 'setup_guide_parallel_work', - activeModal: 'none', - contextualToursSeenIds: ['workspace-creation'], - repos: [{ id: 'repo-1' }], - detachContextualTourSource, - completeContextualTour, - openModal - }) as unknown as ReturnType - ) - - openWorkspaceCreationComposerWithTourHandoff() - - expect(detachContextualTourSource).toHaveBeenCalledWith( - 'workspace-agent-sessions', - 'setup_guide_parallel_work' - ) - expect(completeContextualTour).toHaveBeenCalledWith('workspace-agent-sessions') - expect(openModal).toHaveBeenCalledWith('new-workspace-composer', { - telemetrySource: 'sidebar', - contextualTourSource: 'workspace_creation_modal' - }) - expect(requestContextualTourWhenReady).not.toHaveBeenCalled() - }) - - it('opens the composer without a project so the empty form can guide setup', () => { - const openModal = vi.fn() - const detachContextualTourSource = vi.fn() - vi.spyOn(useAppStore, 'getState').mockImplementation( - () => - ({ - activeContextualTourId: 'workspace-agent-sessions', - activeContextualTourStepIndex: 1, - activeContextualTourSource: 'setup_guide_parallel_work', - activeModal: 'none', - contextualToursSeenIds: [], - repos: [], - detachContextualTourSource, - openModal - }) as unknown as ReturnType - ) - - openWorkspaceCreationComposerWithTourHandoff() - - expect(detachContextualTourSource).not.toHaveBeenCalled() - expect(openModal).toHaveBeenCalledWith('new-workspace-composer', { - telemetrySource: 'sidebar' - }) - expect(requestContextualTourWhenReady).not.toHaveBeenCalled() - }) -}) diff --git a/src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree-windowing.test.tsx b/src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree-windowing.test.tsx index 669d1cc041a..407ab024f61 100644 --- a/src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree-windowing.test.tsx +++ b/src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree-windowing.test.tsx @@ -3,7 +3,7 @@ import React, { act } from 'react' import { createRoot, type Root } from 'react-dom/client' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { VIRTUALIZED_LIST_MIN_ROWS } from '@/components/virtualized-list' + import type { GitBranchChangeEntry } from '../../../../../../shared/git-diff-compare-types' import type { CombinedDiffFileTreeRow as CombinedDiffFileTreeRowComponent } from './combined-diff-file-tree-row' @@ -117,18 +117,6 @@ function renderTree(entries: readonly GitBranchChangeEntry[]): void { } describe('combined diff file tree row windowing', () => { - it('mounts every row below the virtualize threshold', () => { - const fileCount = VIRTUALIZED_LIST_MIN_ROWS - 10 - const directoryCount = 4 - renderTree(buildEntries(fileCount, directoryCount)) - - const totalRows = 1 + directoryCount + fileCount - expect(totalRows).toBeLessThan(VIRTUALIZED_LIST_MIN_ROWS) - expect(mountedRows.count).toBe(totalRows) - expect(host.querySelector('[data-testid="virtualized-list"]')).toBeNull() - expect(host.querySelectorAll('[data-index]').length).toBe(0) - }) - it('mounts only a window of rows for a large review', () => { const fileCount = 900 const directoryCount = 30 diff --git a/src/renderer/src/components/editor/rich-markdown-image-insert-code-block.test.ts b/src/renderer/src/components/editor/rich-markdown-image-insert-code-block.test.ts index 6b96d4e1dac..7ffde07ac02 100644 --- a/src/renderer/src/components/editor/rich-markdown-image-insert-code-block.test.ts +++ b/src/renderer/src/components/editor/rich-markdown-image-insert-code-block.test.ts @@ -7,7 +7,6 @@ import { createRichMarkdownExtensions } from './rich-markdown-extensions' import { createRichMarkdownEditorCodec } from './rich-markdown-source-transport' import { useLocalImagePick } from './useLocalImagePick' import { handleRichMarkdownImagePaste } from './rich-markdown-paste-image' -import { runSlashCommand, slashCommands } from './rich-markdown-slash-commands' vi.mock('@/runtime/runtime-file-import-client', () => ({ importExternalPathsToRuntime: vi.fn().mockResolvedValue({ @@ -102,20 +101,6 @@ describe('inserting an image while the cursor is inside a fenced code block', () expectFencedSplit(editor) }) - it('keeps both halves fenced when the slash command inserts the image', async () => { - const imageCommand = slashCommands.find((command) => command.id === 'image') - expect(imageCommand).toBeDefined() - const { result } = renderHook(() => useLocalImagePick(editor as never, '/repo/note.md', 'wt-1')) - const from = editor.state.selection.from - - runSlashCommand(editor as never, { from, to: from }, imageCommand!, () => { - void result.current() - }) - await flushPromises() - - expectFencedSplit(editor) - }) - it('keeps both halves fenced when a clipboard screenshot is pasted', async () => { const handled = handleRichMarkdownImagePaste({ editor: editor as never, @@ -131,16 +116,4 @@ describe('inserting an image while the cursor is inside a fenced code block', () expect(handled).toBe(true) expectFencedSplit(editor) }) - - it('still inserts the image inline when the cursor is in ordinary prose', async () => { - editor.destroy() - editor = mountRichMarkdownEditor('Install the extension\n') - editor.commands.setTextSelection(13) - const { result } = renderHook(() => useLocalImagePick(editor as never, '/repo/note.md', 'wt-1')) - - await result.current() - await flushPromises() - - expect(editor.getMarkdown().trimEnd()).toBe('Install the ![](shot.png)extension') - }) }) diff --git a/src/renderer/src/components/editor/useDiffViewerLargeDiffLifecycle.test.tsx b/src/renderer/src/components/editor/useDiffViewerLargeDiffLifecycle.test.tsx index a12ab4b3463..b31ce8cd26e 100644 --- a/src/renderer/src/components/editor/useDiffViewerLargeDiffLifecycle.test.tsx +++ b/src/renderer/src/components/editor/useDiffViewerLargeDiffLifecycle.test.tsx @@ -52,54 +52,6 @@ function diffEditorFixture( } describe('useDiffViewerLargeDiffLifecycle', () => { - it('keeps repeated content rotations bounded to the current Monaco models', async () => { - const modelKey = 'diff-tab' - const originalModelKey = 'original-v1' - const onEnterFallback = vi.fn() - const paths = ['modified-v1', 'modified-v2', 'modified-v3'].map((modifiedModelKey) => - getDiffViewerMonacoModelPaths({ - modelKey, - originalModelKey, - modifiedModelKey, - generationSuffix: '' - }) - ) - const firstModel = detachedModel() - const secondModel = detachedModel() - const currentModel = detachedModel() - const originalModel = detachedModel() - monacoFixture.models.set(paths[0].originalModelPath, originalModel) - monacoFixture.models.set(paths[0].modifiedModelPath, firstModel) - monacoFixture.models.set(paths[1].modifiedModelPath, secondModel) - monacoFixture.models.set(paths[2].modifiedModelPath, currentModel) - const retainedEditor = diffEditorFixture(originalModel, firstModel) - - const hook = renderHook( - ({ modifiedModelKey }) => - useDiffViewerLargeDiffLifecycle({ - limited: false, - modelKey, - originalModelKey, - modifiedModelKey, - diffEditorRef: retainedEditor, - onEnterFallback - }), - { initialProps: { modifiedModelKey: 'modified-v1' } } - ) - - hook.rerender({ modifiedModelKey: 'modified-v2' }) - await act(() => Promise.resolve()) - hook.rerender({ modifiedModelKey: 'modified-v3' }) - await act(() => Promise.resolve()) - - expect(firstModel.dispose).toHaveBeenCalledOnce() - expect(secondModel.dispose).toHaveBeenCalledOnce() - expect(currentModel.dispose).not.toHaveBeenCalled() - expect(retainedEditor.setModel).toHaveBeenCalledTimes(2) - expect(hook.result.current).toEqual(paths[2]) - expect(onEnterFallback).not.toHaveBeenCalled() - }) - it('resets the owning diff widget before disposing a superseded model', async () => { const modelKey = 'diff-tab' const paths = ['modified-v1', 'modified-v2'].map((modifiedModelKey) => diff --git a/src/renderer/src/components/native-chat/native-chat-launch-default-adoption.test.ts b/src/renderer/src/components/native-chat/native-chat-launch-default-adoption.test.ts index a41c797c695..9c5793fef0d 100644 --- a/src/renderer/src/components/native-chat/native-chat-launch-default-adoption.test.ts +++ b/src/renderer/src/components/native-chat/native-chat-launch-default-adoption.test.ts @@ -61,24 +61,6 @@ describe('native chat launch-default adoption', () => { expect(resolveNativeChatSessionOptionDefaults(defaults.read(), 'claude')).toBeUndefined() }) - it('does not adopt an unlisted launch-flag model before discovery either', async () => { - // A tracked flag proves emission, not that a catalog recognizes the model. - seedNativeChatAppliedSessionOptions('pty-1', 'claude', { model: 'claude-opus-5' }) - const defaults = claudeLaunchDefaults() - const surface = createNativeChatPtySessionOptions({ - agent: 'claude', - scopeKey: 'pty-1', - mode: 'live', - modelLabel: 'Model', - dispatchCommand: vi.fn().mockResolvedValue({ outcome: 'applied' }), - persistSelection: defaults.persistSelection - })! - - await surface.setOption('model', 'claude-opus-5') - - expect(defaults.read().claude?.model).toBeUndefined() - }) - it('still adopts a model only the host catalog lists', async () => { seedNativeChatAppliedSessionOptions('pty-1', 'claude', { model: 'sonnet' }) const defaults = claudeLaunchDefaults() @@ -119,21 +101,4 @@ describe('native chat launch-default adoption', () => { effort: 'high' }) }) - - it('still adopts a tracked seed model before any discovery', async () => { - seedNativeChatAppliedSessionOptions('pty-1', 'claude', { model: 'opus', effort: 'xhigh' }) - const defaults = claudeLaunchDefaults() - const surface = createNativeChatPtySessionOptions({ - agent: 'claude', - scopeKey: 'pty-1', - mode: 'live', - modelLabel: 'Model', - dispatchCommand: vi.fn(), - persistSelection: defaults.persistSelection - })! - - await surface.setOption('effort', 'high') - - expect(defaults.read().claude?.model).toBe('opus') - }) }) diff --git a/src/renderer/src/components/native-chat/use-structured-agent-session-hold.test.tsx b/src/renderer/src/components/native-chat/use-structured-agent-session-hold.test.tsx index 38edc665e3e..8ce52d67e5d 100644 --- a/src/renderer/src/components/native-chat/use-structured-agent-session-hold.test.tsx +++ b/src/renderer/src/components/native-chat/use-structured-agent-session-hold.test.tsx @@ -26,29 +26,6 @@ beforeEach(() => { }) describe('a mounted structured chat', () => { - it('holds the session while it is on screen and releases it on unmount', async () => { - const { unmount } = renderHook(() => - useStructuredAgentSessionHold({ - sessionId: 'session-alpha', - target: LOCAL_TARGET, - surface: 'desktop-chat' - }) - ) - - await waitFor(() => expect(callsTo('agentSession.hold')).toHaveLength(1)) - const held = callsTo('agentSession.hold')[0] as { sessionId: string; holderId: string } - expect(held.sessionId).toBe('session-alpha') - expect(callsTo('agentSession.release')).toHaveLength(0) - - unmount() - - await waitFor(() => - expect(callsTo('agentSession.release')).toEqual([ - { sessionId: 'session-alpha', holderId: held.holderId } - ]) - ) - }) - it('does not release a hold that has not landed yet', async () => { let settleHold = (): void => {} mocks.call.mockImplementation((_target: unknown, method: string) => @@ -76,26 +53,6 @@ describe('a mounted structured chat', () => { await waitFor(() => expect(callsTo('agentSession.release')).toHaveLength(1)) }) - it('reports a hold the host refused and still releases on unmount', async () => { - const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) - const refused = new Error('structured_agent_session_unsupported') - mocks.call.mockImplementation((_target: unknown, method: string) => - method === 'agentSession.hold' ? Promise.reject(refused) : Promise.resolve() - ) - const { unmount } = renderHook(() => - useStructuredAgentSessionHold({ - sessionId: 'session-alpha', - target: LOCAL_TARGET, - surface: 'desktop-chat' - }) - ) - - await waitFor(() => expect(warn).toHaveBeenCalledWith(expect.any(String), refused)) - unmount() - await waitFor(() => expect(callsTo('agentSession.release')).toHaveLength(1)) - warn.mockRestore() - }) - it('keeps one hold across re-renders that rebuild the target object', async () => { const { rerender, unmount } = renderHook( (props: { sessionId: string }) => diff --git a/src/renderer/src/components/native-chat/use-structured-native-chat-submit-reveal.test.ts b/src/renderer/src/components/native-chat/use-structured-native-chat-submit-reveal.test.ts index 502161ab2db..69a1f1dbce2 100644 --- a/src/renderer/src/components/native-chat/use-structured-native-chat-submit-reveal.test.ts +++ b/src/renderer/src/components/native-chat/use-structured-native-chat-submit-reveal.test.ts @@ -12,46 +12,6 @@ vi.mock('@/runtime/runtime-rpc-client', () => ({ callRuntimeRpc: permission.settings })) -it('reveals before launch Retry, but waits for a successful queue Resume', async () => { - const order: string[] = [] - const controller = { - capturePresentation: () => () => true, - respond: vi.fn(async () => null), - queuedMessages: { - queueCapable: true, - clearCapable: true, - cards: [], - pause: null, - clear: vi.fn(async () => true), - queuePending: null, - pending: () => null, - resume: vi.fn(async () => { - order.push('resume') - return true - }), - steer: vi.fn(async () => {}), - remove: vi.fn(async () => {}), - edit: vi.fn(async () => {}), - editCapable: false, - editor: undefined, - steerHead: vi.fn(() => false), - isWorking: false - } - } - const retryLaunch = (): number => order.push('launch') - const { result } = renderHook(() => useStructuredNativeChatSubmitReveal(controller, retryLaunch)) - result.current.messageListRef.current = { - revealLatest: () => order.push('reveal'), - revealFindMatch: () => {}, - messageCopyText: () => null - } - - act(() => result.current.retryLaunch()) - await act(() => result.current.queuedMessages.resume()) - - expect(order).toEqual(['reveal', 'launch', 'resume', 'reveal']) -}) - // STA-10671: an older host held for its permission mode would run a queued card unprompted. it('sends no queued card and retries no start on a host held for its permission mode', async () => { const queuedMessages = { diff --git a/src/renderer/src/components/repo/CreateFromPicker.test.tsx b/src/renderer/src/components/repo/CreateFromPicker.test.tsx deleted file mode 100644 index 5ab814cf80f..00000000000 --- a/src/renderer/src/components/repo/CreateFromPicker.test.tsx +++ /dev/null @@ -1,141 +0,0 @@ -// @vitest-environment happy-dom - -import React, { act } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { Repo } from '../../../../shared/repo-types' -import { CreateFromPicker } from './CreateFromPicker' -import { TooltipProvider } from '@/components/ui/tooltip' -import { - getRuntimeRepoBaseRefDefault, - searchRuntimeRepoBaseRefs -} from '@/runtime/runtime-repo-client' - -vi.mock('@/components/ui/popover', () => ({ - // Why: the real popover owns open state; expose onOpenChange so a test can open the list. - Popover: ({ - children, - onOpenChange - }: { - children: React.ReactNode - onOpenChange?: (open: boolean) => void - }) => ( -
-
- ), - PopoverContent: ({ children }: { children: React.ReactNode }) =>
{children}
, - PopoverTrigger: ({ children }: { children: React.ReactNode }) => <>{children} -})) - -vi.mock('@/components/ui/command', () => ({ - Command: ({ children }: { children: React.ReactNode }) =>
{children}
, - CommandEmpty: ({ children }: { children: React.ReactNode }) =>
{children}
, - CommandInput: () => , - CommandItem: ({ children }: { children: React.ReactNode }) => , - CommandList: ({ children }: { children: React.ReactNode }) =>
{children}
-})) - -const storeState = { - settings: { activeRuntimeEnvironmentId: 'focused-runtime' }, - repos: [] as Pick[] -} - -vi.mock('@/store', () => ({ - useAppStore: (selector: (state: typeof storeState) => unknown) => selector(storeState) -})) - -vi.mock('@/runtime/runtime-repo-client', () => ({ - getRuntimeRepoBaseRefDefault: vi.fn().mockResolvedValue({ - defaultBaseRef: 'main', - remoteCount: 1 - }), - searchRuntimeRepoBaseRefs: vi.fn().mockResolvedValue([]) -})) - -let container: HTMLDivElement -let root: Root - -function repoMapFor(repo: Repo): Map { - return new Map([[repo.id, repo]]) -} - -function makeRepo(overrides: Partial): Repo { - return { - id: 'repo-1', - path: '/repo', - displayName: 'Repo', - badgeColor: '#000000', - addedAt: 1, - ...overrides - } -} - -async function renderPicker(repo: Repo): Promise { - await act(async () => { - root.render( - - - - ) - }) -} - -describe('CreateFromPicker host routing', () => { - beforeEach(() => { - container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) - vi.clearAllMocks() - }) - - afterEach(() => { - act(() => { - root.unmount() - }) - container.remove() - storeState.repos = [] - }) - - it('uses the selected runtime-owned repo host instead of the focused runtime', async () => { - const repo = makeRepo({ executionHostId: 'runtime:owner-runtime' }) - storeState.repos = [repo] - - await renderPicker(repo) - - expect(getRuntimeRepoBaseRefDefault).toHaveBeenCalledWith('runtime:owner-runtime', repo.id) - }) - - // Empty queries must list all repo branches instead of only defaults/worktree branches. - it('lists the repo branches as soon as it opens, with no query typed', async () => { - const repo = makeRepo({ executionHostId: 'local' }) - storeState.repos = [repo] - - await renderPicker(repo) - await act(async () => { - container.querySelector('[data-testid="open-popover"]')?.click() - }) - await act(async () => { - await new Promise((resolve) => setTimeout(resolve, 250)) - }) - - expect(searchRuntimeRepoBaseRefs).toHaveBeenCalledWith('local', repo.id, '', 30) - }) - - it('keeps an explicit local repo on the local client even when a runtime is focused', async () => { - const repo = makeRepo({ executionHostId: 'local' }) - storeState.repos = [repo] - - await renderPicker(repo) - - expect(getRuntimeRepoBaseRefDefault).toHaveBeenCalledWith('local', repo.id) - expect(searchRuntimeRepoBaseRefs).not.toHaveBeenCalled() - }) -}) diff --git a/src/renderer/src/components/settings/McpConfigSection.host-owner.test.tsx b/src/renderer/src/components/settings/McpConfigSection.host-owner.test.tsx index 947be2a5418..85bdc8fb539 100644 --- a/src/renderer/src/components/settings/McpConfigSection.host-owner.test.tsx +++ b/src/renderer/src/components/settings/McpConfigSection.host-owner.test.tsx @@ -7,7 +7,6 @@ import type { Repo } from '../../../../shared/repo-types' import { makeWorktree } from '@/store/slices/worktrees-slice-test-fixtures' import { useAppStore } from '@/store' import { McpConfigSection } from './McpConfigSection' -import { toast } from 'sonner' const runtimeFiles = vi.hoisted(() => ({ readRuntimeDirectory: vi.fn(), @@ -105,24 +104,6 @@ it('inspects and creates a managed-host repo config on that host, never the desk expect(desktopFs.writeFile).not.toHaveBeenCalled() }) -it('keeps a desktop repo on the desktop while a managed host is focused', async () => { - const desktopRepo: Repo = { ...repo, executionHostId: 'local' } - useAppStore.setState({ - settings: { ...getDefaultSettings('/tmp'), activeRuntimeEnvironmentId: 'host-a' }, - repos: [desktopRepo], - worktreesByRepo: { - repo: [makeWorktree({ id: worktreeId, repoId: 'repo', path: root, isMainWorktree: true })] - } - }) - render() - - await waitFor(() => expect(runtimeFiles.readRuntimeDirectory).toHaveBeenCalled()) - expect(runtimeFiles.readRuntimeDirectory).toHaveBeenCalledWith( - expect.objectContaining({ target: { kind: 'local' } }), - root - ) -}) - it('does not offer the starter over a listed config the host refuses to read', async () => { runtimeFiles.readRuntimeDirectory.mockResolvedValue([{ name: '.mcp.json', isDirectory: false }]) runtimeFiles.readRuntimeFileContent.mockRejectedValue(new Error('Remote file is too large')) @@ -131,14 +112,3 @@ it('does not offer the starter over a listed config the host refuses to read', a await screen.findByText('Remote file is too large') expect(screen.queryByRole('button', { name: /Add MCP config/ })).toBeNull() }) - -it('does not replace a config another process created after the listing', async () => { - runtimeFiles.writeRuntimeFile.mockResolvedValue({ ok: false, diskConflict: 'changed' }) - render() - - fireEvent.click(await screen.findByRole('button', { name: /Add MCP config/ })) - fireEvent.click(await screen.findByRole('button', { name: /Create empty config/ })) - - await waitFor(() => expect(toast.error).toHaveBeenCalled()) - expect(toast.success).not.toHaveBeenCalled() -}) diff --git a/src/renderer/src/components/settings/use-settings-repo-scroll-effects.test.tsx b/src/renderer/src/components/settings/use-settings-repo-scroll-effects.test.tsx index 75ea0ac4a2a..36c09b73766 100644 --- a/src/renderer/src/components/settings/use-settings-repo-scroll-effects.test.tsx +++ b/src/renderer/src/components/settings/use-settings-repo-scroll-effects.test.tsx @@ -70,23 +70,6 @@ describe('settings orca.yaml inspection', () => { // An unmounted hook must drop its retry listeners, or they fire into the next case. afterEach(cleanup) - it('asks in the background and keeps a stopped distro checking, not "missing"', async () => { - checkRuntimeHooksMock.mockResolvedValue({ - status: 'error', - hasHooks: false, - hooks: null, - mayNeedUpdate: false, - unavailable: 'wsl-distro-stopped' - }) - const { map } = renderEffects() - - await waitFor(() => expect(checkRuntimeHooksMock).toHaveBeenCalled()) - // Let the resolved check settle into state before asserting nothing was recorded. - await act(async () => {}) - expect(checkRuntimeHooksMock).toHaveBeenCalledWith('local', 'repo-1', { background: true }) - expect(map()).toEqual({}) - }) - it('re-asks when the window comes back and records the distro once it runs', async () => { const hooks = { scripts: { setup: 'pnpm i' } } checkRuntimeHooksMock @@ -134,11 +117,4 @@ describe('settings orca.yaml inspection', () => { vi.useRealTimers() } }) - - it('records the inspection once the distro answers', async () => { - checkRuntimeHooksMock.mockResolvedValue({ hasHooks: true, hooks: null, mayNeedUpdate: false }) - const { map } = renderEffects() - - await waitFor(() => expect(Object.keys(map())).toHaveLength(1)) - }) }) diff --git a/src/renderer/src/components/sidebar/project-header-drag-commit.test.ts b/src/renderer/src/components/sidebar/project-header-drag-commit.test.ts index 28a3882909e..f84dc98e0e2 100644 --- a/src/renderer/src/components/sidebar/project-header-drag-commit.test.ts +++ b/src/renderer/src/components/sidebar/project-header-drag-commit.test.ts @@ -36,25 +36,6 @@ function makeSession( } describe('commitProjectHeaderDragDrop', () => { - it('commits whole-repo reordering when project groups are absent', () => { - const onCommitRepoOrder = vi.fn() - const repos = [makeRepo('a'), makeRepo('b'), makeRepo('c')] - const repoById = new Map(repos.map((repo) => [repo.id, repo])) - - commitProjectHeaderDragDrop({ - session: makeSession(makeRepo('c'), ['a', 'b', 'c']), - sidebarDropIndex: 0, - orderedRepoIds: ['a', 'b', 'c'], - repoById, - liveRepos: repos, - usesProjectGroupOrdering: false, - onCommitRepoOrder, - onCommitProjectGroupOrder: vi.fn() - }) - - expect(onCommitRepoOrder).toHaveBeenCalledWith(['c', 'a', 'b']) - }) - it('moves a merged paired-host header upward as one stable block', () => { const onCommitRepoOrder = vi.fn() const repos = [makeRepo('b'), makeRepo('same'), makeRepo('c')] @@ -74,25 +55,6 @@ describe('commitProjectHeaderDragDrop', () => { expect(onCommitRepoOrder).toHaveBeenCalledWith(['same', 'same', 'b', 'c']) }) - it('does not reorder host occurrences when a merged header stays in place', () => { - const onCommitRepoOrder = vi.fn() - const repos = [makeRepo('b'), makeRepo('same'), makeRepo('c')] - const repoById = new Map(repos.map((repo) => [repo.id, repo])) - - commitProjectHeaderDragDrop({ - session: makeSession(makeRepo('same'), ['b', 'same', 'c']), - sidebarDropIndex: 2, - orderedRepoIds: ['b', 'same', 'c', 'same'], - repoById, - liveRepos: repos, - usesProjectGroupOrdering: false, - onCommitRepoOrder, - onCommitProjectGroupOrder: vi.fn() - }) - - expect(onCommitRepoOrder).not.toHaveBeenCalled() - }) - it('commits projectGroupOrder for the dragged row, not the id-keyed twin on another host', () => { const onCommitProjectGroupOrder = vi.fn() const repos = [ diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/use-project-group-dialogs-owner-host.test.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/use-project-group-dialogs-owner-host.test.tsx deleted file mode 100644 index 9472719ba58..00000000000 --- a/src/renderer/src/components/sidebar/worktree-list/rows/use-project-group-dialogs-owner-host.test.tsx +++ /dev/null @@ -1,142 +0,0 @@ -// @vitest-environment happy-dom - -import { act } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { useProjectGroupDialogs, type ProjectGroupDialogs } from './use-project-group-dialogs' -import type { ProjectGroup } from '../../../../../../shared/project-group-types' - -const mocks = vi.hoisted(() => ({ - moveProjectToGroup: vi.fn(), - createProjectGroup: vi.fn(), - updateProjectGroup: vi.fn(), - deleteProjectGroupWithContainedProjects: vi.fn(), - confirmUnsavedDiscard: vi.fn(), - toastError: vi.fn() -})) - -vi.mock('@/store', () => ({ - useAppStore: (selector: (state: Record) => unknown) => - selector({ - moveProjectToGroup: mocks.moveProjectToGroup, - createProjectGroup: mocks.createProjectGroup, - updateProjectGroup: mocks.updateProjectGroup, - deleteProjectGroupWithContainedProjects: mocks.deleteProjectGroupWithContainedProjects - }) -})) - -vi.mock('@/store/slices/worktrees/teardown/removed-workspace-unsaved-files', () => ({ - confirmUnsavedDiscard: mocks.confirmUnsavedDiscard -})) - -vi.mock('sonner', () => ({ - toast: { error: mocks.toastError } -})) - -const remoteGroup: ProjectGroup = { - id: 'group-1', - name: 'Projects', - parentPath: '/srv/projects', - parentGroupId: null, - createdFrom: 'folder-scan', - executionHostId: 'runtime:env-1', - tabOrder: 0, - isCollapsed: false, - color: null, - createdAt: 1, - updatedAt: 1 -} - -let latest: ProjectGroupDialogs | null = null -const roots: Root[] = [] - -function HookProbe(): null { - latest = useProjectGroupDialogs({ - repos: [], - repoMap: new Map(), - projectGroups: [remoteGroup] - }) - return null -} - -async function renderHookProbe(): Promise { - const container = document.createElement('div') - document.body.appendChild(container) - const root = createRoot(container) - roots.push(root) - await act(async () => { - root.render() - }) -} - -beforeEach(() => { - vi.clearAllMocks() - mocks.updateProjectGroup.mockResolvedValue(true) - mocks.deleteProjectGroupWithContainedProjects.mockResolvedValue({ - status: 'deleted-group', - groupId: remoteGroup.id, - requestedProjectIds: [], - removedProjectIds: [], - failedProjectRemovals: [] - }) -}) - -afterEach(async () => { - await act(async () => { - for (const root of roots.splice(0)) { - root.unmount() - } - }) - latest = null -}) - -describe('project group dialogs carry the owner host', () => { - it('renames through the host that owns the group row', async () => { - await renderHookProbe() - await act(async () => { - latest!.handleRenameProjectGroup(remoteGroup.id, remoteGroup.name, 'runtime:env-1') - }) - await act(async () => { - await latest!.handleSubmitProjectGroupName('Renamed') - }) - - expect(mocks.updateProjectGroup).toHaveBeenCalledWith( - remoteGroup.id, - { name: 'Renamed' }, - { hostId: 'runtime:env-1' } - ) - expect(mocks.toastError).not.toHaveBeenCalled() - }) - - it('surfaces an unconfirmed-rename toast when the owner host does not answer', async () => { - mocks.updateProjectGroup.mockResolvedValue(false) - await renderHookProbe() - await act(async () => { - latest!.handleRenameProjectGroup(remoteGroup.id, remoteGroup.name, 'runtime:env-1') - }) - await act(async () => { - await latest!.handleSubmitProjectGroupName('Renamed') - }) - - expect(mocks.toastError).toHaveBeenCalledWith('Failed to rename group', { - description: - "Orca could not confirm the new name with the group's host. Recheck the group after reconnecting." - }) - }) - - it('deletes through the host that owns the group row', async () => { - await renderHookProbe() - await act(async () => { - latest!.handleDeleteProjectGroup(remoteGroup.id, remoteGroup.name, 'runtime:env-1') - }) - await act(async () => { - await latest!.handleConfirmDeleteProjectGroup() - }) - - expect(mocks.deleteProjectGroupWithContainedProjects).toHaveBeenCalledWith(remoteGroup.id, { - removeContainedProjects: false, - hostId: 'runtime:env-1' - }) - expect(mocks.confirmUnsavedDiscard).toHaveBeenCalledTimes(1) - }) -}) diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/visible-refresh.test.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/visible-refresh.test.ts index 91b7c5d413a..7ad21064442 100644 --- a/src/renderer/src/components/sidebar/worktree-list/viewport/visible-refresh.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/visible-refresh.test.ts @@ -1,42 +1,9 @@ import type { VirtualItem } from '@tanstack/react-virtual' import type { WorktreeItemRow } from '../listing/renderable-rows' import { makePRRefreshWorktree } from '@/store/slices/github-slice-test-harness' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { - installWorktreeVisibleRefreshVisibilityListener, - visibleReviewCardIds, - installVisibleReviewCardScrollListener -} from './use-visible-review-refresh' +import { describe, expect, it } from 'vitest' -describe('installWorktreeVisibleRefreshVisibilityListener', () => { - beforeEach(() => { - vi.restoreAllMocks() - vi.unstubAllGlobals() - }) - - it('subscribes to document visibility changes so visible PR refresh can rerun on return', () => { - const listeners = new Map void>() - const onChange = vi.fn() - const addEventListener = vi.fn((event: string, listener: () => void) => { - listeners.set(event, listener) - }) - const removeEventListener = vi.fn() - - vi.stubGlobal('document', { - addEventListener, - removeEventListener - }) - - const cleanup = installWorktreeVisibleRefreshVisibilityListener(onChange) - - expect(addEventListener).toHaveBeenCalledWith('visibilitychange', onChange) - listeners.get('visibilitychange')?.() - expect(onChange).toHaveBeenCalledTimes(1) - - cleanup() - expect(removeEventListener).toHaveBeenCalledWith('visibilitychange', onChange) - }) -}) +import { visibleReviewCardIds } from './use-visible-review-refresh' function row(id: string): WorktreeItemRow { return { @@ -74,65 +41,4 @@ describe('actual visible review cards', () => { }) ).toEqual(['visible']) }) - it('includes visible lineage children but excludes offscreen members', () => { - expect( - visibleReviewCardIds({ - enabled: true, - renderRows: [{ type: 'lineage-group', key: 'family', rows: [row('parent'), row('child')] }], - virtualItems: [virtualItem(0)], - viewportTop: 0, - viewportHeight: 100, - isOnScreen: (id) => id === 'child' - }) - ).toEqual(['child']) - }) - it('excludes rows when their review decoration is disabled', () => { - expect( - visibleReviewCardIds({ - enabled: false, - renderRows: [row('visible')], - virtualItems: [virtualItem(0)], - viewportTop: 0, - viewportHeight: 100 - }) - ).toEqual([]) - }) -}) - -describe('scrolling within one virtual lineage row', () => { - it('updates child visibility on scroll without changing virtual indexes and cancels pending frames', () => { - const listeners = new Map() - let frame: FrameRequestCallback = () => {} - const update = vi.fn() - const scroll = { - addEventListener: vi.fn((type: string, callback: EventListenerOrEventListenerObject) => { - listeners.set(type, callback) - }), - removeEventListener: vi.fn() - } - vi.stubGlobal( - 'requestAnimationFrame', - vi.fn((callback: FrameRequestCallback) => { - frame = callback - return 1 - }) - ) - const cancel = vi.fn() - vi.stubGlobal('cancelAnimationFrame', cancel) - const cleanup = installVisibleReviewCardScrollListener(scroll, update) - const listener = listeners.get('scroll') - if (typeof listener === 'function') { - listener(new Event('scroll')) - } - expect(update).not.toHaveBeenCalled() - frame(0) - expect(update).toHaveBeenCalledOnce() - if (typeof listener === 'function') { - listener(new Event('scroll')) - } - cleanup() - expect(cancel).toHaveBeenCalledWith(1) - expect(scroll.removeEventListener).toHaveBeenCalledWith('scroll', listener) - vi.unstubAllGlobals() - }) }) diff --git a/src/renderer/src/components/sidebar/worktree-manual-order-store-write.test.ts b/src/renderer/src/components/sidebar/worktree-manual-order-store-write.test.ts index 93749b72e18..9a726a7ea3b 100644 --- a/src/renderer/src/components/sidebar/worktree-manual-order-store-write.test.ts +++ b/src/renderer/src/components/sidebar/worktree-manual-order-store-write.test.ts @@ -118,24 +118,4 @@ describe('sidebar manual-order drop', () => { expect(updateMeta).not.toHaveBeenCalled() expect(toastInfo).not.toHaveBeenCalled() }) - - it('reorders silently when Manual is already the sort', async () => { - const worktrees = seedManualOrderedRows(3) - useAppStore.setState({ sortBy: 'manual' }) - const ids = worktrees.map((worktree) => worktree.id) - const keys = worktrees.map(getWorktreeHostIdentity) - const reorder = renderReorder(worktrees) - - await act(async () => { - reorder.current.reorderWorktrees({ - groups: [{ key: GROUP_KEY, worktreeIds: keys }], - sourceGroupKey: GROUP_KEY, - draggedKeys: [keys[0]!], - dropIndex: 2 - }) - }) - - expect(manualOrderedIds()).toEqual([ids[1], ids[0], ids[2]]) - expect(toastInfo).not.toHaveBeenCalled() - }) }) diff --git a/src/renderer/src/components/status-bar/status-bar-provider-menu-focus.test.tsx b/src/renderer/src/components/status-bar/status-bar-provider-menu-focus.test.tsx deleted file mode 100644 index 5b0b1a4c23d..00000000000 --- a/src/renderer/src/components/status-bar/status-bar-provider-menu-focus.test.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' - -const recordFeatureInteractionMock = vi.fn() -const usagePercentageDisplayMock = 'used' - -vi.mock(import('react'), async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - memo: function memo(component: T): T { - return component - }, - useRef: function useRef(current: T): { current: T } { - return { current } - } - } -}) - -vi.mock('../../store', () => ({ - useAppStore: ( - selector: (state: { - recordFeatureInteraction: typeof recordFeatureInteractionMock - usagePercentageDisplay: 'used' - }) => unknown - ) => - selector({ - recordFeatureInteraction: recordFeatureInteractionMock, - usagePercentageDisplay: usagePercentageDisplayMock - }) -})) - -vi.mock('./tooltip', () => ({ - ProviderIcon: function ProviderIcon(props: Record) { - return { type: 'ProviderIcon', props } - }, - ProviderPanel: function ProviderPanel(props: Record) { - return { type: 'ProviderPanel', props } - }, - barColor: () => 'bg-green-500', - clampUsedPercent: (n: number) => Math.max(0, Math.min(100, Math.round(n))) -})) - -vi.mock('@/components/ui/dropdown-menu', () => ({ - DropdownMenu: function DropdownMenu(props: Record) { - return { type: 'DropdownMenu', props } - }, - DropdownMenuContent: function DropdownMenuContent(props: Record) { - return { type: 'DropdownMenuContent', props } - }, - DropdownMenuSeparator: function DropdownMenuSeparator() { - return { type: 'DropdownMenuSeparator', props: {} } - }, - DropdownMenuTrigger: function DropdownMenuTrigger(props: Record) { - return { type: 'DropdownMenuTrigger', props } - } -})) - -type ReactElementLike = { - type: unknown - props: Record -} - -function findChildByType(node: unknown, typeName: string): ReactElementLike { - const stack = [node] - while (stack.length > 0) { - const current = stack.pop() - if (current == null || typeof current === 'string' || typeof current === 'number') { - continue - } - if (Array.isArray(current)) { - stack.push(...current) - continue - } - const el = current as ReactElementLike - const type = el.type as { name?: string } | string | undefined - const matchedName = typeof type === 'string' ? type : type?.name - if (matchedName === typeName) { - return el - } - if (el.props && 'children' in el.props) { - stack.push(el.props.children) - } - } - throw new Error(`Could not find ${typeName}`) -} - -async function renderProviderDetailsMenu(): Promise { - const { ProviderDetailsMenu } = await import('./StatusBar') - return ProviderDetailsMenu({ - provider: { - provider: 'codex', - status: 'ok', - error: null, - updatedAt: Date.now(), - session: { - usedPercent: 1, - resetsAt: Date.now() + 1_000, - resetDescription: '5h', - windowMinutes: 300 - }, - weekly: { - usedPercent: 3, - resetsAt: Date.now() + 1_000, - resetDescription: 'wk', - windowMinutes: 10_080 - } - }, - compact: false, - iconOnly: false, - ariaLabel: 'Open Codex usage details' - }) -} - -describe('ProviderDetailsMenu focus handoff', () => { - it('lets pointer-outside closes keep focus on the clicked surface', async () => { - const element = await renderProviderDetailsMenu() - const dropdown = findChildByType(element, 'DropdownMenu') - const content = findChildByType(element, 'DropdownMenuContent') - const providerPanel = findChildByType(element, 'ProviderPanel') - - expect(dropdown.props.modal).toBe(false) - expect(providerPanel.props.usagePercentageDisplay).toBe('used') - - const preventDefault = vi.fn() - ;(content.props.onCloseAutoFocus as (event: { preventDefault: () => void }) => void)({ - preventDefault - }) - expect(preventDefault).not.toHaveBeenCalled() - - ;(content.props.onPointerDownOutside as () => void)() - ;(content.props.onCloseAutoFocus as (event: { preventDefault: () => void }) => void)({ - preventDefault - }) - expect(preventDefault).toHaveBeenCalledOnce() - - preventDefault.mockClear() - ;(content.props.onCloseAutoFocus as (event: { preventDefault: () => void }) => void)({ - preventDefault - }) - expect(preventDefault).not.toHaveBeenCalled() - }) -}) diff --git a/src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-prebind-discard-notice.test.ts b/src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-prebind-discard-notice.test.ts index af4d5974c16..5373d4f293e 100644 --- a/src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-prebind-discard-notice.test.ts +++ b/src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-prebind-discard-notice.test.ts @@ -89,24 +89,6 @@ async function createGatedTransport(options: Record = {}) { // The user is told when keys typed into a starting remote pane were dropped for safety. describe('remote pre-bind typing discard notice', () => { - it('tells the pane when startup work made it drop typing from before the terminal bound', async () => { - const onTypingDiscardedBeforeBind = vi.fn() - const { transport, created, waitForCreate } = await createGatedTransport({ - keepTypeAheadForNewTerminal: () => true, - releasePreconnectInput: () => false, - onTypingDiscardedBeforeBind - }) - expect(transport.sendInput('make the tests pass\r', 'driving')).toBe(true) - const connecting = transport.connect({ url: '', callbacks: {} }) - await waitForCreate() - created.resolve({ handle: 'terminal-new' }) - await connecting - await completeAttach() - - expect(onTypingDiscardedBeforeBind).toHaveBeenCalledTimes(1) - transport.destroy?.() - }) - it('says nothing when the typing reaches the terminal it was typed for', async () => { const onTypingDiscardedBeforeBind = vi.fn() const { transport, created, waitForCreate } = await createGatedTransport({ diff --git a/src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-unmounted-host-pane.test.ts b/src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-unmounted-host-pane.test.ts index cda7e7eb0a4..5ca7426077e 100644 --- a/src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-unmounted-host-pane.test.ts +++ b/src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-unmounted-host-pane.test.ts @@ -20,23 +20,17 @@ afterEach(() => { let subscriptionCallbacks: MultiplexSubscriptionCallbacks = null let resolvedPaneHandle = 'terminal-1' -const { - emitSnapshot, - runtimeCall, - latestSubscribePayload, - subscribeFrameCount, - subscribedTerminalHandles, - resetRemoteRuntimeTransport -} = createRemoteRuntimeTransportMocks({ - getCallbacks: () => subscriptionCallbacks, - setCallbacks: (callbacks) => { - subscriptionCallbacks = callbacks - }, - getResolvedPaneHandle: () => resolvedPaneHandle, - setResolvedPaneHandle: (handle) => { - resolvedPaneHandle = handle - } -}) +const { latestSubscribePayload, subscribeFrameCount, resetRemoteRuntimeTransport } = + createRemoteRuntimeTransportMocks({ + getCallbacks: () => subscriptionCallbacks, + setCallbacks: (callbacks) => { + subscriptionCallbacks = callbacks + }, + getResolvedPaneHandle: () => resolvedPaneHandle, + setResolvedPaneHandle: (handle) => { + resolvedPaneHandle = handle + } + }) // What a host sends when its window did not mount the pane within its 10 s wait. function answerUnmountedHostPane(streamId: number): void { @@ -65,44 +59,6 @@ describe('remote runtime PTY whose host pane is not mounted yet', () => { resetRemoteRuntimeTransport() }) - it('keeps the pane and reattaches once the host mounts it', async () => { - const onPtyExit = vi.fn() - const { transport, onExit } = await connectPane(onPtyExit) - - answerUnmountedHostPane(latestSubscribePayload().streamId) - - expect(onPtyExit).not.toHaveBeenCalled() - expect(onExit).not.toHaveBeenCalled() - await vi.waitFor(() => expect(subscribeFrameCount()).toBe(2)) - expect(subscribedTerminalHandles()).toEqual(['terminal-1', 'terminal-1']) - emitSnapshot(latestSubscribePayload().streamId, 'host mounted the pane') - await vi.waitFor(() => expect(transport.getRecoveryState?.().phase).toBe('connected')) - expect(transport.getPtyId()).toBe('remote:env-1@@terminal-1') - expect(onPtyExit).not.toHaveBeenCalled() - transport.destroy?.() - }) - - it('retires the pane as lost, not exited, when the host stops listing it', async () => { - const onPtyExit = vi.fn() - const { transport, onExit } = await connectPane(onPtyExit) - const defaultCall = runtimeCall.getMockImplementation() - runtimeCall.mockImplementation(async (args: { method: string }) => { - if (args.method === 'terminal.resolvePane') { - return { ok: false, error: { code: 'runtime_error', message: 'terminal_not_found' } } - } - return defaultCall?.(args) - }) - - answerUnmountedHostPane(latestSubscribePayload().streamId) - - // Why -1: the host-loss sentinel; a pane the host stopped listing is not proof of exit. - await vi.waitFor(() => expect(onPtyExit).toHaveBeenCalledWith('remote:env-1@@terminal-1', -1)) - expect(onPtyExit).toHaveBeenCalledOnce() - expect(onExit).not.toHaveBeenCalled() - expect(subscribeFrameCount()).toBe(1) - transport.destroy?.() - }) - it('backs off and settles disconnected, not exited, when the host never mounts it', async () => { vi.useFakeTimers() try { diff --git a/src/renderer/src/components/terminal-pane/terminal-ime-input-context-refresh.test.ts b/src/renderer/src/components/terminal-pane/terminal-ime-input-context-refresh.test.ts index 5a31b691cdd..a5025e5977b 100644 --- a/src/renderer/src/components/terminal-pane/terminal-ime-input-context-refresh.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-ime-input-context-refresh.test.ts @@ -17,30 +17,6 @@ describe('refreshTerminalImeInputContext', () => { return helper } - it('blurs then refocuses the helper on macOS', () => { - const helper = appendHelper() - const blur = vi.spyOn(helper, 'blur') - const focus = vi.spyOn(helper, 'focus') - helper.focus() - focus.mockClear() - const scheduled: (() => void)[] = [] - - const refreshed = refreshTerminalImeInputContext(helper, { - isMac: true, - scheduleRefocus: (callback) => scheduled.push(callback) - }) - - expect(refreshed).toBe(true) - expect(blur).toHaveBeenCalledOnce() - expect(focus).not.toHaveBeenCalled() - - for (const run of scheduled) { - run() - } - expect(focus).toHaveBeenCalledOnce() - expect(document.activeElement).toBe(helper) - }) - it('marks only the synchronous refresh blur so focus ownership can stay latched', () => { const helper = appendHelper() let refreshingDuringBlur = false @@ -80,27 +56,6 @@ describe('refreshTerminalImeInputContext', () => { expect(document.activeElement).toBe(outside) }) - it('reports when a newer focus owner wins the refocus guard', () => { - const helper = appendHelper() - const outside = document.createElement('input') - document.body.appendChild(outside) - const onRefocusSkipped = vi.fn() - const scheduled: (() => void)[] = [] - helper.focus() - - refreshTerminalImeInputContext(helper, { - isMac: true, - onRefocusSkipped, - scheduleRefocus: (callback) => scheduled.push(callback) - }) - - outside.focus() - for (const run of scheduled) { - run() - } - expect(onRefocusSkipped).toHaveBeenCalledWith(outside) - }) - it('reports when a connected helper cannot accept the scheduled focus', () => { const helper = appendHelper() const onRefocusSkipped = vi.fn() @@ -120,19 +75,4 @@ describe('refreshTerminalImeInputContext', () => { expect(document.activeElement).toBe(document.body) expect(onRefocusSkipped).toHaveBeenCalledWith(document.body) }) - - it('skips non-macOS platforms', () => { - const helper = appendHelper() - const blur = vi.spyOn(helper, 'blur') - - const refreshed = refreshTerminalImeInputContext(helper, { - isMac: false, - scheduleRefocus: () => { - throw new Error('should not schedule') - } - }) - - expect(refreshed).toBe(false) - expect(blur).not.toHaveBeenCalled() - }) }) diff --git a/src/renderer/src/components/terminal-pane/terminal-pane-host-state-memo.test.ts b/src/renderer/src/components/terminal-pane/terminal-pane-host-state-memo.test.ts index 36d065e01a1..b667aad7f14 100644 --- a/src/renderer/src/components/terminal-pane/terminal-pane-host-state-memo.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-pane-host-state-memo.test.ts @@ -1,27 +1,12 @@ // useShallow skips renders, not selectors; memoize host resolution to avoid per-tab traversal and discarded objects on every publication. -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type * as ConnectionContext from '@/lib/connection-context' +import { beforeEach, describe, expect, it } from 'vitest' import type { AppState } from '@/store/types' import { resetTerminalPaneHostStateMemoForTests, selectTerminalPaneHostState } from './terminal-pane-host-state' -const connectionIdCalls = vi.hoisted(() => ({ count: 0 })) - -vi.mock('@/lib/connection-context', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - getConnectionIdFromState: (state: AppState, worktreeId: string | null) => { - connectionIdCalls.count += 1 - return actual.getConnectionIdFromState(state, worktreeId) - } - } -}) - const LOCAL_WORKTREE = 'repo-1::/repo/worktrees/local' -const OTHER_WORKTREE = 'repo-2::/repo/worktrees/other' /** Only the fields the resolver touches; the memo key is the whole object. */ function makeState(overrides: Partial> = {}): AppState { @@ -46,7 +31,6 @@ function makeState(overrides: Partial> = {}): AppState { beforeEach(() => { resetTerminalPaneHostStateMemoForTests() - connectionIdCalls.count = 0 }) describe('selectTerminalPaneHostState memo', () => { @@ -77,21 +61,6 @@ describe('selectTerminalPaneHostState memo', () => { expect(after).toBe(before) }) - it('resolves the host once per published state, not once per mounted tab', () => { - const state = makeState() - selectTerminalPaneHostState(state, LOCAL_WORKTREE) - const afterFirstTab = connectionIdCalls.count - expect(afterFirstTab).toBe(1) - - for (let index = 0; index < 4; index += 1) { - selectTerminalPaneHostState(state, LOCAL_WORKTREE) - } - expect(connectionIdCalls.count).toBe(afterFirstTab) - - selectTerminalPaneHostState(state, OTHER_WORKTREE) - expect(connectionIdCalls.count).toBe(afterFirstTab + 1) - }) - it('re-resolves when the published state changes, so a host change is never stale', () => { const local = makeState() const before = selectTerminalPaneHostState(local, LOCAL_WORKTREE) @@ -108,31 +77,4 @@ describe('selectTerminalPaneHostState memo', () => { expect(after).not.toBe(before) expect(after.sshReconnectTargetId).toBe('ssh-host-a') }) - - it('allocates nothing across 1,000 publications at 6 worktrees x 5 tabs', () => { - const worktreeIds = Array.from({ length: 6 }, (_, index) => `repo-${index}::/repo/wt-${index}`) - const firstState = makeState() - const firstByWorktree = new Map( - worktreeIds.map((worktreeId) => [ - worktreeId, - selectTerminalPaneHostState(firstState, worktreeId) - ]) - ) - connectionIdCalls.count = 0 - let allocations = 0 - - for (let publication = 0; publication < 1_000; publication += 1) { - const state = makeState({ agentStatusEpoch: publication }) - for (const worktreeId of worktreeIds) { - for (let tab = 0; tab < 5; tab += 1) { - if (selectTerminalPaneHostState(state, worktreeId) !== firstByWorktree.get(worktreeId)) { - allocations += 1 - } - } - } - } - - expect(allocations).toBe(0) - expect(connectionIdCalls.count).toBe(6 * 1_000) - }) }) diff --git a/src/renderer/src/components/terminal-pane/terminal-pane-split-request-routing.test.ts b/src/renderer/src/components/terminal-pane/terminal-pane-split-request-routing.test.ts index 6fc9fdda38b..3aaac8a3fc7 100644 --- a/src/renderer/src/components/terminal-pane/terminal-pane-split-request-routing.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-pane-split-request-routing.test.ts @@ -6,7 +6,6 @@ import { BACKGROUND_WORKTREE_MEASURE_WINDOW_MS } from '../terminal/background-te import { _resetTerminalPaneSplitRequestRoutingForTests, cancelQueuedTerminalPaneSplitRequests, - dispatchTerminalPaneSplitRequest, hasTerminalPaneSplitMountLease, queueTerminalPaneSplitRequest, registerTerminalPaneSplitRequestHandler, @@ -37,16 +36,6 @@ afterEach(() => { }) describe('parked terminal split request routing', () => { - it('demonstrates that the legacy fire-and-forget event is lost before a parked pane mounts', () => { - const handler = vi.fn() - - dispatchTerminalPaneSplitRequest(splitRequest('tab-parked')) - const unregister = registerTerminalPaneSplitRequestHandler('tab-parked', undefined, handler) - - expect(handler).not.toHaveBeenCalled() - unregister() - }) - it('replays a parked-tab request as soon as that exact pane lifecycle registers', () => { const request = splitRequest('tab-parked', 91) const splitPane = vi.fn() @@ -74,10 +63,6 @@ describe('parked terminal split request routing', () => { unregister() }) - it('fails closed when a remount no longer contains the stable source leaf', () => { - expect(resolveTerminalPaneSplitSourceId(splitRequest('tab-parked', 91), () => null)).toBe(-1) - }) - it('keeps the mount lease through replay, then releases it at the existing measure bound', () => { queueTerminalPaneSplitRequest(splitRequest('tab-parked')) takeQueuedTerminalPaneSplitRequests('tab-parked') diff --git a/src/renderer/src/components/terminal-pane/useTerminalFontZoom.test.ts b/src/renderer/src/components/terminal-pane/useTerminalFontZoom.test.ts deleted file mode 100644 index f4dcdcd3f8b..00000000000 --- a/src/renderer/src/components/terminal-pane/useTerminalFontZoom.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -// @vitest-environment happy-dom -import type * as ReactModule from 'react' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { useTerminalFontZoom } from './useTerminalFontZoom' - -const mocks = vi.hoisted(() => ({ - captureScrollState: vi.fn(() => ({ wasAtBottom: true })), - restoreScrollState: vi.fn(), - safeFit: vi.fn(), - dispatchZoomLevelChanged: vi.fn() -})) - -vi.mock('react', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - useEffect: (effect: () => void | (() => void)) => effect() - } -}) - -vi.mock('@/lib/pane-manager/pane-tree-ops', () => ({ - captureScrollState: mocks.captureScrollState, - restoreScrollState: mocks.restoreScrollState, - safeFit: mocks.safeFit -})) - -vi.mock('@/lib/zoom-events', () => ({ - dispatchZoomLevelChanged: mocks.dispatchZoomLevelChanged -})) - -describe('useTerminalFontZoom', () => { - let terminalZoomListeners: ((direction: 'in' | 'out' | 'reset') => void)[] - - beforeEach(() => { - terminalZoomListeners = [] - document.body.replaceChildren() - vi.clearAllMocks() - vi.stubGlobal('window', { - api: { - ui: { - onTerminalZoom: vi.fn((listener: (direction: 'in' | 'out' | 'reset') => void) => { - terminalZoomListeners.push(listener) - return () => {} - }) - } - } - }) - }) - - function useMountedTerminalFontZoom(activeElement: HTMLElement): { - terminal: { options: { fontSize?: number } } - listener: (direction: 'in' | 'out' | 'reset') => void - } { - const container = document.createElement('div') - document.body.appendChild(container) - container.appendChild(activeElement) - activeElement.focus() - const terminal = { options: { fontSize: 14 } } - useTerminalFontZoom({ - isActive: true, - containerRef: { current: container }, - managerRef: { - current: { - getActivePane: () => ({ id: 1, terminal }) - } - } as never, - paneFontSizesRef: { current: new Map() }, - settingsRef: { current: { terminalFontSize: 14 } } - }) - const listener = terminalZoomListeners.at(-1) - expect(listener).toBeTypeOf('function') - return { terminal, listener: listener as (direction: 'in' | 'out' | 'reset') => void } - } - - it('ignores zoom events when terminal input no longer owns focus', () => { - const button = document.createElement('button') - const { listener, terminal } = useMountedTerminalFontZoom(button) - - listener('in') - - expect(terminal.options.fontSize).toBe(14) - expect(mocks.safeFit).not.toHaveBeenCalled() - expect(mocks.dispatchZoomLevelChanged).not.toHaveBeenCalled() - }) - - it('applies terminal font zoom while the xterm helper textarea owns focus', () => { - const helper = document.createElement('textarea') - helper.className = 'xterm-helper-textarea' - const { listener, terminal } = useMountedTerminalFontZoom(helper) - - listener('in') - - expect(terminal.options.fontSize).toBe(15) - expect(mocks.safeFit).toHaveBeenCalledTimes(1) - expect(mocks.dispatchZoomLevelChanged).toHaveBeenCalledWith('terminal', 107) - }) - - it('only lets the pane owning the focused helper apply terminal font zoom', () => { - const inactiveContainer = document.createElement('div') - const activeContainer = document.createElement('div') - const focusedHelper = document.createElement('textarea') - focusedHelper.className = 'xterm-helper-textarea' - document.body.append(inactiveContainer, activeContainer) - activeContainer.appendChild(focusedHelper) - focusedHelper.focus() - - const inactiveTerminal = { options: { fontSize: 14 } } - const activeTerminal = { options: { fontSize: 14 } } - useTerminalFontZoom({ - isActive: true, - containerRef: { current: inactiveContainer }, - managerRef: { - current: { - getActivePane: () => ({ id: 1, terminal: inactiveTerminal }) - } - } as never, - paneFontSizesRef: { current: new Map() }, - settingsRef: { current: { terminalFontSize: 14 } } - }) - useTerminalFontZoom({ - isActive: true, - containerRef: { current: activeContainer }, - managerRef: { - current: { - getActivePane: () => ({ id: 2, terminal: activeTerminal }) - } - } as never, - paneFontSizesRef: { current: new Map() }, - settingsRef: { current: { terminalFontSize: 14 } } - }) - - for (const listener of terminalZoomListeners) { - listener('in') - } - - expect(inactiveTerminal.options.fontSize).toBe(14) - expect(activeTerminal.options.fontSize).toBe(15) - expect(mocks.safeFit).toHaveBeenCalledTimes(1) - }) -}) diff --git a/src/renderer/src/components/terminal/terminal-tab-actions-kill-all.test.ts b/src/renderer/src/components/terminal/terminal-tab-actions-kill-all.test.ts deleted file mode 100644 index b259954e02c..00000000000 --- a/src/renderer/src/components/terminal/terminal-tab-actions-kill-all.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' - -const { getStateMock, closeWebRuntimeSessionTabMock, isWebRuntimeSessionActiveMock } = vi.hoisted( - () => ({ - getStateMock: vi.fn(), - closeWebRuntimeSessionTabMock: vi.fn(), - isWebRuntimeSessionActiveMock: vi.fn(() => false) - }) -) - -vi.mock('@/store', () => ({ - useAppStore: { getState: getStateMock } -})) - -vi.mock('@/runtime/web-runtime-session-tab-lifecycle', () => ({ - activateWebRuntimeSessionTab: vi.fn(), - closeWebRuntimeSessionTab: closeWebRuntimeSessionTabMock -})) -vi.mock('@/runtime/web-runtime-terminal-creation', () => ({ - createWebRuntimeSessionTerminal: vi.fn() -})) -vi.mock('@/runtime/web-runtime-session-environment', () => ({ - isWebRuntimeSessionActive: isWebRuntimeSessionActiveMock -})) -vi.mock('../../../../shared/terminal-surface-id', () => ({ - toHostSessionTabId: vi.fn((tabId: string) => tabId) -})) - -vi.mock('@/runtime/web-session-tabs-sync/tracking-mappings', () => ({ - resolveHostSessionTabIdForWebSessionTab: vi.fn(() => null) -})) - -import { closeTerminalTab } from './terminal-tab-actions' - -function baseState(overrides: Record = {}): Record { - return { - settings: { activeRuntimeEnvironmentId: null, confirmClosePinnedTab: true }, - repos: [{ id: 'repo', executionHostId: 'local', connectionId: null }], - worktreesByRepo: { repo: [{ id: 'wt', repoId: 'repo' }] }, - tabsByWorktree: { wt: [{ id: 'terminal-1' }] }, - unifiedTabsByWorktree: {}, - activeWorktreeId: 'wt', - activeTabId: 'terminal-1', - openFiles: [], - browserTabsByWorktree: {}, - reconcileWorktreeTabModel: vi.fn(() => ({ renderableTabCount: 0 })), - closeTab: vi.fn(), - closeUnifiedTab: vi.fn(), - setActiveFile: vi.fn(), - setActiveBrowserTab: vi.fn(), - setActiveTabType: vi.fn(), - setActiveTab: vi.fn(), - setActiveWorktree: vi.fn(), - requestPinnedTabCloseConfirm: vi.fn(), - createTab: vi.fn(), - closeFile: vi.fn(), - closeBrowserTab: vi.fn(), - ...overrides - } -} - -describe('closeTerminalTab kill-all routing', () => { - beforeEach(() => { - vi.clearAllMocks() - isWebRuntimeSessionActiveMock.mockReturnValue(false) - }) - - it('force-closes a pinned terminal without opening a second confirmation', () => { - const closeTab = vi.fn() - const closeUnifiedTab = vi.fn() - const requestPinnedTabCloseConfirm = vi.fn() - getStateMock.mockReturnValue( - baseState({ - tabsByWorktree: {}, - unifiedTabsByWorktree: { - wt: [ - { - id: 'visible-pinned', - entityId: 'terminal-1', - contentType: 'terminal', - isPinned: true - } - ] - }, - closeTab, - closeUnifiedTab, - requestPinnedTabCloseConfirm - }) - ) - - closeTerminalTab('terminal-1', { force: true }) - - expect(requestPinnedTabCloseConfirm).not.toHaveBeenCalled() - expect(closeTab).toHaveBeenCalledWith('terminal-1', { reason: undefined }) - expect(closeUnifiedTab).not.toHaveBeenCalled() - }) - - it('routes the last active terminal to an existing editor without closing it', () => { - const state = baseState({ - openFiles: [{ id: 'editor-1', worktreeId: 'wt' }] - }) - getStateMock.mockReturnValue(state) - - closeTerminalTab('terminal-1', { force: true }) - - expect(state.closeTab).toHaveBeenCalledWith('terminal-1') - expect(state.setActiveFile).toHaveBeenCalledWith('editor-1') - expect(state.setActiveTabType).toHaveBeenCalledWith('editor', 'wt') - expect(state.closeFile).not.toHaveBeenCalled() - expect(state.closeBrowserTab).not.toHaveBeenCalled() - expect(state.setActiveWorktree).not.toHaveBeenCalled() - expect(state.createTab).not.toHaveBeenCalled() - }) - - it('routes the last active terminal to an existing browser when no editor exists', () => { - const state = baseState({ - browserTabsByWorktree: { wt: [{ id: 'browser-1' }] } - }) - getStateMock.mockReturnValue(state) - - closeTerminalTab('terminal-1', { force: true }) - - expect(state.setActiveBrowserTab).toHaveBeenCalledWith('browser-1') - expect(state.setActiveTabType).toHaveBeenCalledWith('browser', 'wt') - expect(state.closeBrowserTab).not.toHaveBeenCalled() - expect(state.setActiveWorktree).not.toHaveBeenCalled() - expect(state.createTab).not.toHaveBeenCalled() - }) - - it('deactivates after the last active terminal when no other content exists', () => { - const state = baseState() - getStateMock.mockReturnValue(state) - - closeTerminalTab('terminal-1', { force: true }) - - expect(state.setActiveWorktree).toHaveBeenCalledWith(null) - expect(state.setActiveFile).not.toHaveBeenCalled() - expect(state.setActiveBrowserTab).not.toHaveBeenCalled() - expect(state.createTab).not.toHaveBeenCalled() - }) -}) diff --git a/src/renderer/src/components/use-terminal-editor-close-queue.reveal.test.tsx b/src/renderer/src/components/use-terminal-editor-close-queue.reveal.test.tsx index 6b54d111dcd..92df9cd45de 100644 --- a/src/renderer/src/components/use-terminal-editor-close-queue.reveal.test.tsx +++ b/src/renderer/src/components/use-terminal-editor-close-queue.reveal.test.tsx @@ -84,40 +84,6 @@ describe('unsaved-changes prompt for a closing file', () => { }) afterEach(() => cleanup()) - it('reveals a floating note in the floating panel without moving the main window', () => { - store.openFiles = [dirtyFile('note', FLOATING_TERMINAL_WORKTREE_ID)] - const { result } = mountCloseQueue() - - act(() => requestEditorFileClose('note')) - - expect(result.current.saveDialogFileId).toBe('note') - expect(revealFloatingWorkspacePanel).toHaveBeenCalledOnce() - expect(setActiveWorktree).not.toHaveBeenCalled() - expect(setActiveTabType).toHaveBeenCalledWith('editor', FLOATING_TERMINAL_WORKTREE_ID) - }) - - it('still switches to a background worktree to show its file', () => { - store.openFiles = [dirtyFile('readme', 'wt-other')] - mountCloseQueue() - - act(() => requestEditorFileClose('readme')) - - expect(setActiveWorktree).toHaveBeenCalledWith('wt-other') - expect(revealFloatingWorkspacePanel).not.toHaveBeenCalled() - }) - - it('runs the close reaction once the file is discarded', async () => { - store.openFiles = [dirtyFile('note', FLOATING_TERMINAL_WORKTREE_ID)] - const onClosed = vi.fn() - const { result } = mountCloseQueue() - act(() => requestEditorFileClose('note', { onClosed })) - - await act(() => result.current.handleSaveDialogDiscard()) - - expect(closeFile).toHaveBeenCalledWith('note') - expect(onClosed).toHaveBeenCalledOnce() - }) - // Why a later close of the same file: a cancelled reaction left behind would fire on it. it('drops the close reaction when the prompt is cancelled', async () => { vi.useFakeTimers() diff --git a/src/renderer/src/hooks/client-workspace-view-report.test.ts b/src/renderer/src/hooks/client-workspace-view-report.test.ts index c0ff673f13b..34601032e40 100644 --- a/src/renderer/src/hooks/client-workspace-view-report.test.ts +++ b/src/renderer/src/hooks/client-workspace-view-report.test.ts @@ -73,19 +73,6 @@ describe('createClientWorkspaceViewReporter', () => { ]) }) - it('refreshes the host’s copy at most every ten seconds of activity', () => { - const { sent, report } = reporter() - report.update(STATE, ['local-wt'], []) - vi.setSystemTime(Date.now() + 9_000) - report.renewPresence() - expect(sent).toHaveLength(1) - - vi.setSystemTime(Date.now() + 1_000) - report.renewPresence() - expect(sent).toHaveLength(2) - expect(sent[1].view.presentForMs).toBe(190_000) - }) - it('reports an away user’s screen with no presence, and renews as soon as input returns', () => { const { sent, report } = reporter() presence.remaining = 0 @@ -98,16 +85,6 @@ describe('createClientWorkspaceViewReporter', () => { expect(sent).toHaveLength(2) }) - it('refreshes where presence is not tracked too, so a reconnected host learns the view', () => { - const { sent, report } = reporter() - presence.remaining = null - report.update(STATE, ['local-wt'], []) - vi.setSystemTime(Date.now() + 10_000) - report.renewPresence() - expect(sent).toHaveLength(2) - expect(sent[1].view.presentForMs).toBeNull() - }) - it('retries a failed send, empty or not, no sooner than a refresh', async () => { const sent: ClientWorkspaceView[] = [] let fail = true diff --git a/src/renderer/src/hooks/ipc-events/agent-status-reconnect-replay-notification.test.ts b/src/renderer/src/hooks/ipc-events/agent-status-reconnect-replay-notification.test.ts index 1c4ffd501bb..9e51628c867 100644 --- a/src/renderer/src/hooks/ipc-events/agent-status-reconnect-replay-notification.test.ts +++ b/src/renderer/src/hooks/ipc-events/agent-status-reconnect-replay-notification.test.ts @@ -121,24 +121,4 @@ describe('live agent-status replay notifications', () => { 1_700_000_004_000 ) }) - - it('seeds a stamped working replay when this renderer holds no row', () => { - const apply = createApplicator() - - apply(status({ state: 'working', isReplay: true, turnCompletedAt: 1_700_000_004_000 })) - - expect(mocks.observe.mock.calls[0][0]).toHaveProperty('seedOnly', true) - }) - - it('seeds a replayed working row, and a later real done still notifies', () => { - const apply = createApplicator() - - apply(status({ state: 'working', isReplay: true })) - apply(status({ state: 'done', receivedAt: 1_700_000_006_000 })) - - expect(mocks.observe).toHaveBeenCalledTimes(2) - expect(mocks.observe.mock.calls[0][0]).toHaveProperty('seedOnly', true) - expect(mocks.observe.mock.calls[1][0]).not.toHaveProperty('seedOnly') - expect(mocks.observe.mock.calls[1][0].payload).toHaveProperty('state', 'done') - }) }) diff --git a/src/renderer/src/hooks/useReadOnScreenAttention.clock-skew.test.ts b/src/renderer/src/hooks/useReadOnScreenAttention.clock-skew.test.ts index 5816405d4a9..e1751724d05 100644 --- a/src/renderer/src/hooks/useReadOnScreenAttention.clock-skew.test.ts +++ b/src/renderer/src/hooks/useReadOnScreenAttention.clock-skew.test.ts @@ -123,15 +123,6 @@ describe('useReadOnScreenAttention — clock-skewed execution host', () => { expect(calls).toEqual([]) }) - it('still acks a normally stamped turn once', () => { - seedFutureStampedTurn(NOW - 5_000) - const calls = instrumentAcknowledgeAgents() - - renderHook(() => useReadOnScreenAttention()) - - expect(calls).toEqual([[PANE_KEY]]) - }) - it('keeps an explicitly marked-unread visible turn unread', () => { seedFutureStampedTurn(NOW - 5_000) useAppStore.getState().acknowledgeAgents([PANE_KEY]) diff --git a/src/renderer/src/lib/pane-manager/terminal-linkifier-hover-reset-on-mouseleave.test.ts b/src/renderer/src/lib/pane-manager/terminal-linkifier-hover-reset-on-mouseleave.test.ts deleted file mode 100644 index d1748395dd5..00000000000 --- a/src/renderer/src/lib/pane-manager/terminal-linkifier-hover-reset-on-mouseleave.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import type { Terminal } from '@xterm/xterm' -import { describe, expect, it, vi } from 'vitest' -import { - installTerminalLinkifierHoverResetOnMouseLeave, - installTerminalLinkifierHoverResetOnWindowBlur -} from './terminal-linkifier-hover-reset-on-mouseleave' - -type FakeLinkifier = { - _lastBufferCell?: unknown - _activeLine?: number - _clearCurrentLink?: () => void - _currentLink?: unknown -} - -function createHarness(hasScreen = true) { - let mouseLeaveHandler: (() => void) | null = null - let blurHandler: (() => void) | null = null - const addEventListener = vi.fn((_event: string, handler: () => void) => { - if (_event === 'mouseleave') { - mouseLeaveHandler = handler - } - }) - const removeEventListener = vi.fn((_event: string, handler: () => void) => { - if (_event === 'mouseleave' && mouseLeaveHandler === handler) { - mouseLeaveHandler = null - } - }) - const screen = { addEventListener, removeEventListener, classList: { remove: vi.fn() } } - const querySelector = vi.fn(() => (hasScreen ? screen : null)) - const rendererWindow = { - addEventListener: vi.fn((_event: string, handler: () => void) => { - blurHandler = handler - }), - removeEventListener: vi.fn((_event: string, handler: () => void) => { - if (blurHandler === handler) { - blurHandler = null - } - }) - } - const linkifier: FakeLinkifier = { - _lastBufferCell: { x: 2, y: 3 }, - _activeLine: 3, - _clearCurrentLink: vi.fn(), - _currentLink: { link: 'https://example.com' } - } - const terminal = { - element: { querySelector, ownerDocument: { defaultView: rendererWindow } }, - _core: { linkifier } - } as unknown as Terminal - const linkTooltip = { - ownerDocument: { defaultView: rendererWindow }, - style: { display: '' } - } as unknown as HTMLElement - return { - terminal, - linkifier, - linkTooltip, - rendererWindow, - querySelector, - addEventListener, - removeEventListener, - dispatchMouseLeave: () => mouseLeaveHandler?.(), - dispatchBlur: () => blurHandler?.() - } -} - -describe('installTerminalLinkifierHoverResetOnMouseLeave', () => { - it('resets the hover cache when the terminal surface loses the pointer', () => { - const harness = createHarness() - installTerminalLinkifierHoverResetOnMouseLeave(harness.terminal, harness.linkTooltip) - - expect(harness.querySelector).toHaveBeenCalledWith('.xterm-screen') - expect(harness.addEventListener).toHaveBeenCalledWith('mouseleave', expect.any(Function)) - harness.dispatchMouseLeave() - - expect(harness.linkifier._lastBufferCell).toBeUndefined() - expect(harness.linkifier._activeLine).toBe(-1) - expect(harness.linkifier._clearCurrentLink).toHaveBeenCalledTimes(1) - expect(harness.linkifier._currentLink).toBeUndefined() - expect(harness.linkTooltip.style.display).toBe('none') - }) - - it('removes the listener on dispose', () => { - const harness = createHarness() - const disposable = installTerminalLinkifierHoverResetOnMouseLeave(harness.terminal) - const mouseLeaveHandler = harness.addEventListener.mock.calls.find( - ([eventName]) => eventName === 'mouseleave' - )?.[1] - expect(mouseLeaveHandler).toBeTypeOf('function') - - disposable.dispose() - expect(harness.removeEventListener).toHaveBeenCalledWith('mouseleave', mouseLeaveHandler) - harness.dispatchMouseLeave() - - expect(harness.linkifier._lastBufferCell).toEqual({ x: 2, y: 3 }) - expect(harness.linkifier._activeLine).toBe(3) - }) - - it('degrades to a no-op when the screen is unavailable', () => { - const harness = createHarness(false) - - expect(() => - installTerminalLinkifierHoverResetOnMouseLeave(harness.terminal).dispose() - ).not.toThrow() - }) - - it('does not throw when xterm linkifier internals are unavailable', () => { - const harness = createHarness() - const terminal = harness.terminal as unknown as { _core?: unknown } - terminal._core = undefined - installTerminalLinkifierHoverResetOnMouseLeave(harness.terminal) - - expect(harness.dispatchMouseLeave).not.toThrow() - }) - - it('clears the active link and tooltip when the renderer window blurs', () => { - const harness = createHarness() - const disposable = installTerminalLinkifierHoverResetOnWindowBlur( - harness.terminal, - harness.linkTooltip - ) - - harness.dispatchBlur() - - expect(harness.rendererWindow.addEventListener).toHaveBeenCalledWith( - 'blur', - expect.any(Function) - ) - expect(harness.linkifier._clearCurrentLink).toHaveBeenCalledTimes(1) - expect(harness.linkifier._currentLink).toBeUndefined() - expect(harness.linkifier._lastBufferCell).toBeUndefined() - expect(harness.linkifier._activeLine).toBe(-1) - expect(harness.linkTooltip.style.display).toBe('none') - - disposable.dispose() - expect(harness.rendererWindow.removeEventListener).toHaveBeenCalledWith( - 'blur', - expect.any(Function) - ) - }) -}) diff --git a/src/renderer/src/lib/worktree-creation-hook-flow.test.ts b/src/renderer/src/lib/worktree-creation-hook-flow.test.ts index e9e2312510c..22b7ef77f01 100644 --- a/src/renderer/src/lib/worktree-creation-hook-flow.test.ts +++ b/src/renderer/src/lib/worktree-creation-hook-flow.test.ts @@ -33,10 +33,7 @@ vi.mock('@/lib/ensure-hooks-confirmed', () => ({ vi.mock('sonner', () => ({ toast: { error: vi.fn() } })) import { ensureHooksConfirmed } from './ensure-hooks-confirmed' -import { - runBackgroundWorktreeCreation, - retryBackgroundWorktreeCreation -} from './worktree-creation-flow' +import { runBackgroundWorktreeCreation } from './worktree-creation-flow' function makeRequest(overrides: Partial = {}): WorktreeCreationRequest { return { @@ -106,34 +103,4 @@ describe('background script preparation', () => { await flushAsyncWorktreeCreation() expect(store.createWorktree).not.toHaveBeenCalled() }) - - it('keeps refused scripts disabled when creating the worktree', async () => { - vi.mocked(ensureHooksConfirmed).mockResolvedValueOnce('skip') - runBackgroundWorktreeCreation( - makeRequest({ - setupDecision: 'run', - hookPreparation: { executionHostId: 'local' } - }) - ) - await vi.waitFor(() => expect(store.createWorktree).toHaveBeenCalledOnce()) - expect(store.pendingWorktreeCreations['creation-1'].request.setupDecision).toBe('skip') - }) - - it('retries failed preparation on the captured host', async () => { - vi.mocked(ensureHooksConfirmed).mockRejectedValueOnce(new Error('inspection failed')) - runBackgroundWorktreeCreation( - makeRequest({ hookPreparation: { executionHostId: 'runtime:host-1' } }) - ) - await vi.waitFor(() => - expect(store.pendingWorktreeCreations['creation-1'].status).toBe('error') - ) - expect(store.createWorktree).not.toHaveBeenCalled() - - const trust = Promise.withResolvers<'run' | 'skip'>() - vi.mocked(ensureHooksConfirmed).mockReturnValueOnce(trust.promise) - retryBackgroundWorktreeCreation('creation-1') - expect(store.pendingWorktreeCreations['creation-1'].phase).toBe('preparing') - trust.resolve('run') - await vi.waitFor(() => expect(store.createWorktree).toHaveBeenCalledOnce()) - }) }) diff --git a/src/renderer/src/runtime/web-session-terminal-handle-events.test.ts b/src/renderer/src/runtime/web-session-terminal-handle-events.test.ts index 08fee80eea9..9bb29cab217 100644 --- a/src/renderer/src/runtime/web-session-terminal-handle-events.test.ts +++ b/src/renderer/src/runtime/web-session-terminal-handle-events.test.ts @@ -1,7 +1,6 @@ import { describe, expect, it, vi } from 'vitest' import type { RuntimeMobileSessionTabsResult } from '../../../shared/runtime-types' import { - getWebSessionTerminalHandleSubscriberCountForTests, queueAcceptedWebSessionTerminalSnapshot, subscribeAcceptedWebSessionTerminalHandle } from './web-session-terminal-handle-events' @@ -22,42 +21,6 @@ function snapshot( } describe('accepted web-session terminal handle events', () => { - it('notifies only the matching runtime/worktree/pane and releases the listener', async () => { - const listener = vi.fn() - const unsubscribe = subscribeAcceptedWebSessionTerminalHandle( - { environmentId: 'env-1', worktreeId: 'wt-1', hostTabId: 'tab-1', leafId: 'leaf-1' }, - listener - ) - - queueAcceptedWebSessionTerminalSnapshot(snapshot([]), 'env-2') - queueAcceptedWebSessionTerminalSnapshot(snapshot([], 'wt-2'), 'env-1') - queueAcceptedWebSessionTerminalSnapshot( - snapshot([ - { - type: 'terminal', - id: 'tab-1::leaf-1', - parentTabId: 'tab-1', - leafId: 'leaf-1', - title: 'Claude Code', - isActive: true, - status: 'ready', - terminal: 'terminal-replacement' - } - ]), - 'env-1' - ) - await Promise.resolve() - - expect(listener).toHaveBeenCalledOnce() - expect(listener).toHaveBeenCalledWith({ - surfacePresent: true, - terminalHandle: 'terminal-replacement' - }) - expect(getWebSessionTerminalHandleSubscriberCountForTests()).toBe(1) - unsubscribe() - expect(getWebSessionTerminalHandleSubscriberCountForTests()).toBe(0) - }) - it('distinguishes a pending handle from an explicitly removed surface', async () => { const listener = vi.fn() const unsubscribe = subscribeAcceptedWebSessionTerminalHandle( diff --git a/src/renderer/src/store/plugin-icon-themes.test.ts b/src/renderer/src/store/plugin-icon-themes.test.ts index 15d0a4c760b..46676936a48 100644 --- a/src/renderer/src/store/plugin-icon-themes.test.ts +++ b/src/renderer/src/store/plugin-icon-themes.test.ts @@ -91,50 +91,3 @@ it('drops the previous icons when the next selected theme fails to load', async await waitFor(() => expect(document.getElementById('orca-plugin-icon-theme')).toBeNull()) expect(store.useActivePluginIconTheme.getState().theme).toBeNull() }) - -it('reloads the active icon theme when a same-metadata update changes its content', async () => { - const metadata = { id: 'plugin:samples.icons/minimal', pluginKey: 'samples.icons', label: 'A' } - let listed: { id: string; pluginKey: string; label: string; revision?: string }[] = [ - { ...metadata, revision: 'r1' } - ] - const listeners: ((event: PluginChangeEvent) => void)[] = [] - const loadIconTheme = vi.fn(async () => ({ - ...listed[0], - svgs: [''], - icons: { file: 0 }, - fileNames: {}, - fileExtensions: {}, - folderNames: {}, - folderNamesExpanded: {} - })) - vi.stubGlobal('api', { - plugins: { - listIconThemes: async () => listed, - loadIconTheme, - onChanged: (listener: (event: PluginChangeEvent) => void) => { - listeners.push(listener) - return () => {} - } - } - }) - const store = await import('./plugin-icon-themes') - renderHook(() => store.usePluginIconThemeActivation(metadata.id)) - await waitFor(() => expect(loadIconTheme).toHaveBeenCalledTimes(1)) - - const refresh = async () => { - act(() => listeners.forEach((listener) => listener({ contentPacksChanged: true }))) - await act(async () => { - await store.usePluginIconThemeStore.getState().fetchEntries() - }) - } - listed = [ - { ...metadata, revision: 'r1' }, - { ...metadata, id: 'plugin:samples.icons/other' } - ] - await refresh() - expect(loadIconTheme).toHaveBeenCalledTimes(1) - - listed = [{ ...metadata, revision: 'r2' }] - await refresh() - await waitFor(() => expect(loadIconTheme).toHaveBeenCalledTimes(2)) -}) diff --git a/src/renderer/src/store/repos/dropped-repo-worktree-rows.test.ts b/src/renderer/src/store/repos/dropped-repo-worktree-rows.test.ts index 080823a7ab3..7c82ab5ef1a 100644 --- a/src/renderer/src/store/repos/dropped-repo-worktree-rows.test.ts +++ b/src/renderer/src/store/repos/dropped-repo-worktree-rows.test.ts @@ -97,24 +97,6 @@ describe('a fetched catalog that drops a repo registration', () => { ]) }) - it('drops the pre-host rows too once no host registers the repo id', async () => { - const store = createTestStore() - seedStore(store, { - repos: [serverRepo('old')], - worktreesByRepo: { - old: [ - serverRow('old'), - makeWorktree({ id: 'old::/legacy', repoId: 'old', path: '/legacy' }) - ] - } - }) - serverRepos = [serverRepo('copied')] - - await store.getState().fetchRuntimeEnvironmentRepos('env-1') - - expect(store.getState().worktreesByRepo.old).toEqual([]) - }) - it('keeps the rows of a repo added while the catalog was in flight', async () => { const store = createTestStore() seedStore(store, { diff --git a/src/renderer/src/store/slices/tab-view-mode.test.ts b/src/renderer/src/store/slices/tab-view-mode.test.ts index e003f9ee11a..a6690302fa0 100644 --- a/src/renderer/src/store/slices/tab-view-mode.test.ts +++ b/src/renderer/src/store/slices/tab-view-mode.test.ts @@ -68,27 +68,6 @@ describe('tab view mode', () => { seedTwoLeafTabs(store) }) - it('toggles a tab to chat and back to terminal', async () => { - expect(findTab(store, 'left-tab')?.viewMode).toBeUndefined() - - store.getState().toggleTabViewMode('left-tab') - await layoutApplyTestTransport.settle() - expect(findTab(store, 'left-tab')?.viewMode).toBe('chat') - - store.getState().toggleTabViewMode('left-tab') - await layoutApplyTestTransport.settle() - expect(findTab(store, 'left-tab')?.viewMode).toBe('terminal') - }) - - it('setTabViewMode sets an explicit mode', async () => { - store.getState().setTabViewMode('left-tab', 'chat') - await layoutApplyTestTransport.settle() - expect(findTab(store, 'left-tab')?.viewMode).toBe('chat') - store.getState().setTabViewMode('left-tab', 'terminal') - await layoutApplyTestTransport.settle() - expect(findTab(store, 'left-tab')?.viewMode).toBe('terminal') - }) - it('mutates only the targeted tab, leaving a split sibling unchanged', async () => { store.getState().toggleTabViewMode('left-tab') await layoutApplyTestTransport.settle() @@ -97,12 +76,6 @@ describe('tab view mode', () => { expect(findTab(store, 'right-tab')?.viewMode).toBeUndefined() }) - it('is a no-op for an unknown tab id', () => { - const before = store.getState().unifiedTabsByWorktree[WT] - store.getState().toggleTabViewMode('missing-tab') - expect(store.getState().unifiedTabsByWorktree[WT]).toBe(before) - }) - // Local toggles must mirror viewMode onto terminal rows used by recovery, matching host sync. describe('mirrors onto the terminal row', () => { function terminalRow(tabId: string) { diff --git a/src/renderer/src/store/slices/worktrees/host-scoped-row-merges.test.ts b/src/renderer/src/store/slices/worktrees/host-scoped-row-merges.test.ts index 2369ca97c85..406f2b3913e 100644 --- a/src/renderer/src/store/slices/worktrees/host-scoped-row-merges.test.ts +++ b/src/renderer/src/store/slices/worktrees/host-scoped-row-merges.test.ts @@ -52,24 +52,6 @@ describe('row writers touch only the copy on their own host', () => { expect(rows).toHaveLength(2) }) - it('a server create leaves the local twin alone', () => { - const store = createTestStore() - const { local } = seedTwins(store) - applyCreatedWorktree( - store.setState, - REPO_ID, - { - worktree: makeWorktree({ id: WORKTREE_ID, repoId: REPO_ID, displayName: 'fresh remote' }) - } satisfies CreateWorktreeResult, - 'runtime:hub' - ) - const rows = store.getState().worktreesByRepo[REPO_ID]! - expect(rows.find((row) => !row.runtimeOwnerEnvironmentId)).toEqual(local) - expect(rows.find((row) => row.runtimeOwnerEnvironmentId === 'hub')?.displayName).toBe( - 'fresh remote' - ) - }) - it("a server's lineage reply updates its own copy, never the focused local twin", () => { const store = createTestStore() const { local } = seedTwins(store) diff --git a/src/renderer/src/store/slices/wsl-moved-project-chat-hydration.test.ts b/src/renderer/src/store/slices/wsl-moved-project-chat-hydration.test.ts index b6087e07939..fce46e68a98 100644 --- a/src/renderer/src/store/slices/wsl-moved-project-chat-hydration.test.ts +++ b/src/renderer/src/store/slices/wsl-moved-project-chat-hydration.test.ts @@ -114,14 +114,6 @@ it("keeps only a moved project's chat tab through hydration and persistence", () expect(persisted.unifiedTabs?.[MOVED]?.map((entry) => entry.id)).toEqual([CHAT]) }) -it("never treats a paired host's partition as the moved local workspace", () => { - const session = movedSession() - expect( - collectWslMovedWorkspaceKeysFromSession(session, projects, { [MOVED]: 'runtime:wsl-ubuntu' }) - ).toEqual([]) - expect(collectWslMovedWorkspaceKeysFromSession(session, [])).toEqual([]) -}) - it("lists the moved chats under their project, and never the host's listed worktree", () => { const store = createTestStore() hydrate(store, movedSession()) 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 index d97df520570..dc17f743963 100644 --- 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 @@ -2,10 +2,8 @@ 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, - isTabPaneTreeHeld -} from '@/runtime/layout-owner-pane-intents' +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' @@ -116,24 +114,4 @@ describe('the chat pane in a layout-owner build', () => { applySent() expect(mainTab(tabId).panes.chatLeafId).toBeUndefined() }) - - it('holds frames off the tab until main answers, so a frame cannot undo the pick', () => { - const tabId = heldTab() - mocks.callRuntimeRpc.mockImplementationOnce(() => new Promise(() => {})) - - setChatLeaf(tabId, LEAF) - - expect(isTabPaneTreeHeld(tabId)).toBe(true) - }) - - it('sends nothing for a write that keeps the chat pane, or with the switch off', () => { - const tabId = heldTab() - const layout = store.getState().terminalLayoutsByTabId[tabId]! - store.getState().setTabLayout(tabId, { ...layout, expandedLeafId: LEAF }) - expect(mocks.callRuntimeRpc).not.toHaveBeenCalled() - - mocks.layoutOwnerIsMain = false - setChatLeaf(tabId, LEAF) - expect(mocks.callRuntimeRpc).not.toHaveBeenCalled() - }) }) 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 a56ac98f8c6..0568dd0428e 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 @@ -8,32 +8,6 @@ import { createTestStore, makeLayout } from '../slices/store-test-helpers' afterEach(resetTerminalInputActivityCoalescingForTests) describe('terminal no-op subscriber budget', () => { - it('does not publish missing-entry cleanup and restart actions', () => { - const store = createTestStore() - const before = store.getState() - const listener = vi.fn() - store.subscribe(listener) - - for (let i = 0; i < 25; i += 1) { - const s = store.getState() - s.replaceTerminalLayoutPanePtyId('missing', 'leaf', 'pty') - expect(s.consumeSuppressedPtyExit('missing')).toBe(false) - expect(s.consumePendingPaneAccountRestart('missing')).toBeNull() - s.clearCodexRestartNotice('missing') - s.dismissCodexRestartNotices(['missing']) - s.reopenCodexRestartPrompt('missing') - s.markNativeChatLaunchPromptFailed('missing') - s.clearNativeChatLaunchPrompt('missing') - s.markNativeChatLaunchDraftAdopted('missing') - s.resolveNativeChatLaunchDraft('missing', { text: 'draft', createdAt: 1 }) - s.clearNativeChatLaunchDraft('missing') - s.removeDeferredSshSessionId('missing') - } - - expect(listener).not.toHaveBeenCalled() - expect(store.getState()).toBe(before) - }) - 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 @@ -81,23 +55,6 @@ describe('terminal no-op subscriber budget', () => { expect(store.getState().nativeChatLaunchDraftByTabId.tab).toBe(draft) }) - it('consumes real restart entries and leaves repeated consumes silent', () => { - const store = createTestStore() - store.getState().suppressPtyExit('pty') - store.getState().queuePaneAccountRestarts(['pty'], 'codex') - const listener = vi.fn() - store.subscribe(listener) - - expect(store.getState().consumeSuppressedPtyExit('pty')).toBe(true) - expect(store.getState().consumePendingPaneAccountRestart('pty')).toBe('codex') - expect(listener).toHaveBeenCalledTimes(2) - const before = store.getState() - expect(store.getState().consumeSuppressedPtyExit('pty')).toBe(false) - expect(store.getState().consumePendingPaneAccountRestart('pty')).toBeNull() - expect(listener).toHaveBeenCalledTimes(2) - expect(store.getState()).toBe(before) - }) - it('drops a trailing input flush after pane teardown without publishing', () => { const store = createTestStore() store.getState().recordTerminalInput('tab:leaf', 1000) @@ -113,34 +70,4 @@ describe('terminal no-op subscriber budget', () => { expect(store.getState()).toBe(before) expect(store.getState().lastTerminalInputAtByPaneKey['tab:leaf']).toBeUndefined() }) - - it('dismisses, reopens and clears restart notices without replaying no-op notifications', () => { - const store = createTestStore() - store - .getState() - .markCodexRestartNotices([ - { ptyId: 'pty', previousAccountLabel: 'old', nextAccountLabel: 'new' } - ]) - const listener = vi.fn() - store.subscribe(listener) - const actions = [ - () => store.getState().dismissCodexRestartNotices(['pty']), - () => store.getState().reopenCodexRestartPrompt('pty'), - () => store.getState().clearCodexRestartNotice('pty') - ] - for (const [index, action] of actions.entries()) { - if (index === 1) { - store.getState().queuePaneAccountRestarts(['pty'], 'codex') - } - listener.mockClear() - action() - expect(listener).toHaveBeenCalledTimes(1) - const before = store.getState() - action() - expect(listener).toHaveBeenCalledTimes(1) - expect(store.getState()).toBe(before) - } - expect(store.getState().codexRestartNoticeByPtyId.pty).toBeUndefined() - expect(store.getState().pendingPaneAccountRestarts.pty).toBeUndefined() - }) }) diff --git a/src/renderer/src/web/preload-api/web-jira-filter-state-api.test.ts b/src/renderer/src/web/preload-api/web-jira-filter-state-api.test.ts index 3d9f8023461..d8df58c4ab6 100644 --- a/src/renderer/src/web/preload-api/web-jira-filter-state-api.test.ts +++ b/src/renderer/src/web/preload-api/web-jira-filter-state-api.test.ts @@ -50,11 +50,6 @@ vi.mock('../../runtime/runtime-rpc-client', () => ({ assertRuntimeEnvironmentCapability: vi.fn(), callRuntimeRpc: rpc })) - -const savedState = { - filters: [{ id: 'mine', name: 'My work', jql: 'project = APP' }], - activeFilterId: 'mine' -} const quickState = { statuses: ['In Progress'] } const BOTH = [JIRA_SAVED_FILTER_RUNTIME_CAPABILITY, JIRA_STATUS_FILTER_RUNTIME_CAPABILITY] @@ -69,26 +64,6 @@ beforeEach(() => { }) afterEach(() => vi.unstubAllGlobals()) -it('reads and writes Jira filter state on the paired host from the local target', async () => { - runtime.status.mockResolvedValue({ capabilities: BOTH }) - runtime.call.mockImplementation(async (method: string) => - method.includes('Saved') ? savedState : quickState - ) - - expect(await readJiraSavedFilterState({ kind: 'local' })).toEqual(savedState) - await mutateJiraSavedFilters({ kind: 'local' }, { kind: 'select', id: 'mine' }) - await readJiraQuickFilterState({ kind: 'local' }) - await writeJiraQuickFilterState({ kind: 'local' }, quickState) - - expect(runtime.call.mock.calls).toEqual([ - ['ui.getJiraSavedFilters', undefined, 15_000], - ['ui.mutateJiraSavedFilters', { kind: 'select', id: 'mine' }, 15_000], - ['ui.getJiraQuickFilters', undefined, 15_000], - ['ui.setJiraQuickFilters', quickState, 15_000] - ]) - expect(rpc).not.toHaveBeenCalled() -}) - it('reports update-required for an older host and never sends the new methods', async () => { runtime.status.mockResolvedValue({ capabilities: [] }) @@ -107,17 +82,6 @@ it('reports update-required for an older host and never sends the new methods', expect(runtime.call).not.toHaveBeenCalled() }) -it('rejects host failures instead of returning empty or unsaved state', async () => { - runtime.status.mockRejectedValueOnce(new Error('Connection lost')) - await expect(readJiraSavedFilterState({ kind: 'local' })).rejects.toThrow('Connection lost') - - runtime.status.mockResolvedValue({ capabilities: BOTH }) - runtime.call.mockRejectedValue(new Error('Host write failed')) - await expect(writeJiraQuickFilterState({ kind: 'local' }, quickState)).rejects.toThrow( - 'Host write failed' - ) -}) - it('does not send to a host paired in while the capability probe was in flight', async () => { runtime.status.mockResolvedValue({ capabilities: BOTH }) runtime.idAfterProbe = 'host-b' @@ -127,17 +91,3 @@ it('does not send to a host paired in while the capability probe was in flight', ).rejects.toThrow('Pairing changed') expect(runtime.call).not.toHaveBeenCalled() }) - -it('keeps explicit environment targets on the environment RPC path', async () => { - rpc.mockResolvedValue(savedState) - - await readJiraSavedFilterState({ kind: 'environment', environmentId: 'host-c' }) - - expect(rpc).toHaveBeenCalledWith( - { kind: 'environment', environmentId: 'host-c' }, - 'ui.getJiraSavedFilters', - undefined, - { timeoutMs: 15_000 } - ) - expect(runtime.call).not.toHaveBeenCalled() -}) diff --git a/src/renderer/src/web/web-clipboard-copy-fallback.test.ts b/src/renderer/src/web/web-clipboard-copy-fallback.test.ts deleted file mode 100644 index 28e9f1dc4dd..00000000000 --- a/src/renderer/src/web/web-clipboard-copy-fallback.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { copyClipboardTextViaExecCommand } from './web-clipboard-copy-fallback' - -type FakeDocOptions = { - dispatchesCopyEvent?: boolean - execCommandResult?: boolean - execCommandThrows?: boolean - withClipboardData?: boolean -} - -function createFakeDocument(options?: FakeDocOptions) { - const listeners: ((event: unknown) => void)[] = [] - const clipboardData = { setData: vi.fn() } - const stopImmediatePropagation = vi.fn() - const createElement = vi.fn() - const appendChild = vi.fn() - const execCommand = vi.fn((command: string) => { - if (options?.execCommandThrows) { - throw new Error('execCommand denied') - } - if (command !== 'copy') { - return false - } - if (options?.dispatchesCopyEvent ?? true) { - for (const listener of listeners.slice()) { - listener({ - clipboardData: (options?.withClipboardData ?? true) ? clipboardData : undefined, - preventDefault: vi.fn(), - stopImmediatePropagation - }) - } - } - return options?.execCommandResult ?? true - }) - const doc = { - activeElement: { focus: vi.fn() }, - createElement, - execCommand, - addEventListener: vi.fn((type: string, listener: (event: unknown) => void) => { - if (type === 'copy') { - listeners.push(listener) - } - }), - removeEventListener: vi.fn((type: string, listener: (event: unknown) => void) => { - if (type === 'copy') { - const index = listeners.indexOf(listener) - if (index !== -1) { - listeners.splice(index, 1) - } - } - }), - body: { appendChild } - } as unknown as Document - - return { - doc, - clipboardData, - stopImmediatePropagation, - createElement, - appendChild, - execCommand, - listeners - } -} - -describe('copyClipboardTextViaExecCommand', () => { - it('serves the text from the copy event without touching the DOM', () => { - const { doc, clipboardData, createElement, appendChild, execCommand } = createFakeDocument() - - expect(copyClipboardTextViaExecCommand('terminal selection', doc)).toBe(true) - expect(clipboardData.setData).toHaveBeenCalledWith('text/plain', 'terminal selection') - expect(execCommand).toHaveBeenCalledWith('copy') - expect(createElement).not.toHaveBeenCalled() - expect(appendChild).not.toHaveBeenCalled() - }) - - it('removes the copy listener after a successful copy', () => { - const { doc, listeners } = createFakeDocument() - - copyClipboardTextViaExecCommand('copy me', doc) - - expect(listeners).toHaveLength(0) - }) - - it('removes the copy listener when execCommand throws', () => { - const { doc, listeners } = createFakeDocument({ execCommandThrows: true }) - - expect(copyClipboardTextViaExecCommand('copy me', doc)).toBe(false) - expect(listeners).toHaveLength(0) - }) - - it('reports failure when no copy event is dispatched', () => { - const { doc, createElement, appendChild } = createFakeDocument({ dispatchesCopyEvent: false }) - - expect(copyClipboardTextViaExecCommand('copy me', doc)).toBe(false) - expect(createElement).not.toHaveBeenCalled() - expect(appendChild).not.toHaveBeenCalled() - }) - - it('reports failure when the copy event has no clipboardData', () => { - const { doc } = createFakeDocument({ withClipboardData: false }) - - expect(copyClipboardTextViaExecCommand('copy me', doc)).toBe(false) - }) - - it('reports failure when execCommand reports failure', () => { - const { doc } = createFakeDocument({ execCommandResult: false }) - - expect(copyClipboardTextViaExecCommand('copy me', doc)).toBe(false) - }) - - it('returns false when the document has no execCommand', () => { - const doc = { addEventListener: vi.fn() } as unknown as Document - - expect(copyClipboardTextViaExecCommand('copy me', doc)).toBe(false) - }) - - it('returns false when the document has no event listener API', () => { - const doc = { execCommand: vi.fn() } as unknown as Document - - expect(copyClipboardTextViaExecCommand('copy me', doc)).toBe(false) - }) - - it('leaves focus and the page selection untouched', () => { - const { doc } = createFakeDocument() - const focus = vi.fn() - const selection = { - rangeCount: 1, - getRangeAt: vi.fn(), - removeAllRanges: vi.fn(), - addRange: vi.fn() - } - ;(doc as unknown as { activeElement: unknown }).activeElement = { focus } - ;(doc as unknown as { getSelection: () => unknown }).getSelection = () => selection - - expect(copyClipboardTextViaExecCommand('copy me', doc)).toBe(true) - expect(selection.removeAllRanges).not.toHaveBeenCalled() - expect(focus).not.toHaveBeenCalled() - }) -}) diff --git a/src/shared/agent-session/journal/agent-session-journal-producer.test.ts b/src/shared/agent-session/journal/agent-session-journal-producer.test.ts index c18d7f577d2..fd92654703b 100644 --- a/src/shared/agent-session/journal/agent-session-journal-producer.test.ts +++ b/src/shared/agent-session/journal/agent-session-journal-producer.test.ts @@ -5,23 +5,10 @@ import { } from '../../agent-session-journal-producer' describe('isRootAgentJournalItem', () => { - it("reads a row carrying no agent id as the session's own", () => { - expect(isRootAgentJournalItem({})).toBe(true) - }) - - it('reads a row carrying one as a subagent’s', () => { - expect(isRootAgentJournalItem({ agentId: 'task-1' })).toBe(false) - }) - it('reads an id that failed to resolve as a subagent’s, not as root', () => { // Presence, not truthiness: an unresolved child id must never attribute its content to the parent. expect(isRootAgentJournalItem({ agentId: '' })).toBe(false) }) - - it('reads a missing item as root rather than throwing', () => { - // Every caller walks a list backwards with an index that can fall off it. - expect(isRootAgentJournalItem(undefined)).toBe(true) - }) }) describe('agentJournalLinkageFields', () => { @@ -31,15 +18,4 @@ describe('agentJournalLinkageFields', () => { expect(Object.keys(agentJournalLinkageFields({ agentId: 'task-1' }))).toEqual(['agentId']) expect(agentJournalLinkageFields(undefined)).toEqual({}) }) - - it('carries every member of the bundle through', () => { - const linkage = { - agentId: 'task-1', - parentAgentId: 'task-parent', - providerParentRef: 'toolu_1', - producerKind: 'background' as const, - attempt: 3 - } - expect(agentJournalLinkageFields(linkage)).toEqual(linkage) - }) }) diff --git a/src/wsl-guest/dispatcher-silent-client-reaper.test.ts b/src/wsl-guest/dispatcher-silent-client-reaper.test.ts index 82f49494e6c..96619d65d1f 100644 --- a/src/wsl-guest/dispatcher-silent-client-reaper.test.ts +++ b/src/wsl-guest/dispatcher-silent-client-reaper.test.ts @@ -29,46 +29,6 @@ describe('RelayDispatcher silent-client reaper', () => { expect(detachListener).toHaveBeenCalledWith(clientId, 'local') }) - it('keeps a quiet but answering client attached', () => { - const detachListener = vi.fn() - dispatcher = new RelayDispatcher(() => true) - dispatcher.onClientDetached(detachListener) - const clientId = dispatcher.attachClient(() => true) - - for (let tick = 0; tick < 10; tick += 1) { - vi.advanceTimersByTime(KEEPALIVE_SEND_MS) - dispatcher.feedClient(clientId, encodeKeepAliveFrame(tick + 1, 0)) - } - - // Assert this client specifically; the unfed primary in this harness proves nothing about the socket under test. - expect(detachListener).not.toHaveBeenCalledWith(clientId, expect.anything()) - }) - - it('does not reap every client on the first tick after the host slept', () => { - const detachListener = vi.fn() - dispatcher = new RelayDispatcher(() => true) - dispatcher.onClientDetached(detachListener) - dispatcher.attachClient(() => true) - - // One tick fires far late because the process was paused, not because the peers went away. - vi.setSystemTime(10 * 60_000) - vi.advanceTimersByTime(KEEPALIVE_SEND_MS) - - expect(detachListener).not.toHaveBeenCalled() - }) - - it('does not judge a client that has not spoken yet by the silence window', () => { - // A slow initial handshake can outlast the silence window; give never-speaking clients a wider bound. - const detachListener = vi.fn() - dispatcher = new RelayDispatcher(() => true) - dispatcher.onClientDetached(detachListener) - const clientId = dispatcher.attachClient(() => true) - - vi.advanceTimersByTime(TIMEOUT_MS * 5) - - expect(detachListener).not.toHaveBeenCalledWith(clientId, expect.anything()) - }) - it('still bounds a client that never speaks at all', () => { // Bound attach age because a never-speaking client has no lastReceivedAt to expire. const detachListener = vi.fn()