Files
orca/src/main/providers/execution-host-provider-dispatch.test.ts
T
Neil b031ec9dde feat(providers): dispatch git and filesystem providers by execution host
`const c = repo.connectionId; c ? sshProvider(c) : local()` overloads `null`
to mean both "resolved: local" and "could not resolve", so every path that
cannot determine the host silently runs remote work on the client (#11163).
It also cannot express a `runtime:` host at all.

Add a host-keyed dispatch whose input is an `ExecutionHostId` — never null —
with `local`, `ssh` and `runtime` as three symmetric entries, and which throws
on an id that names no host instead of degrading to this machine. `ssh` carries
`provider: null` for "remote, currently unreachable", which is now a different
answer from "local" rather than the same one.

`runtime:` is a distinct entry rather than a provider because main does not
execute runtime hosts at all: they are forwarded over the environment transport,
and a runtime row's `connectionId` names a target in the server's namespace.
Dialing it from this client's SSH table would trade a silent-local bug for a
silent-wrong-host one.

First migrations, both to rows resolved via `getRepoExecutionHostId`:
- repo-worktrees: an `executionHostId: 'ssh:*'`-only row no longer lists,
  root-matches, or strict-lists against a same-named local path.
- workspace-space-repo-scan: same for the size scan, and `isRemote` no longer
  contradicts the `executionHostId` emitted beside it.
2026-09-02 17:22:21 -07:00

106 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { afterEach, describe, expect, it } from 'vitest'
import {
ExecutionHostNotDispatchableError,
requireFilesystemProviderForHost,
requireGitProviderForHost,
resolveFilesystemRouteForHost,
resolveGitRouteForHost,
UnresolvableExecutionHostError
} from './execution-host-provider-dispatch'
import { registerSshGitProvider, unregisterSshGitProvider } from './ssh-git-dispatch'
import {
registerSshFilesystemProvider,
unregisterSshFilesystemProvider
} from './ssh-filesystem-dispatch'
const connectionId = 'host-dispatch-target'
const gitProvider = { listWorktrees: async () => [] } as never
const filesystemProvider = { readDir: async () => [] } as never
describe('execution host provider dispatch', () => {
afterEach(() => {
unregisterSshGitProvider(connectionId)
unregisterSshFilesystemProvider(connectionId)
})
it('routes `local` to the local entry rather than to a provider', () => {
expect(resolveGitRouteForHost('local')).toEqual({ kind: 'local', hostId: 'local' })
expect(resolveFilesystemRouteForHost('local')).toEqual({ kind: 'local', hostId: 'local' })
})
it('routes an ssh host to its registered provider', () => {
registerSshGitProvider(connectionId, gitProvider)
registerSshFilesystemProvider(connectionId, filesystemProvider)
expect(resolveGitRouteForHost(`ssh:${connectionId}`)).toEqual({
kind: 'ssh',
hostId: `ssh:${connectionId}`,
connectionId,
provider: gitProvider
})
expect(resolveFilesystemRouteForHost(`ssh:${connectionId}`)).toEqual({
kind: 'ssh',
hostId: `ssh:${connectionId}`,
connectionId,
provider: filesystemProvider
})
expect(requireGitProviderForHost(`ssh:${connectionId}`)).toBe(gitProvider)
expect(requireFilesystemProviderForHost(`ssh:${connectionId}`)).toBe(filesystemProvider)
})
it('answers `unreachable`, not `local`, for an ssh host with no registered provider', () => {
const route = resolveGitRouteForHost(`ssh:${connectionId}`)
// The distinction the old `connectionId ? ssh : local` shape could not spell.
expect(route.kind).toBe('ssh')
expect(route.kind === 'ssh' && route.provider).toBeNull()
expect(() => requireGitProviderForHost(`ssh:${connectionId}`)).toThrow(
/Remote connection dropped/
)
expect(() => requireFilesystemProviderForHost(`ssh:${connectionId}`)).toThrow(
/Remote connection dropped/
)
})
it('routes a runtime host to its own entry instead of collapsing it into local', () => {
expect(resolveGitRouteForHost('runtime:env-7')).toEqual({
kind: 'runtime',
hostId: 'runtime:env-7',
environmentId: 'env-7'
})
expect(resolveFilesystemRouteForHost('runtime:env-7')).toEqual({
kind: 'runtime',
hostId: 'runtime:env-7',
environmentId: 'env-7'
})
})
it('refuses to hand a runtime host to this process’s ssh table', () => {
// A runtime repo row carries the *server's* nested target id. Dialling it here would reach a
// same-named target in this client's namespace.
registerSshGitProvider(connectionId, gitProvider)
expect(() => requireGitProviderForHost('runtime:env-7')).toThrow(
ExecutionHostNotDispatchableError
)
expect(() => requireFilesystemProviderForHost('runtime:env-7')).toThrow(
ExecutionHostNotDispatchableError
)
})
it('refuses to serve a local host from the remote-only accessor', () => {
expect(() => requireGitProviderForHost('local')).toThrow(ExecutionHostNotDispatchableError)
expect(() => requireFilesystemProviderForHost('local')).toThrow(
ExecutionHostNotDispatchableError
)
})
it.each([null, undefined, '', 'nonsense', 'ssh:', 'runtime:', 'ssh:a|b'])(
'throws instead of answering local for the unresolvable host %p',
(hostId) => {
expect(() => resolveGitRouteForHost(hostId)).toThrow(UnresolvableExecutionHostError)
expect(() => resolveFilesystemRouteForHost(hostId)).toThrow(UnresolvableExecutionHostError)
}
)
})