From dc57b3d6e81c9825446a520bf0aee8600a2f5586 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Tue, 27 Oct 2026 07:30:16 -0700 Subject: [PATCH] test: remove 61 low-value runtime tests and five suites (#34431) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/reliability-gates.jsonc | 2 +- config/scripts/ci-shard-timings.json | 5 - .../runtime-environment-ssh-state.test.ts | 231 +------------- .../runtime-file-client-download.test.ts | 53 ---- ...untime-file-client-external-import.test.ts | 290 +----------------- ...ime-file-client-mutation-ownership.test.ts | 250 --------------- .../runtime/runtime-file-client-watch.test.ts | 195 ------------ .../runtime-host-connection-state.test.ts | 53 ---- .../runtime-host-contact-parity.test.ts | 169 ---------- ...me-jira-board-filter-compatibility.test.ts | 23 -- .../runtime-jira-connection-status.test.ts | 137 --------- .../runtime-jira-saved-filter-state.test.ts | 68 ---- .../runtime-jira-status-filters.test.ts | 84 ----- .../runtime-server-directory-browser.test.ts | 66 ---- .../runtime-ssh-target-management.test.ts | 86 ------ 15 files changed, 3 insertions(+), 1709 deletions(-) delete mode 100644 src/renderer/src/runtime/runtime-host-contact-parity.test.ts delete mode 100644 src/renderer/src/runtime/runtime-jira-saved-filter-state.test.ts delete mode 100644 src/renderer/src/runtime/runtime-jira-status-filters.test.ts delete mode 100644 src/renderer/src/runtime/runtime-server-directory-browser.test.ts delete mode 100644 src/renderer/src/runtime/runtime-ssh-target-management.test.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 5c76318168e..a10ada80ee9 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -10855,7 +10855,7 @@ "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/settings.test.ts src/main/ipc/runtime-environments/runtime-environments-pairing.test.ts src/renderer/src/store/slices/settings.test.ts src/renderer/src/store/slices/browser.test.ts src/renderer/src/components/settings/browser-session-host-selection.test.ts src/renderer/src/components/settings/RuntimeEnvironmentsPane.test.ts src/renderer/src/components/status-bar/SshStatusSegment.test.ts src/renderer/src/components/sidebar/use-add-repo-host-selection.test.ts src/renderer/src/hooks/useIpcEvents-terminal-create-surfacing.test.ts src/renderer/src/web/web-preload-api-runtime-environment.test.ts --maxWorkers=1", "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/AddProjectFromFolderDialog.test.tsx src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx src/renderer/src/components/sidebar/AddRepoDialog.default-checkout.test.ts src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts src/renderer/src/components/sidebar/project-added-default-checkout.test.ts src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.test.ts src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts src/renderer/src/components/sidebar/worktree-list/listing/host-filtering.test.ts src/renderer/src/lib/resolved-worktree-execution-host.test.ts src/renderer/src/lib/worktree-runtime-owner.test.ts src/renderer/src/store/selectors.test.ts src/renderer/src/store/slices/repos-catalog-reconciliation.test.ts src/renderer/src/store/slices/repos-project-groups.test.ts src/renderer/src/store/slices/repos-selected-owner-routing.test.ts src/renderer/src/store/slices/selected-host-active-workspace-identity.test.ts src/renderer/src/store/slices/worktrees-fetch-owner-routing.test.ts --maxWorkers=1", - "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/use-add-repo-host-selection.test.ts src/renderer/src/components/sidebar/add-repo-browse-authority.test.ts src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx src/renderer/src/components/sidebar/AddRepoStartSteps.test.tsx src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/runtime/runtime-server-directory-browser.test.ts src/renderer/src/store/slices/repos-paired-runtime-add.test.ts --maxWorkers=1", + "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/use-add-repo-host-selection.test.ts src/renderer/src/components/sidebar/add-repo-browse-authority.test.ts src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx src/renderer/src/components/sidebar/AddRepoStartSteps.test.tsx src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/store/slices/repos-paired-runtime-add.test.ts --maxWorkers=1", "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/AddProjectFromFolderDialog.test.tsx src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx src/renderer/src/components/sidebar/AddRepoDialog.default-checkout.test.ts src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts src/renderer/src/components/sidebar/project-added-default-checkout.test.ts src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.test.ts src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts src/renderer/src/components/sidebar/worktree-list/listing/host-filtering.test.ts src/renderer/src/lib/resolved-worktree-execution-host.test.ts src/renderer/src/lib/worktree-runtime-owner.test.ts src/renderer/src/store/selectors.test.ts src/renderer/src/store/slices/repos-catalog-reconciliation.test.ts src/renderer/src/store/slices/repos-project-groups.test.ts src/renderer/src/store/slices/repos-selected-owner-routing.test.ts src/renderer/src/store/slices/selected-host-active-workspace-identity.test.ts src/renderer/src/store/slices/worktrees-fetch-owner-routing.test.ts src/renderer/src/runtime/web-runtime-session.test.ts --maxWorkers=1", "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/pr11346-selected-runtime-add.spec.ts --config tests/playwright.config.ts --project electron-headful --workers=1 --reporter=line", "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/pr11346-selected-runtime-add.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1 --reporter=line", diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index 67e476b243b..02099ce9256 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -11235,7 +11235,6 @@ "src/renderer/src/runtime/runtime-hooks-client.test.ts": 132, "src/renderer/src/runtime/runtime-host-account-sign-in-client.test.ts": 189, "src/renderer/src/runtime/runtime-host-connection-state.test.ts": 35, - "src/renderer/src/runtime/runtime-host-contact-parity.test.ts": 40, "src/renderer/src/runtime/runtime-host-contact-regained.test.ts": 408, "src/renderer/src/runtime/runtime-jira-board-filter-compatibility.test.ts": 527, "src/renderer/src/runtime/runtime-jira-client.test.ts": 347, @@ -11245,8 +11244,6 @@ "src/renderer/src/runtime/runtime-jira-image-cache.test.ts": 1065, "src/renderer/src/runtime/runtime-jira-image-client.test.ts": 140, "src/renderer/src/runtime/runtime-jira-payload-stream.test.ts": 165, - "src/renderer/src/runtime/runtime-jira-saved-filter-state.test.ts": 162, - "src/renderer/src/runtime/runtime-jira-status-filters.test.ts": 199, "src/renderer/src/runtime/runtime-jira-summary-generation-client.test.ts": 159, "src/renderer/src/runtime/runtime-layout-client.test.ts": 109, "src/renderer/src/runtime/runtime-legacy-inventory-budget.test.ts": 73, @@ -11267,10 +11264,8 @@ "src/renderer/src/runtime/runtime-rpc-client-status-publication.test.ts": 234, "src/renderer/src/runtime/runtime-rpc-client.test.ts": 129, "src/renderer/src/runtime/runtime-rpc-result.test.ts": 44, - "src/renderer/src/runtime/runtime-server-directory-browser.test.ts": 150, "src/renderer/src/runtime/runtime-skills-client.test.ts": 147, "src/renderer/src/runtime/runtime-skills-delete-client.test.ts": 57, - "src/renderer/src/runtime/runtime-ssh-target-management.test.ts": 63, "src/renderer/src/runtime/runtime-terminal-artifact-read.test.ts": 196, "src/renderer/src/runtime/runtime-terminal-inspection.test.ts": 1950, "src/renderer/src/runtime/runtime-terminal-stream.test.ts": 638, diff --git a/src/renderer/src/runtime/runtime-environment-ssh-state.test.ts b/src/renderer/src/runtime/runtime-environment-ssh-state.test.ts index 7efb1800084..4714522e575 100644 --- a/src/renderer/src/runtime/runtime-environment-ssh-state.test.ts +++ b/src/renderer/src/runtime/runtime-environment-ssh-state.test.ts @@ -3,10 +3,8 @@ import type { SshConnectionState, SshProviderEpoch } from '../../../shared/ssh-t import { useAppStore } from '@/store' import { applyRuntimeEnvironmentSshStateChanged, - connectRuntimeEnvironmentSshTarget, hydrateRuntimeEnvironmentSshState, - refreshRuntimeEnvironmentSshTargetMetadata, - resyncRuntimeEnvironmentSshTargets + refreshRuntimeEnvironmentSshTargetMetadata } from './runtime-environment-ssh-state' import { callRuntimeRpc } from './runtime-rpc-client' @@ -74,52 +72,6 @@ beforeEach(() => { }) describe('hydrateRuntimeEnvironmentSshState', () => { - it('populates the environment bucket with targets, tombstones, and per-target states', async () => { - const envId = nextEnvId() - installRpcResponses({ - targets: [ - { id: 'ssh-1', label: 'devbox' }, - { id: 'ssh-2', label: 'buildbox' } - ], - labels: { 'ssh-old': 'retired box' }, - states: { 'ssh-1': connState('ssh-1', 'connected') } - }) - - await hydrateRuntimeEnvironmentSshState(envId) - - const bucket = useAppStore.getState().sshStateByEnvironment.get(envId) - expect(bucket?.targetsHydrated).toBe(true) - expect(bucket?.targetLabels.get('ssh-1')).toBe('devbox') - expect(bucket?.removedTargetLabels.get('ssh-old')).toBe('retired box') - expect(bucket?.connectionStates.get('ssh-1')?.status).toBe('connected') - expect(bucket?.connectionStates.get('ssh-1')).toMatchObject({ - providerEpoch: 'ssh-1-provider-epoch', - connectionGeneration: 7 - }) - // ssh-2 had no live state: absent, so reads fall back to 'disconnected'. - expect(bucket?.connectionStates.has('ssh-2')).toBe(false) - // Local maps stay untouched. - expect(useAppStore.getState().sshTargetLabels.size).toBe(0) - expect(useAppStore.getState().sshTargetsHydrated).toBe(false) - // Every call was routed to the owning environment. - for (const [target] of callRuntimeRpcMock.mock.calls) { - expect(target).toEqual({ kind: 'environment', environmentId: envId }) - } - }) - - it('skips refetching when already hydrated unless forced', async () => { - const envId = nextEnvId() - installRpcResponses({ targets: [] }) - await hydrateRuntimeEnvironmentSshState(envId) - const callsAfterFirst = callRuntimeRpcMock.mock.calls.length - - await hydrateRuntimeEnvironmentSshState(envId) - expect(callRuntimeRpcMock.mock.calls.length).toBe(callsAfterFirst) - - await hydrateRuntimeEnvironmentSshState(envId, { force: true }) - expect(callRuntimeRpcMock.mock.calls.length).toBeGreaterThan(callsAfterFirst) - }) - it('does not rerun when an ordinary refresh joins a forced hydration', async () => { const envId = nextEnvId() useAppStore @@ -155,41 +107,6 @@ describe('hydrateRuntimeEnvironmentSshState', () => { ).toHaveLength(1) }) - it('hydrates without force after the environment bucket is marked stale', async () => { - const envId = nextEnvId() - useAppStore - .getState() - .setEnvironmentSshTargetsMetadata(envId, [{ id: 'ssh-old', label: 'old box' }]) - useAppStore.getState().markEnvironmentSshStateStale(envId) - installRpcResponses({ - targets: [{ id: 'ssh-new', label: 'new box' }], - states: { 'ssh-new': connState('ssh-new') } - }) - - await hydrateRuntimeEnvironmentSshState(envId) - - const bucket = useAppStore.getState().sshStateByEnvironment.get(envId) - expect(bucket?.targetsHydrated).toBe(true) - expect(bucket?.targetLabels.get('ssh-new')).toBe('new box') - expect(bucket?.connectionStates.get('ssh-new')?.status).toBe('connected') - }) - - it('refreshes only target summaries when hydrated metadata is unchanged', async () => { - const envId = nextEnvId() - installRpcResponses({ - targets: [{ id: 'ssh-1', label: 'devbox' }], - states: { 'ssh-1': connState('ssh-1') } - }) - await hydrateRuntimeEnvironmentSshState(envId) - callRuntimeRpcMock.mockClear() - - await refreshRuntimeEnvironmentSshTargetMetadata(envId) - - expect(callRuntimeRpcMock.mock.calls.map(([, method]) => method)).toEqual([ - 'ssh.listTargetSummaries' - ]) - }) - it('reads state for a labelled target the bucket never recorded a state for', async () => { const envId = nextEnvId() // A failed-connect resync can label a target without ever reading its state. @@ -326,16 +243,6 @@ describe('hydrateRuntimeEnvironmentSshState', () => { ]) }) - it('leaves the bucket un-hydrated when the host lacks the ssh RPC methods', async () => { - const envId = nextEnvId() - installRpcResponses({ failListTargets: true }) - - await expect(hydrateRuntimeEnvironmentSshState(envId)).rejects.toThrow() - - const bucket = useAppStore.getState().sshStateByEnvironment.get(envId) - expect(bucket?.targetsHydrated ?? false).toBe(false) - }) - it('still hydrates the target list when the removed-labels fetch fails', async () => { const envId = nextEnvId() installRpcResponses({ @@ -400,54 +307,9 @@ describe('hydrateRuntimeEnvironmentSshState', () => { expect(useAppStore.getState().sshStateByEnvironment.has(envId)).toBe(false) }) - - it('prunes connection state for targets removed by an authoritative refresh', async () => { - const envId = nextEnvId() - useAppStore.getState().setEnvironmentSshTargetsMetadata(envId, [ - { id: 'ssh-live', label: 'live box' }, - { id: 'ssh-removed', label: 'retired box' } - ]) - useAppStore - .getState() - .setEnvironmentSshConnectionState(envId, 'ssh-live', connState('ssh-live')) - useAppStore - .getState() - .setEnvironmentSshConnectionState(envId, 'ssh-removed', connState('ssh-removed')) - installRpcResponses({ - targets: [{ id: 'ssh-live', label: 'live box' }], - states: { 'ssh-live': connState('ssh-live') } - }) - - await hydrateRuntimeEnvironmentSshState(envId, { force: true }) - - const bucket = useAppStore.getState().sshStateByEnvironment.get(envId) - expect(bucket?.targetLabels.has('ssh-removed')).toBe(false) - expect(bucket?.connectionStates.has('ssh-removed')).toBe(false) - expect(bucket?.connectionStates.get('ssh-live')?.status).toBe('connected') - }) }) describe('applyRuntimeEnvironmentSshStateChanged', () => { - it('applies a known target state directly into the owning bucket without RPC', () => { - const envId = nextEnvId() - useAppStore - .getState() - .setEnvironmentSshTargetsMetadata(envId, [{ id: 'ssh-1', label: 'devbox' }]) - - applyRuntimeEnvironmentSshStateChanged(envId, 'ssh-1', connState('ssh-1', 'disconnected')) - - expect( - useAppStore.getState().sshStateByEnvironment.get(envId)?.connectionStates.get('ssh-1') - ).toMatchObject({ - status: 'disconnected', - providerEpoch: 'ssh-1-provider-epoch', - connectionGeneration: 7 - }) - expect(callRuntimeRpcMock).not.toHaveBeenCalled() - // Local map untouched. - expect(useAppStore.getState().sshConnectionStates.size).toBe(0) - }) - it('rejects partial authority before retaining a runtime-owned state', () => { const envId = nextEnvId() useAppStore @@ -502,95 +364,4 @@ describe('applyRuntimeEnvironmentSshStateChanged', () => { expect(bucket?.connectionStates.has('ssh-gone')).toBe(false) expect(bucket?.removedTargetLabels.get('ssh-gone')).toBe('old devbox') }) - - it('picks up a just-added target through the forced refresh', async () => { - const envId = nextEnvId() - installRpcResponses({ - targets: [{ id: 'ssh-new', label: 'fresh box' }], - states: { 'ssh-new': connState('ssh-new', 'connecting') } - }) - - applyRuntimeEnvironmentSshStateChanged(envId, 'ssh-new', connState('ssh-new', 'connecting')) - await vi.waitFor(() => { - expect( - useAppStore.getState().sshStateByEnvironment.get(envId)?.targetLabels.get('ssh-new') - ).toBe('fresh box') - }) - - expect( - useAppStore.getState().sshStateByEnvironment.get(envId)?.connectionStates.get('ssh-new') - ?.status - ).toBe('connecting') - }) -}) - -describe('connectRuntimeEnvironmentSshTarget', () => { - it('routes ssh.connect to the owning environment and mirrors the returned state', async () => { - const envId = nextEnvId() - callRuntimeRpcMock.mockResolvedValue({ state: connState('ssh-1', 'connected') } as never) - - const state = await connectRuntimeEnvironmentSshTarget(envId, 'ssh-1') - - expect(state?.status).toBe('connected') - expect(callRuntimeRpcMock).toHaveBeenCalledWith( - { kind: 'environment', environmentId: envId }, - 'ssh.connect', - { targetId: 'ssh-1' }, - expect.anything() - ) - expect( - useAppStore.getState().sshStateByEnvironment.get(envId)?.connectionStates.get('ssh-1')?.status - ).toBe('connected') - expect( - useAppStore.getState().sshStateByEnvironment.get(envId)?.connectionStates.get('ssh-1') - ).toMatchObject({ - providerEpoch: 'ssh-1-provider-epoch', - connectionGeneration: 7 - }) - expect(useAppStore.getState().sshConnectionStates.size).toBe(0) - }) - - it('propagates connect failures without writing any state', async () => { - const envId = nextEnvId() - callRuntimeRpcMock.mockRejectedValue(new Error('SSH target not found')) - - await expect(connectRuntimeEnvironmentSshTarget(envId, 'ssh-dead')).rejects.toThrow( - 'SSH target not found' - ) - expect(useAppStore.getState().sshStateByEnvironment.has(envId)).toBe(false) - }) -}) - -describe('resyncRuntimeEnvironmentSshTargets', () => { - it('applies the target list even when the removed-labels refresh fails', async () => { - const envId = nextEnvId() - installRpcResponses({ - targets: [{ id: 'ssh-live', label: 'devbox' }], - failRemovedLabels: true - }) - - await resyncRuntimeEnvironmentSshTargets(envId) - - const bucket = useAppStore.getState().sshStateByEnvironment.get(envId) - expect(bucket?.targetLabels.get('ssh-live')).toBe('devbox') - expect(bucket?.targetsHydrated).toBe(true) - }) - - it('reads the connection state of a host re-added under a new target id', async () => { - const envId = nextEnvId() - useAppStore - .getState() - .setEnvironmentSshTargetsMetadata(envId, [{ id: 'ssh-old', label: 'devbox' }]) - installRpcResponses({ - targets: [{ id: 'ssh-new', label: 'devbox' }], - labels: { 'ssh-old': 'devbox' }, - states: { 'ssh-new': connState('ssh-new') } - }) - - await resyncRuntimeEnvironmentSshTargets(envId) - - const bucket = useAppStore.getState().sshStateByEnvironment.get(envId) - expect(bucket?.targetLabels.get('ssh-new')).toBe('devbox') - expect(bucket?.connectionStates.get('ssh-new')?.status).toBe('connected') - }) }) diff --git a/src/renderer/src/runtime/runtime-file-client-download.test.ts b/src/renderer/src/runtime/runtime-file-client-download.test.ts index c6e73f582d8..0a57b538783 100644 --- a/src/renderer/src/runtime/runtime-file-client-download.test.ts +++ b/src/renderer/src/runtime/runtime-file-client-download.test.ts @@ -95,25 +95,6 @@ describe('runtime file client', () => { expect(fsCancelDownloadedFile).not.toHaveBeenCalled() }) - it('does not open the save dialog when the remote chunk probe fails for transport reasons', async () => { - runtimeEnvironmentCall.mockRejectedValueOnce(new Error('connection dropped')) - - await expect( - downloadRuntimeFile( - { - target: { kind: 'environment' as const, environmentId: 'env-1' }, - worktreeId: 'wt-1', - worktreePath: '/remote/repo' - }, - '/remote/repo/archive.zip', - 'archive.zip' - ) - ).rejects.toThrow('connection dropped') - - expect(fsStartDownloadedFile).not.toHaveBeenCalled() - expect(fsSaveDownloadedFile).not.toHaveBeenCalled() - }) - it('falls back to preview content when older remote runtimes lack chunked download', async () => { fsSaveDownloadedFile.mockResolvedValue({ canceled: false, @@ -205,40 +186,6 @@ describe('runtime file client', () => { }) }) - it('asks users to update older remote runtimes when preview fallback cannot download the file', async () => { - runtimeEnvironmentCall - .mockResolvedValueOnce({ - id: 'chunk-1', - ok: false, - error: { - code: 'method_not_found', - message: 'Unknown method: files.readChunk' - }, - _meta: { runtimeId: 'remote-runtime' } - }) - .mockResolvedValueOnce({ - id: 'preview-1', - ok: false, - error: { code: 'runtime_error', message: 'file_too_large' }, - _meta: { runtimeId: 'remote-runtime' } - }) - - await expect( - downloadRuntimeFile( - { - target: { kind: 'environment' as const, environmentId: 'env-1' }, - worktreeId: 'wt-1', - worktreePath: '/remote/repo' - }, - '/remote/repo/archive.zip', - 'archive.zip' - ) - ).rejects.toThrow('Remote file download requires a newer Orca server') - - expect(fsStartDownloadedFile).not.toHaveBeenCalled() - expect(fsSaveDownloadedFile).not.toHaveBeenCalled() - }) - it('cancels the local temp download when a remote chunk fails', async () => { fsStartDownloadedFile.mockResolvedValue({ canceled: false, diff --git a/src/renderer/src/runtime/runtime-file-client-external-import.test.ts b/src/renderer/src/runtime/runtime-file-client-external-import.test.ts index 536d7cd709c..48c2ff1437a 100644 --- a/src/renderer/src/runtime/runtime-file-client-external-import.test.ts +++ b/src/renderer/src/runtime/runtime-file-client-external-import.test.ts @@ -1,12 +1,9 @@ -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it } from 'vitest' import { importExternalPathsToRuntime } from './runtime-file-import-client' -import { replaceRuntimeEnvironmentRevisions } from './runtime-environment-revision' import { - fsImportExternalPaths, fsStageExternalPathsForRuntimeUpload, fsUploadExternalFileToRuntime, runtimeEnvironmentCall, - runtimeEnvironmentTransportCall, installRuntimeFileClientEnvironment } from './runtime-file-client-test-harness' @@ -59,251 +56,7 @@ function uploadRequests(): UploadRequest[] { return fsUploadExternalFileToRuntime.mock.calls.flat() } -const identityOf = (entry: Record): Record => ({ - byteLength: entry.byteLength, - inode: entry.inode, - deviceId: entry.deviceId, - modifiedAtMs: entry.modifiedAtMs -}) - describe('runtime file client', () => { - it('streams staged directory entries through main instead of sending base64 itself', async () => { - replaceRuntimeEnvironmentRevisions([{ id: 'env-1', createdAt: 1, pairingRevision: 17 }]) - const logo = stagedFile('logo.png', 3, 101) - const large = stagedFile('large.bin', 40 * 1024 * 1024, 102) - fsStageExternalPathsForRuntimeUpload.mockResolvedValue({ - sources: [ - { - sourcePath: '/Users/me/assets', - status: 'staged', - name: 'assets', - kind: 'directory', - entries: [{ relativePath: '', kind: 'directory' }, logo, large] - } - ] - }) - runtimeEnvironmentCall - .mockResolvedValueOnce(notFoundResponse('stat-destination-miss')) - .mockResolvedValueOnce(okResponse('create-destination-dir')) - .mockResolvedValueOnce(notFoundResponse('stat-miss')) - .mockResolvedValueOnce(okResponse('create-dir')) - .mockResolvedValueOnce(okResponse('commit-upload')) - .mockResolvedValueOnce(okResponse('delete-temp')) - .mockResolvedValueOnce(okResponse('commit-large-upload')) - .mockResolvedValueOnce(okResponse('delete-large-temp')) - - await expect( - importExternalPathsToRuntime( - { - target: { kind: 'environment' as const, environmentId: 'env-1' }, - worktreeId: 'wt-1', - worktreePath: '/remote/repo' - }, - ['/Users/me/assets'], - '/remote/repo/uploads' - ) - ).resolves.toEqual({ - results: [ - { - sourcePath: '/Users/me/assets', - status: 'imported', - destPath: '/remote/repo/uploads/assets', - kind: 'directory', - renamed: false - } - ] - }) - - expect(fsStageExternalPathsForRuntimeUpload).toHaveBeenCalledWith({ - sourcePaths: ['/Users/me/assets'], - nameExtensionlessImages: undefined - }) - const transportCalls = runtimeEnvironmentTransportCall.mock.calls.map(([args]) => args) - expect(transportCalls.map((args) => args.method)).toEqual([ - 'status.get', - 'status.get', - 'files.stat', - 'files.createDir', - 'files.stat', - 'files.createDirNoClobber', - 'files.commitUpload', - 'files.delete', - 'files.commitUpload', - 'files.delete' - ]) - // Why: the whole point of the change — no file body crosses this boundary. - expect(transportCalls.some((args) => String(args.method).startsWith('files.writeBase64'))).toBe( - false - ) - expect(transportCalls.every((args) => args.expectedEnvironmentPairingRevision === 17)).toBe( - true - ) - - const uploads = uploadRequests() - expect(uploads).toHaveLength(2) - expect(uploads[0]?.relativePath).toMatch(/^uploads\/assets\/\.logo\.png\.orca-upload-/) - expect(uploads[0]).toEqual({ - environmentId: 'env-1', - sourceRootPath: '/Users/me/assets', - entryRelativePath: 'logo.png', - expected: identityOf(logo), - worktree: 'id:wt-1', - relativePath: uploads[0]?.relativePath, - expectedExecutionHostId: 'local', - expectedSshTargetId: undefined, - expectedSshConnectionGeneration: undefined, - expectedEnvironmentPairingRevision: 17, - expectedEnvironmentRuntimeId: 'remote-runtime' - }) - expect(uploads[1]).toMatchObject({ - entryRelativePath: 'large.bin', - expected: identityOf(large) - }) - expect(uploads[1]?.relativePath).toMatch(/^uploads\/assets\/\.large\.bin\.orca-upload-/) - - expect(runtimeEnvironmentCall).toHaveBeenNthCalledWith(5, { - selector: 'env-1', - method: 'files.commitUpload', - params: { - worktree: 'id:wt-1', - tempRelativePath: uploads[0]?.relativePath, - finalRelativePath: 'uploads/assets/logo.png', - expectedExecutionHostId: 'local', - expectedSshTargetId: undefined, - expectedSshConnectionGeneration: undefined - }, - timeoutMs: 30_000, - expectedEnvironmentPairingRevision: 17, - expectedEnvironmentRuntimeId: 'remote-runtime' - }) - expect(runtimeEnvironmentCall).toHaveBeenNthCalledWith(6, { - selector: 'env-1', - method: 'files.delete', - params: { - worktree: 'id:wt-1', - relativePath: uploads[0]?.relativePath, - recursive: false, - expectedExecutionHostId: 'local', - expectedSshTargetId: undefined, - expectedSshConnectionGeneration: undefined - }, - timeoutMs: 15_000, - expectedEnvironmentPairingRevision: 17, - expectedEnvironmentRuntimeId: 'remote-runtime' - }) - expect(fsImportExternalPaths).not.toHaveBeenCalled() - }) - - it('forwards a single staged file with the identity staging measured', async () => { - const entry = stagedFile('', 40 * 1024 * 1024, 55) - fsStageExternalPathsForRuntimeUpload.mockResolvedValue({ - sources: [ - { - sourcePath: '/Users/me/large.bin', - status: 'staged', - name: 'large.bin', - kind: 'file', - entries: [entry] - } - ] - }) - runtimeEnvironmentCall - .mockResolvedValueOnce(notFoundResponse('stat-destination-miss')) - .mockResolvedValueOnce(okResponse('create-destination-dir')) - .mockResolvedValueOnce(notFoundResponse('stat-miss')) - .mockResolvedValueOnce(okResponse('commit-upload')) - .mockResolvedValueOnce(okResponse('delete-temp')) - - await expect( - importExternalPathsToRuntime( - { - target: { kind: 'environment' as const, environmentId: 'env-1' }, - worktreeId: 'wt-1', - worktreePath: '/remote/repo' - }, - ['/Users/me/large.bin'], - '/remote/repo/uploads' - ) - ).resolves.toEqual({ - results: [ - { - sourcePath: '/Users/me/large.bin', - status: 'imported', - destPath: '/remote/repo/uploads/large.bin', - kind: 'file', - renamed: false - } - ] - }) - - const upload = uploadRequests()[0] - expect(upload?.relativePath).toMatch(/^uploads\/\.large\.bin\.orca-upload-/) - expect(upload?.sourceRootPath).toBe('/Users/me/large.bin') - expect(upload?.entryRelativePath).toBe('') - expect(upload?.expected).toEqual(identityOf(entry)) - expect(runtimeEnvironmentCall).not.toHaveBeenCalledWith( - expect.objectContaining({ method: 'files.writeBase64' }) - ) - expect(runtimeEnvironmentCall).not.toHaveBeenCalledWith( - expect.objectContaining({ method: 'files.writeBase64Chunk' }) - ) - }) - - it('does not commit an upload when the owner generation changes while it streams', async () => { - fsStageExternalPathsForRuntimeUpload.mockResolvedValue({ - sources: [ - { - sourcePath: '/Users/me/large.bin', - status: 'staged', - name: 'large.bin', - kind: 'file', - entries: [stagedFile('', 40 * 1024 * 1024, 55)] - } - ] - }) - runtimeEnvironmentCall - .mockResolvedValueOnce({ - id: 'stat-destination', - ok: true, - result: { size: 0, isDirectory: true, mtime: 1 }, - _meta: { runtimeId: 'remote-runtime' } - }) - .mockResolvedValueOnce(notFoundResponse('stat-file-miss')) - let ownerChanged = false - fsUploadExternalFileToRuntime.mockImplementation(async () => { - ownerChanged = true - return { byteLength: 40 * 1024 * 1024 } - }) - const assertCurrent = vi.fn(() => { - if (ownerChanged) { - throw new Error('runtime owner generation changed') - } - }) - - await expect( - importExternalPathsToRuntime( - { - target: { kind: 'environment' as const, environmentId: 'env-1' }, - worktreeId: 'wt-1', - worktreePath: '/remote/repo' - }, - ['/Users/me/large.bin'], - '/remote/repo/uploads', - { assertCurrent } - ) - ).resolves.toMatchObject({ - results: [{ status: 'failed', reason: 'runtime owner generation changed' }] - }) - - expect(runtimeEnvironmentCall.mock.calls.map((call) => call[0].method)).toEqual([ - 'files.stat', - 'files.stat' - ]) - expect(runtimeEnvironmentCall).not.toHaveBeenCalledWith( - expect.objectContaining({ method: 'files.commitUpload' }) - ) - }) - it('cleans up the staged temp path when the streamed upload fails', async () => { fsStageExternalPathsForRuntimeUpload.mockResolvedValue({ sources: [ @@ -415,45 +168,4 @@ describe('runtime file client', () => { expectedEnvironmentRuntimeId: 'remote-runtime' }) }) - - it('keeps local external imports on filesystem IPC when no runtime is active', async () => { - fsImportExternalPaths.mockResolvedValue({ - results: [ - { - sourcePath: '/Users/me/readme.md', - status: 'imported', - destPath: '/repo/readme.md', - kind: 'file', - renamed: false - } - ] - }) - - await importExternalPathsToRuntime( - { - target: { kind: 'local' as const }, - worktreeId: 'wt-1', - worktreePath: '/repo', - connectionId: 'ssh-1', - expectedSshTargetId: 'ssh-1', - expectedSshConnectionGeneration: 5 - }, - ['/Users/me/readme.md'], - '/repo', - { ensureDestinationDir: true } - ) - - expect(fsImportExternalPaths).toHaveBeenCalledWith({ - sourcePaths: ['/Users/me/readme.md'], - destDir: '/repo', - connectionId: 'ssh-1', - expectedExecutionHostId: 'ssh:ssh-1', - ensureDir: true, - expectedSshTargetId: 'ssh-1', - expectedSshConnectionGeneration: 5 - }) - expect(fsStageExternalPathsForRuntimeUpload).not.toHaveBeenCalled() - expect(fsUploadExternalFileToRuntime).not.toHaveBeenCalled() - expect(runtimeEnvironmentCall).not.toHaveBeenCalled() - }) }) diff --git a/src/renderer/src/runtime/runtime-file-client-mutation-ownership.test.ts b/src/renderer/src/runtime/runtime-file-client-mutation-ownership.test.ts index 2496a60300a..c86f973f0d2 100644 --- a/src/renderer/src/runtime/runtime-file-client-mutation-ownership.test.ts +++ b/src/renderer/src/runtime/runtime-file-client-mutation-ownership.test.ts @@ -3,7 +3,6 @@ import { copyRuntimePath, createRuntimePath, deleteRuntimePath, - readRuntimeDirectory, renameRuntimePath, writeRuntimeFile } from './runtime-file-mutation-client' @@ -17,8 +16,6 @@ import { RUNTIME_PROTOCOL_VERSION } from '../../../shared/protocol-version' import { - fsReadFile, - fsWriteFile, fsCopy, fsCreateFile, fsRename, @@ -33,208 +30,6 @@ import { installRuntimeFileClientEnvironment() describe('runtime file client', () => { - it('routes create, rename, copy, and delete mutations through the selected runtime', async () => { - runtimeEnvironmentCall.mockResolvedValue({ - id: 'rpc-1', - ok: true, - result: { ok: true }, - _meta: { runtimeId: 'remote-runtime' } - }) - const context = { - target: { kind: 'environment' as const, environmentId: 'env-1' }, - worktreeId: 'wt-1', - worktreePath: '/remote/repo', - expectedSshTargetId: 'ssh-1', - expectedSshConnectionGeneration: 7 - } - - await createRuntimePath(context, '/remote/repo/src/new.ts', 'file') - await renameRuntimePath(context, '/remote/repo/src/new.ts', '/remote/repo/src/renamed.ts') - await copyRuntimePath( - context, - '/remote/repo/src/renamed.ts', - '/remote/repo/src/renamed copy.ts' - ) - await deleteRuntimePath(context, '/remote/repo/src/renamed.ts', false) - - expect(runtimeEnvironmentCall).toHaveBeenNthCalledWith(1, { - selector: 'env-1', - method: 'files.createFile', - params: { - worktree: 'id:wt-1', - relativePath: 'src/new.ts', - expectedExecutionHostId: 'ssh:ssh-1', - expectedSshTargetId: 'ssh-1', - expectedSshConnectionGeneration: 7 - }, - timeoutMs: 15_000 - }) - expect(runtimeEnvironmentCall).toHaveBeenNthCalledWith(2, { - selector: 'env-1', - method: 'files.rename', - params: { - worktree: 'id:wt-1', - oldRelativePath: 'src/new.ts', - newRelativePath: 'src/renamed.ts', - expectedExecutionHostId: 'ssh:ssh-1', - expectedSshTargetId: 'ssh-1', - expectedSshConnectionGeneration: 7 - }, - timeoutMs: 15_000 - }) - expect(runtimeEnvironmentCall).toHaveBeenNthCalledWith(3, { - selector: 'env-1', - method: 'files.copy', - params: { - worktree: 'id:wt-1', - sourceRelativePath: 'src/renamed.ts', - destinationRelativePath: 'src/renamed copy.ts', - expectedExecutionHostId: 'ssh:ssh-1', - expectedSshTargetId: 'ssh-1', - expectedSshConnectionGeneration: 7 - }, - timeoutMs: 15_000 - }) - expect(runtimeEnvironmentCall).toHaveBeenNthCalledWith(4, { - selector: 'env-1', - method: 'files.delete', - params: { - worktree: 'id:wt-1', - relativePath: 'src/renamed.ts', - recursive: false, - expectedExecutionHostId: 'ssh:ssh-1', - expectedSshTargetId: 'ssh-1', - expectedSshConnectionGeneration: 7 - }, - timeoutMs: 15_000 - }) - }) - - it('refuses HUB-local mutations before RPC when the HUB lacks ownership support', async () => { - runtimeEnvironmentTransportCall.mockImplementation((args: { method: string }) => { - if (args.method === 'status.get') { - return Promise.resolve({ - id: 'status', - ok: true, - result: { - runtimeProtocolVersion: RUNTIME_PROTOCOL_VERSION, - minCompatibleRuntimeClientVersion: MIN_COMPATIBLE_RUNTIME_CLIENT_VERSION, - capabilities: [] - }, - _meta: { runtimeId: 'old-hub-runtime' } - }) - } - return runtimeEnvironmentCall(args) - }) - - await expect( - writeRuntimeFile( - { - target: { kind: 'environment' as const, environmentId: 'env-old-hub' }, - worktreeId: 'wt-hub-local', - worktreePath: '/hub/repo', - expectedExecutionHostId: 'local' - }, - '/hub/repo/readme.md', - 'changed' - ) - ).rejects.toThrow(FILE_MUTATION_OWNERSHIP_UPDATE_REQUIRED_MESSAGE) - - expect(runtimeEnvironmentTransportCall).toHaveBeenCalledWith({ - selector: 'env-old-hub', - method: 'status.get', - timeoutMs: 15_000 - }) - expect(runtimeEnvironmentCall).not.toHaveBeenCalled() - expect(fsWriteFile).not.toHaveBeenCalled() - }) - - it('refuses nested SSH mutations before RPC when the HUB lacks ownership support', async () => { - runtimeEnvironmentTransportCall.mockImplementation((args: { method: string }) => { - if (args.method === 'status.get') { - return Promise.resolve({ - id: 'status', - ok: true, - result: { - runtimeProtocolVersion: RUNTIME_PROTOCOL_VERSION, - minCompatibleRuntimeClientVersion: MIN_COMPATIBLE_RUNTIME_CLIENT_VERSION, - capabilities: [] - }, - _meta: { runtimeId: 'old-hub-runtime' } - }) - } - return runtimeEnvironmentCall(args) - }) - - await expect( - renameRuntimePath( - { - target: { kind: 'environment' as const, environmentId: 'env-old-hub' }, - worktreeId: 'wt-nested-ssh', - worktreePath: '/ssh/repo', - connectionId: 'hub-ssh-1', - expectedExecutionHostId: 'ssh:hub-ssh-1', - expectedSshTargetId: 'hub-ssh-1', - expectedSshConnectionGeneration: 7 - }, - '/ssh/repo/old.md', - '/ssh/repo/new.md' - ) - ).rejects.toThrow(FILE_MUTATION_OWNERSHIP_UPDATE_REQUIRED_MESSAGE) - - expect(runtimeEnvironmentTransportCall).toHaveBeenCalledWith({ - selector: 'env-old-hub', - method: 'status.get', - timeoutMs: 15_000 - }) - expect(runtimeEnvironmentCall).not.toHaveBeenCalled() - expect(fsRename).not.toHaveBeenCalled() - }) - - it('keeps reads compatible with HUBs that lack mutation ownership support', async () => { - runtimeEnvironmentTransportCall.mockImplementation((args: { method: string }) => { - if (args.method === 'status.get') { - return Promise.resolve({ - id: 'status', - ok: true, - result: { - runtimeProtocolVersion: RUNTIME_PROTOCOL_VERSION, - minCompatibleRuntimeClientVersion: MIN_COMPATIBLE_RUNTIME_CLIENT_VERSION, - capabilities: [] - }, - _meta: { runtimeId: 'old-hub-runtime' } - }) - } - return runtimeEnvironmentCall(args) - }) - runtimeEnvironmentCall.mockResolvedValue({ - id: 'read-dir', - ok: true, - result: [], - _meta: { runtimeId: 'old-hub-runtime' } - }) - - await expect( - readRuntimeDirectory( - { - target: { kind: 'environment' as const, environmentId: 'env-old-hub' }, - worktreeId: 'wt-nested-ssh', - worktreePath: '/ssh/repo', - connectionId: 'hub-ssh-1' - }, - '/ssh/repo' - ) - ).resolves.toEqual([]) - - expect(runtimeEnvironmentCall).toHaveBeenCalledWith({ - selector: 'env-old-hub', - method: 'files.readDir', - params: { worktree: 'id:wt-nested-ssh', relativePath: '' }, - timeoutMs: 15_000 - }) - expect(fsReadFile).not.toHaveBeenCalled() - }) - it('refuses old-HUB imports before staging client-local files', async () => { runtimeEnvironmentTransportCall.mockImplementation((args: { method: string }) => { if (args.method === 'status.get') { @@ -436,49 +231,4 @@ describe('runtime file client', () => { expect(fsCreateFile).not.toHaveBeenCalled() expect(runtimeEnvironmentCall).not.toHaveBeenCalled() }) - - it('keeps copy operations on local filesystem IPC when no runtime is active', async () => { - await copyRuntimePath( - { - target: { kind: 'local' as const }, - worktreeId: 'wt-1', - worktreePath: '/repo' - }, - '/repo/a.md', - '/repo/a copy.md' - ) - - expect(fsCopy).toHaveBeenCalledWith({ - sourcePath: '/repo/a.md', - destinationPath: '/repo/a copy.md', - connectionId: undefined, - expectedExecutionHostId: 'local' - }) - expect(runtimeEnvironmentCall).not.toHaveBeenCalled() - }) - - it('preserves the SSH connection for copy operations when no runtime is active', async () => { - await copyRuntimePath( - { - target: { kind: 'local' as const }, - worktreeId: 'wt-1', - worktreePath: '/repo', - connectionId: 'ssh-1', - expectedSshTargetId: 'ssh-1', - expectedSshConnectionGeneration: 5 - }, - '/repo/a.md', - '/repo/a copy.md' - ) - - expect(fsCopy).toHaveBeenCalledWith({ - sourcePath: '/repo/a.md', - destinationPath: '/repo/a copy.md', - connectionId: 'ssh-1', - expectedExecutionHostId: 'ssh:ssh-1', - expectedSshTargetId: 'ssh-1', - expectedSshConnectionGeneration: 5 - }) - expect(runtimeEnvironmentCall).not.toHaveBeenCalled() - }) }) diff --git a/src/renderer/src/runtime/runtime-file-client-watch.test.ts b/src/renderer/src/runtime/runtime-file-client-watch.test.ts index 212183c6d5a..99a43c49c47 100644 --- a/src/renderer/src/runtime/runtime-file-client-watch.test.ts +++ b/src/renderer/src/runtime/runtime-file-client-watch.test.ts @@ -1,7 +1,6 @@ import { describe, expect, it, vi } from 'vitest' import { subscribeRuntimeFileChanges } from './runtime-file-watch-client' import { - fsOnChanged, runtimeEnvironmentCall, runtimeEnvironmentSubscribe, installRuntimeFileClientEnvironment @@ -10,128 +9,6 @@ import { installRuntimeFileClientEnvironment() describe('runtime file client', () => { - it('uses the local fs changed stream when no runtime environment is active', async () => { - const unsubscribe = vi.fn() - const onPayload = vi.fn() - fsOnChanged.mockReturnValue(unsubscribe) - - await expect( - subscribeRuntimeFileChanges( - { - target: { kind: 'local' as const }, - worktreeId: 'wt-1', - worktreePath: '/repo' - }, - onPayload - ) - ).resolves.toBe(unsubscribe) - - expect(fsOnChanged).toHaveBeenCalledWith(onPayload) - expect(runtimeEnvironmentSubscribe).not.toHaveBeenCalled() - }) - - it('maps runtime file watch events back to fs changed payloads', async () => { - const onPayload = vi.fn() - const unsubscribe = vi.fn() - let onResponse: ((response: unknown) => void) | undefined - runtimeEnvironmentSubscribe.mockImplementation((_args, callbacks) => { - onResponse = callbacks.onResponse - return Promise.resolve({ unsubscribe, sendBinary: vi.fn() }) - }) - - const stop = await subscribeRuntimeFileChanges( - { - target: { kind: 'environment' as const, environmentId: 'env-1' }, - worktreeId: 'wt-1', - worktreePath: '/remote/repo' - }, - onPayload - ) - - expect(runtimeEnvironmentSubscribe).toHaveBeenCalledWith( - { - selector: 'env-1', - method: 'files.watch', - params: { worktree: 'id:wt-1', batchCadence: 'interval' }, - timeoutMs: 15_000 - }, - expect.any(Object) - ) - - onResponse?.({ - id: 'rpc-1', - ok: true, - result: { - type: 'changed', - worktree: 'id:wt-1', - events: [{ kind: 'update', absolutePath: '/remote/repo/readme.md' }] - }, - _meta: { runtimeId: 'remote-runtime' } - }) - - expect(onPayload).toHaveBeenCalledWith({ - worktreePath: '/remote/repo', - events: [{ kind: 'update', absolutePath: '/remote/repo/readme.md' }] - }) - - stop() - expect(unsubscribe).toHaveBeenCalled() - }) - - it('surfaces terminal watcher errors and allows a fresh subscription after stream end', async () => { - const onError = vi.fn() - const unsubscribe = vi.fn() - let callbacks: { onResponse: (response: unknown) => void; onClose: () => void } | undefined - runtimeEnvironmentSubscribe.mockImplementation((_args, nextCallbacks) => { - callbacks = nextCallbacks - return Promise.resolve({ unsubscribe, sendBinary: vi.fn() }) - }) - - await subscribeRuntimeFileChanges( - { - target: { kind: 'environment' as const, environmentId: 'env-1' }, - worktreeId: 'wt-1', - worktreePath: '/remote/repo' - }, - vi.fn(), - onError - ) - callbacks?.onResponse({ - id: 'rpc-error', - ok: true, - result: { type: 'error', message: 'file watcher process crashed repeatedly' }, - _meta: { runtimeId: 'remote-runtime' } - }) - callbacks?.onResponse({ - id: 'rpc-end', - ok: true, - result: { type: 'end' }, - _meta: { runtimeId: 'remote-runtime' } - }) - - expect(onError).toHaveBeenCalledWith( - expect.objectContaining({ message: 'file watcher process crashed repeatedly' }) - ) - expect(unsubscribe).toHaveBeenCalledTimes(1) - - await subscribeRuntimeFileChanges( - { - target: { kind: 'environment' as const, environmentId: 'env-1' }, - worktreeId: 'wt-1', - worktreePath: '/remote/repo' - }, - vi.fn(), - vi.fn() - ) - expect(runtimeEnvironmentSubscribe).toHaveBeenCalledTimes(2) - callbacks?.onResponse({ - id: 'rpc-second-end', - ok: true, - result: { type: 'end' }, - _meta: { runtimeId: 'remote-runtime' } - }) - }) - it('evicts a terminal watch before notifying error listeners that retry', async () => { const callbacks: { onResponse: (response: unknown) => void @@ -311,76 +188,4 @@ describe('runtime file client', () => { }) ) }) - - it('delegates stopped pre-ready web shared file watch cleanup to the subscription handle', async () => { - ;(globalThis as { __ORCA_WEB_CLIENT__?: boolean }).__ORCA_WEB_CLIENT__ = true - const onPayload = vi.fn() - const unsubscribe = vi.fn() - let onResponse: ((response: unknown) => void) | undefined - runtimeEnvironmentSubscribe.mockImplementation((_args, callbacks) => { - onResponse = callbacks.onResponse - return Promise.resolve({ unsubscribe, sendBinary: vi.fn() }) - }) - - const stop = await subscribeRuntimeFileChanges( - { - target: { kind: 'environment' as const, environmentId: 'env-1' }, - worktreeId: 'wt-1', - worktreePath: '/remote/repo' - }, - onPayload - ) - - stop() - expect(unsubscribe).toHaveBeenCalledTimes(1) - expect(runtimeEnvironmentCall).not.toHaveBeenCalledWith( - expect.objectContaining({ method: 'files.unwatch' }) - ) - - onResponse?.({ - id: 'ready', - ok: true, - result: { type: 'ready', subscriptionId: 'files-watch-late' }, - _meta: { runtimeId: 'remote-runtime' } - }) - - expect(unsubscribe).toHaveBeenCalledTimes(1) - expect(runtimeEnvironmentCall).not.toHaveBeenCalledWith( - expect.objectContaining({ method: 'files.unwatch' }) - ) - }) - - it('delegates stopped ready web shared file watch cleanup to the subscription handle', async () => { - ;(globalThis as { __ORCA_WEB_CLIENT__?: boolean }).__ORCA_WEB_CLIENT__ = true - const onPayload = vi.fn() - const unsubscribe = vi.fn() - let onResponse: ((response: unknown) => void) | undefined - runtimeEnvironmentSubscribe.mockImplementation((_args, callbacks) => { - onResponse = callbacks.onResponse - return Promise.resolve({ unsubscribe, sendBinary: vi.fn() }) - }) - - const stop = await subscribeRuntimeFileChanges( - { - target: { kind: 'environment' as const, environmentId: 'env-1' }, - worktreeId: 'wt-1', - worktreePath: '/remote/repo' - }, - onPayload - ) - - onResponse?.({ - id: 'ready', - ok: true, - result: { type: 'ready', subscriptionId: 'files-watch-ready' }, - _meta: { runtimeId: 'remote-runtime' } - }) - - stop() - - expect(unsubscribe).toHaveBeenCalledTimes(1) - expect(runtimeEnvironmentCall).not.toHaveBeenCalledWith( - expect.objectContaining({ method: 'files.unwatch' }) - ) - }) }) diff --git a/src/renderer/src/runtime/runtime-host-connection-state.test.ts b/src/renderer/src/runtime/runtime-host-connection-state.test.ts index 95aa7573763..90ba9d84112 100644 --- a/src/renderer/src/runtime/runtime-host-connection-state.test.ts +++ b/src/renderer/src/runtime/runtime-host-connection-state.test.ts @@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest' import type { RuntimeHostStatusSnapshot } from '../../../shared/runtime-host-status' import type { RuntimeStatus } from '../../../shared/runtime-types' import { - isConnectedRuntimeHostState, isDisconnectedRuntimeHostState, runtimeHostConnectionState, runtimeHostConnectionStateForEntry, @@ -29,37 +28,6 @@ const windowClosedStatus = makeStatus({ }) describe('runtime host connection state', () => { - it('counts connected remote servers as connected hosts', () => { - // Connected means reachable, independent of active/default host; there is no separate available state. - expect(runtimeStatusForOverall('connected')).toBe('connected') - expect(isConnectedRuntimeHostState('connected')).toBe(true) - }) - - it('keeps reconnecting and disconnected remote servers out of the connected count', () => { - expect(runtimeStatusForOverall('reconnecting')).toBe('connecting') - expect(runtimeStatusForOverall('disconnected')).toBe('disconnected') - expect(isConnectedRuntimeHostState('reconnecting')).toBe(false) - expect(isConnectedRuntimeHostState('disconnected')).toBe(false) - }) - - it('distinguishes a connected transport from an unavailable runtime', () => { - expect( - runtimeHostConnectionState({ - hasStatusEntry: true, - status: null, - transportStatus: 'connected' - }) - ).toBe('runtime-unavailable') - expect(runtimeStatusForOverall('runtime-unavailable')).toBe('connected') - expect(isConnectedRuntimeHostState('runtime-unavailable')).toBe(true) - }) - - it('still counts a workspace-window-closed remote server as a connected host', () => { - // Healthy transport remains connected; degraded runtime changes only wording (#12350). - expect(runtimeStatusForOverall('workspace-window-closed')).toBe('connected') - expect(isConnectedRuntimeHostState('workspace-window-closed')).toBe(true) - }) - it('distinguishes a reachable host whose workspace window is closed', () => { expect(runtimeHostConnectionState({ hasStatusEntry: true, status: windowClosedStatus })).toBe( 'workspace-window-closed' @@ -192,27 +160,6 @@ describe('runtime host connection state for a recorded status entry', () => { }) ).toBe('reconnecting') }) - - it('agrees with the status bar that a closed control channel is disconnected', () => { - expect( - runtimeHostConnectionStateForEntry({ - status: makeStatus({ remoteControl: remoteControl('closed') }) - }) - ).toBe('disconnected') - }) - - it('names only the disconnected verdict as disconnected', () => { - expect(isDisconnectedRuntimeHostState('disconnected')).toBe(true) - for (const state of [ - 'connected', - 'checking', - 'reconnecting', - 'runtime-unavailable', - 'workspace-window-closed' - ] as const) { - expect(isDisconnectedRuntimeHostState(state)).toBe(false) - } - }) }) function remoteControl( diff --git a/src/renderer/src/runtime/runtime-host-contact-parity.test.ts b/src/renderer/src/runtime/runtime-host-contact-parity.test.ts deleted file mode 100644 index 3d99216543c..00000000000 --- a/src/renderer/src/runtime/runtime-host-contact-parity.test.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - isRuntimeHostContactRevoked, - type RuntimeHostStatusSnapshot -} from '../../../shared/runtime-host-status' -import { - isRuntimeHostContactRevokedVerdict, - lastRuntimeHostAnswer, - liveRuntimeHostStatus, - runtimeHostContactFromSnapshot -} from '../../../shared/runtime-host-contact' -import type { RuntimeStatus } from '../../../shared/runtime-types' - -type Entry = { - status: RuntimeStatus | null - remoteControl?: RuntimeStatus['remoteControl'] | null - snapshot?: RuntimeHostStatusSnapshot -} - -const VERIFICATIONS = ['checking', 'verified', 'unavailable', 'blocked'] as const -const TRANSPORTS = ['unknown', 'connecting', 'ready', 'disconnected'] as const -const RETIRED = [false, true] as const -const REMOTE_CONTROL_STATES = [ - undefined, - 'ready', - 'awaiting_ready', - 'awaiting_authenticated', - 'reconnecting', - 'closed' -] as const - -function makeStatus(overrides: Partial = {}): RuntimeStatus { - return { - runtimeId: 'rt-1', - rendererGraphEpoch: 0, - graphStatus: 'ready', - authoritativeWindowId: 1, - liveTabCount: 0, - liveLeafCount: 0, - ...overrides - } -} - -function makeRemoteControl( - state: Exclude<(typeof REMOTE_CONTROL_STATES)[number], undefined> -): NonNullable { - return { - state, - pendingRequestCount: 0, - subscriptionCount: 0, - reconnectAttempt: 0, - lastConnectedAt: null, - lastClose: null, - lastError: null - } -} - -function makeSnapshot( - verification: (typeof VERIFICATIONS)[number], - transport: (typeof TRANSPORTS)[number], - retired: boolean, - answered: RuntimeStatus | null -): RuntimeHostStatusSnapshot { - return { - environmentId: 'env-a', - pairingRevision: 1, - sequence: 1, - checkedAt: 1, - status: answered, - verification, - transport, - ...(retired ? { retired: true as const } : {}) - } -} - -/** Every entry shape the derivation can distinguish: 4 x 4 x 2, across each status/diagnostic. */ -function* everySnapshotEntry(): Generator<{ label: string; entry: Entry }> { - for (const verification of VERIFICATIONS) { - for (const transport of TRANSPORTS) { - for (const retired of RETIRED) { - for (const answered of [null, makeStatus()] as const) { - for (const remoteControlState of REMOTE_CONTROL_STATES) { - // Store status exists only for verified unretired probes; enumerate reachable combinations, not an impossible cross-product. - const entryStatus = verification === 'verified' && !retired ? answered : null - const remoteControl = remoteControlState - ? makeRemoteControl(remoteControlState) - : undefined - yield { - label: `${verification}/${transport}/retired=${retired}/answered=${answered !== null}/rc=${remoteControlState ?? 'none'}`, - entry: { - status: entryStatus, - ...(remoteControl ? { remoteControl } : {}), - snapshot: makeSnapshot(verification, transport, retired, answered) - } - } - } - } - } - } - } -} - -describe('the revoked predicate and the contact verdict stay in step', () => { - it('keeps the revoked predicate and the contact verdict in step', () => { - for (const { label, entry } of everySnapshotEntry()) { - expect( - isRuntimeHostContactRevokedVerdict( - runtimeHostContactFromSnapshot(entry.snapshot!, entry.status) - ), - label - ).toBe(isRuntimeHostContactRevoked(entry)) - } - }) -}) - -describe('the contact separates what the host said from what it is worth', () => { - it('retains the host answer through every non-live verdict', () => { - const answered = makeStatus() - for (const [verification, transport, retired] of [ - ['unavailable', 'ready', false], - ['checking', 'connecting', false], - ['unavailable', 'disconnected', false], - ['blocked', 'ready', false], - ['verified', 'ready', true] - ] as const) { - const contact = runtimeHostContactFromSnapshot( - makeSnapshot(verification, transport, retired, answered), - null - ) - expect(contact.verdict, `${verification}/${transport}`).not.toBe('live') - // The fact the host gave us survives; only its currency is in question. - expect(lastRuntimeHostAnswer(contact)).toBe(answered) - expect(liveRuntimeHostStatus(contact)).toBeNull() - } - }) - - it('reports a verified probe as live and nothing else', () => { - const answered = makeStatus() - const contact = runtimeHostContactFromSnapshot( - makeSnapshot('verified', 'ready', false, answered), - answered - ) - expect(contact.verdict).toBe('live') - expect(liveRuntimeHostStatus(contact)).toBe(answered) - expect(lastRuntimeHostAnswer(contact)).toBe(answered) - }) - - it('tells a host that was never reached apart from a handshake in flight', () => { - // Previously collapsed nulls need opposite Connect affordances. - expect( - runtimeHostContactFromSnapshot(makeSnapshot('unavailable', 'unknown', false, null), null) - ).toEqual({ verdict: 'unverifiable', reason: 'never-asked', lastAnswer: null }) - expect( - runtimeHostContactFromSnapshot(makeSnapshot('unavailable', 'connecting', false, null), null) - ).toEqual({ verdict: 'unverifiable', reason: 'transport-connecting', lastAnswer: null }) - }) - - it('tells a refused host apart from a retired pairing', () => { - const answered = makeStatus() - expect( - runtimeHostContactFromSnapshot(makeSnapshot('blocked', 'ready', false, answered), null) - .verdict - ).toBe('refused') - expect( - runtimeHostContactFromSnapshot(makeSnapshot('verified', 'ready', true, answered), null) - .verdict - ).toBe('retired') - }) -}) diff --git a/src/renderer/src/runtime/runtime-jira-board-filter-compatibility.test.ts b/src/renderer/src/runtime/runtime-jira-board-filter-compatibility.test.ts index ea83324944d..8f3ffbd0ac5 100644 --- a/src/renderer/src/runtime/runtime-jira-board-filter-compatibility.test.ts +++ b/src/renderer/src/runtime/runtime-jira-board-filter-compatibility.test.ts @@ -52,26 +52,3 @@ it.each([ expect(call.mock.calls.filter(([args]) => args.method === 'jira.listIssues')).toHaveLength(1) } ) - -it('forwards both filters to a host that supports the combined board query', async () => { - call.mockImplementation(async (args: { method: string }) => - args.method === 'status.get' - ? createCompatibleRuntimeStatusResponse() - : { id: 'board-filter', ok: true, result: [] } - ) - const filters = { statuses: ['Blocked'], currentSprint: true } - await jiraListIssues( - { kind: 'environment', environmentId: 'board-current' }, - 'all', - 50, - 'site-a', - filters - ) - expect(call).toHaveBeenCalledWith( - expect.objectContaining({ - selector: 'board-current', - method: 'jira.listIssues', - params: { filter: 'all', limit: 50, siteId: 'site-a', filters } - }) - ) -}) diff --git a/src/renderer/src/runtime/runtime-jira-connection-status.test.ts b/src/renderer/src/runtime/runtime-jira-connection-status.test.ts index f9437920375..97408b4cf94 100644 --- a/src/renderer/src/runtime/runtime-jira-connection-status.test.ts +++ b/src/renderer/src/runtime/runtime-jira-connection-status.test.ts @@ -2,32 +2,6 @@ import { describe, expect, it } from 'vitest' import { parseJiraConnectionStatus } from './runtime-jira-connection-status' describe('parseJiraConnectionStatus', () => { - it('accepts an upgraded host without changing the Cloud/Server values older readers know', () => { - const site = { - id: 'site-scoped', - siteUrl: 'https://example.atlassian.net', - email: '', - displayName: 'Ada', - accountId: 'account-alpha', - authType: 'cloud', - apiBaseUrl: 'https://api.atlassian.com/ex/jira/cloud-abc' - } - const status = parseJiraConnectionStatus({ - connected: true, - viewer: null, - sites: [site], - supportsScopedTokens: true - }) - expect(status.sites).toEqual([site]) - expect(status.supportsScopedTokens).toBe(true) - expect( - parseJiraConnectionStatus({ connected: true, viewer: null }).supportsScopedTokens - ).toBeUndefined() - expect( - parseJiraConnectionStatus({ connected: true, supportsScopedTokens: 'yes' }) - .supportsScopedTokens - ).toBeUndefined() - }) it.each([ ['undefined', undefined], ['null', null], @@ -37,73 +11,6 @@ describe('parseJiraConnectionStatus', () => { expect(parseJiraConnectionStatus(value)).toEqual({ connected: false, viewer: null }) }) - it('keeps a well-formed status', () => { - const status = { - connected: true, - viewer: { accountId: 'a', email: 'a@example.com', displayName: 'A' }, - sites: [], - activeSiteId: 'site-1', - credentialProtection: 'plaintext' - } - expect(parseJiraConnectionStatus(status)).toEqual(status) - }) - - it('round-trips a populated multi-site status unchanged', () => { - const status = { - connected: true, - viewer: { - accountId: 'account-cloud', - displayName: 'Ada Lovelace', - email: 'ada@example.com', - avatarUrl: 'https://avatar.example/ada.png' - }, - sites: [ - { - id: 'site-server', - siteUrl: 'https://jira.internal.example', - email: '', - displayName: 'Ada (PAT)', - accountId: '', - authType: 'server' - }, - { - id: 'site-cloud', - siteUrl: 'https://example.atlassian.net', - email: 'ada@example.com', - displayName: 'Ada Lovelace', - accountId: 'account-cloud', - authType: 'cloud' - }, - { - id: 'site-legacy', - siteUrl: 'https://legacy.atlassian.net', - email: 'ada@legacy.example', - displayName: 'Ada (legacy)', - accountId: 'account-legacy' - } - ], - activeSiteId: 'site-cloud', - selectedSiteId: 'all', - credentialError: 'Could not decrypt the stored Jira token.', - credentialProtection: 'plaintext' - } - expect(parseJiraConnectionStatus(status)).toEqual(status) - }) - - it.each([ - ['a non-object viewer', { connected: true, viewer: 'me' }], - ['a non-array sites value', { connected: true, viewer: null, sites: {} }] - ])('keeps a connected status with %s', (_label, value) => { - expect(parseJiraConnectionStatus(value)).toEqual({ connected: true, viewer: null }) - }) - - it('fills a missing viewer with null', () => { - expect(parseJiraConnectionStatus({ connected: false })).toEqual({ - connected: false, - viewer: null - }) - }) - it('keeps a connected status while dropping malformed nested values', () => { expect( parseJiraConnectionStatus({ @@ -139,48 +46,4 @@ describe('parseJiraConnectionStatus', () => { selectedSiteId: 'site-1' }) }) - - it('keeps a site with an unknown auth type connected', () => { - expect( - parseJiraConnectionStatus({ - connected: true, - viewer: null, - sites: [ - { - id: 'site-1', - siteUrl: 'https://example.atlassian.net', - email: 'a@example.com', - displayName: 'Example', - accountId: 'account-1', - authType: 'datacenter' - } - ] - }) - ).toEqual({ - connected: true, - viewer: null, - sites: [ - { - id: 'site-1', - siteUrl: 'https://example.atlassian.net', - email: 'a@example.com', - displayName: 'Example', - accountId: 'account-1' - } - ] - }) - }) - - it('ignores the Keychain fields an older host still sends', () => { - const status = parseJiraConnectionStatus({ - connected: true, - viewer: null, - credentialError: 'Could not decrypt saved Jira credential.', - credentialErrorReason: 'keychain_locked', - canMoveCredentialKeyToFile: true - }) - expect(status.credentialError).toBe('Could not decrypt saved Jira credential.') - expect(status).not.toHaveProperty('credentialErrorReason') - expect(status).not.toHaveProperty('canMoveCredentialKeyToFile') - }) }) diff --git a/src/renderer/src/runtime/runtime-jira-saved-filter-state.test.ts b/src/renderer/src/runtime/runtime-jira-saved-filter-state.test.ts deleted file mode 100644 index 4b4caed0a3a..00000000000 --- a/src/renderer/src/runtime/runtime-jira-saved-filter-state.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -// @vitest-environment happy-dom -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { readJiraSavedFilterState, mutateJiraSavedFilters } from './runtime-jira-saved-filter-state' -import { JIRA_SAVED_FILTER_RUNTIME_CAPABILITY } from '../../../shared/jira/jira-saved-filter-capability' -const { assertCapability, rpc } = vi.hoisted(() => ({ assertCapability: vi.fn(), rpc: vi.fn() })) -vi.mock('./runtime-rpc-client', () => ({ - assertRuntimeEnvironmentCapability: assertCapability, - callRuntimeRpc: rpc -})) -const state = { - filters: [{ id: 'mine', name: 'My work', jql: 'project = APP' }], - activeFilterId: 'mine' -} -beforeEach(() => { - vi.resetAllMocks() -}) -afterEach(() => { - vi.unstubAllGlobals() -}) -it('uses local UI state for local execution', async () => { - const read = vi.fn(async () => state) - const mutate = vi.fn(async () => state) - vi.stubGlobal('window', { - api: { ui: { getJiraSavedFilters: read, mutateJiraSavedFilters: mutate } } - }) - expect(await readJiraSavedFilterState({ kind: 'local' })).toEqual(state) - await mutateJiraSavedFilters({ kind: 'local' }, { kind: 'select', id: 'mine' }) - expect(mutate).toHaveBeenCalledWith({ kind: 'select', id: 'mine' }) - expect(rpc).not.toHaveBeenCalled() -}) -it('routes paired/SSH persistence to the selected Jira host rather than this device', async () => { - rpc.mockResolvedValue(state) - const target = { kind: 'environment', environmentId: 'ssh-host-b' } as const - expect(await readJiraSavedFilterState(target)).toEqual(state) - await mutateJiraSavedFilters(target, { kind: 'select', id: 'mine' }) - expect(assertCapability).toHaveBeenCalledWith( - 'ssh-host-b', - JIRA_SAVED_FILTER_RUNTIME_CAPABILITY, - expect.any(String), - 15_000 - ) - expect(rpc).toHaveBeenCalledWith( - target, - 'ui.mutateJiraSavedFilters', - { kind: 'select', id: 'mine' }, - { timeoutMs: 15_000 } - ) - expect(rpc.mock.calls.some(([, method]) => method === 'ui.set')).toBe(false) -}) -it('never sends new fields to an older host or falls back to local persistence', async () => { - assertCapability.mockRejectedValue(new Error('Update the Jira host')) - await expect( - mutateJiraSavedFilters( - { kind: 'environment', environmentId: 'old-host' }, - { kind: 'select', id: 'mine' } - ) - ).rejects.toThrow('Update') - expect(rpc).not.toHaveBeenCalled() -}) -it('propagates loss of contact instead of pretending the write was saved', async () => { - rpc.mockRejectedValue(new Error('Connection lost')) - await expect( - mutateJiraSavedFilters( - { kind: 'environment', environmentId: 'host-a' }, - { kind: 'select', id: null } - ) - ).rejects.toThrow('Connection lost') -}) diff --git a/src/renderer/src/runtime/runtime-jira-status-filters.test.ts b/src/renderer/src/runtime/runtime-jira-status-filters.test.ts deleted file mode 100644 index 2e549c69ffb..00000000000 --- a/src/renderer/src/runtime/runtime-jira-status-filters.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -// @vitest-environment happy-dom -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { jiraListIssues, jiraListIssuesForTarget } from './runtime-jira-client' -import { - readJiraQuickFilterState, - writeJiraQuickFilterState -} from './runtime-jira-quick-filter-state' -import { JIRA_STATUS_FILTER_RUNTIME_CAPABILITY } from '../../../shared/jira/jira-quick-filters' -const { assertCapability, rpc } = vi.hoisted(() => ({ assertCapability: vi.fn(), rpc: vi.fn() })) -vi.mock('./runtime-rpc-client', () => ({ - assertRuntimeEnvironmentCapability: assertCapability, - callRuntimeRpc: rpc -})) -beforeEach(() => { - vi.resetAllMocks() -}) -afterEach(() => { - vi.unstubAllGlobals() -}) -const target = { kind: 'environment', environmentId: 'jira-owner' } as const -it('keeps the old request shape when no statuses are selected', async () => { - rpc.mockResolvedValue([]) - await jiraListIssues(target, 'all', 50, 'site-a') - expect(assertCapability).not.toHaveBeenCalled() - expect(rpc).toHaveBeenCalledWith( - target, - 'jira.listIssues', - { filter: 'all', limit: 50, siteId: 'site-a' }, - { timeoutMs: 30_000 } - ) -}) -it('gates status list filters on the called execution host', async () => { - rpc.mockResolvedValue([]) - await jiraListIssuesForTarget(target, 'all', 50, 'site-a', { statuses: ['In Progress'] }) - expect(assertCapability).toHaveBeenCalledWith( - 'jira-owner', - JIRA_STATUS_FILTER_RUNTIME_CAPABILITY, - expect.any(String), - 15_000 - ) - expect(rpc).toHaveBeenCalledWith( - target, - 'jira.listIssues', - { filter: 'all', limit: 50, siteId: 'site-a', filters: { statuses: ['In Progress'] } }, - { timeoutMs: 30_000 } - ) -}) -it('refuses an older host without silently dropping selection or writing strict resume fields', async () => { - assertCapability.mockRejectedValue(new Error('Update the Jira host')) - await expect( - jiraListIssues(target, 'all', 50, 'site-a', { statuses: ['Blocked'] }) - ).rejects.toThrow('Update') - await expect(writeJiraQuickFilterState(target, { statuses: ['Blocked'] })).rejects.toThrow( - 'Update' - ) - expect(rpc).not.toHaveBeenCalled() -}) -it('routes persisted state through host UI methods and acknowledges the write', async () => { - rpc.mockResolvedValue({ statuses: ['Blocked'] }) - expect(await readJiraQuickFilterState(target)).toEqual({ statuses: ['Blocked'] }) - expect(await writeJiraQuickFilterState(target, { statuses: ['Blocked'] })).toEqual({ - statuses: ['Blocked'] - }) - expect(rpc).toHaveBeenLastCalledWith( - target, - 'ui.setJiraQuickFilters', - { statuses: ['Blocked'] }, - { timeoutMs: 15_000 } - ) -}) -it('uses local IPC and reports a lost write instead of pretending to save', async () => { - const read = vi.fn(async () => ({ statuses: ['Open'] })) - const write = vi.fn(async () => { - throw new Error('Lost host') - }) - vi.stubGlobal('window', { - api: { ui: { getJiraQuickFilters: read, setJiraQuickFilters: write } } - }) - expect(await readJiraQuickFilterState({ kind: 'local' })).toEqual({ statuses: ['Open'] }) - await expect(writeJiraQuickFilterState({ kind: 'local' }, { statuses: [] })).rejects.toThrow( - 'Lost host' - ) - expect(rpc).not.toHaveBeenCalled() -}) diff --git a/src/renderer/src/runtime/runtime-server-directory-browser.test.ts b/src/renderer/src/runtime/runtime-server-directory-browser.test.ts deleted file mode 100644 index a61ee38e977..00000000000 --- a/src/renderer/src/runtime/runtime-server-directory-browser.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { browseRuntimeServerDirectory } from './runtime-server-directory-browser' -import { clearRuntimeCompatibilityCacheForTests } from './runtime-rpc-client' -import { - MIN_COMPATIBLE_RUNTIME_CLIENT_VERSION, - RUNTIME_PROTOCOL_VERSION -} from '../../../shared/protocol-version' -import { stubRuntimeEnvironmentBridge } from './runtime-environment-bridge-fixture' - -const runtimeEnvironmentCall = vi.fn() - -beforeEach(() => { - clearRuntimeCompatibilityCacheForTests() - runtimeEnvironmentCall.mockReset() - runtimeEnvironmentCall.mockImplementation((args: { method: string }) => { - if (args.method === 'status.get') { - return Promise.resolve({ - id: 'status', - ok: true, - result: { - runtimeProtocolVersion: RUNTIME_PROTOCOL_VERSION, - minCompatibleRuntimeClientVersion: MIN_COMPATIBLE_RUNTIME_CLIENT_VERSION - }, - _meta: { runtimeId: 'remote-runtime' } - }) - } - return Promise.resolve({ - id: 'browse', - ok: true, - result: { - resolvedPath: '/home/me', - pathFlavor: 'posix', - entries: [ - { name: '100 - file.txt', isDirectory: false, isSymlink: false }, - { name: '9 - file.txt', isDirectory: false, isSymlink: false }, - { name: '10 - repo', isDirectory: true, isSymlink: false }, - { name: '99 - file.txt', isDirectory: false, isSymlink: false } - ] - }, - _meta: { runtimeId: 'remote-runtime' } - }) - }) - stubRuntimeEnvironmentBridge({ call: runtimeEnvironmentCall }) -}) - -describe('runtime server directory browser', () => { - it('routes browse requests through the selected runtime environment', async () => { - await expect(browseRuntimeServerDirectory('env-1', '~')).resolves.toEqual({ - resolvedPath: '/home/me', - pathFlavor: 'posix', - entries: [ - { name: '10 - repo', isDirectory: true, isSymlink: false }, - { name: '9 - file.txt', isDirectory: false, isSymlink: false }, - { name: '99 - file.txt', isDirectory: false, isSymlink: false }, - { name: '100 - file.txt', isDirectory: false, isSymlink: false } - ] - }) - - expect(runtimeEnvironmentCall).toHaveBeenLastCalledWith({ - selector: 'env-1', - method: 'files.browseServerDir', - params: { path: '~' }, - timeoutMs: 15_000 - }) - }) -}) diff --git a/src/renderer/src/runtime/runtime-ssh-target-management.test.ts b/src/renderer/src/runtime/runtime-ssh-target-management.test.ts deleted file mode 100644 index 7fc0e2dcc35..00000000000 --- a/src/renderer/src/runtime/runtime-ssh-target-management.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { SSH_TARGET_MANAGEMENT_RUNTIME_CAPABILITY } from '../../../shared/ssh-target-management' - -const mocks = vi.hoisted(() => ({ - callRuntimeRpc: vi.fn(), - runtimeEnvironmentSupportsCapability: vi.fn() -})) - -vi.mock('./runtime-rpc-client', () => ({ - callRuntimeRpc: mocks.callRuntimeRpc, - runtimeEnvironmentSupportsCapability: mocks.runtimeEnvironmentSupportsCapability -})) - -const { - addRuntimeSshRepo, - addRuntimeSshTarget, - browseRuntimeSshDirectory, - listRuntimeEditableSshTargets, - removeRuntimeSshTarget, - RuntimeSshTargetManagementUnsupportedError, - updateRuntimeSshTarget -} = await import('./runtime-ssh-target-management') - -const input = { label: 'Build', host: 'build.internal', port: 22, username: 'ci' } - -const CALLS: [string, () => Promise][] = [ - ['list', () => listRuntimeEditableSshTargets('env-1')], - ['add', () => addRuntimeSshTarget('env-1', input)], - ['update', () => updateRuntimeSshTarget('env-1', 'ssh-1', input)], - ['remove', () => removeRuntimeSshTarget('env-1', 'ssh-1')], - ['browse', () => browseRuntimeSshDirectory('env-1', 'ssh-1', '~')], - ['add project', () => addRuntimeSshRepo('env-1', { connectionId: 'ssh-1', remotePath: '~/app' })] -] - -describe('managing a paired server SSH targets across versions', () => { - const sshApi = { addTarget: vi.fn(), listTargets: vi.fn(), browseDir: vi.fn() } - - beforeEach(() => { - vi.clearAllMocks() - vi.stubGlobal('window', { api: { ssh: sshApi, repos: { addRemote: vi.fn() } } }) - }) - - it.each(CALLS)( - 'an old server without the capability refuses %s and nothing falls back to this client', - async (_name, call) => { - mocks.runtimeEnvironmentSupportsCapability.mockResolvedValue(false) - - await expect(call()).rejects.toBeInstanceOf(RuntimeSshTargetManagementUnsupportedError) - - expect(mocks.runtimeEnvironmentSupportsCapability).toHaveBeenCalledWith( - 'env-1', - SSH_TARGET_MANAGEMENT_RUNTIME_CAPABILITY - ) - expect(mocks.callRuntimeRpc).not.toHaveBeenCalled() - expect(sshApi.addTarget).not.toHaveBeenCalled() - expect(sshApi.listTargets).not.toHaveBeenCalled() - expect(sshApi.browseDir).not.toHaveBeenCalled() - } - ) - - it.each(CALLS)('a current server is called for %s', async (_name, call) => { - mocks.runtimeEnvironmentSupportsCapability.mockResolvedValue(true) - mocks.callRuntimeRpc.mockResolvedValue({ targets: [], target: {}, repo: {} }) - - await call() - - expect(mocks.callRuntimeRpc.mock.calls[0]?.[0]).toEqual({ - kind: 'environment', - environmentId: 'env-1' - }) - }) - - it('sends a whole target so the server can clear omitted fields', async () => { - mocks.runtimeEnvironmentSupportsCapability.mockResolvedValue(true) - mocks.callRuntimeRpc.mockResolvedValue({ target: { id: 'ssh-1', ...input } }) - - await updateRuntimeSshTarget('env-1', 'ssh-1', input) - - expect(mocks.callRuntimeRpc).toHaveBeenCalledWith( - { kind: 'environment', environmentId: 'env-1' }, - 'ssh.updateTarget', - { targetId: 'ssh-1', target: input }, - {} - ) - }) -})