Files
orca/src/main/opencode/opencode-launch-capabilities.test.ts
T
NeilandBrennan Benson 5a2aa87f1c Select OpenCode plugin exports from the execution host version (#24662)
* fix: wait for OpenCode worker composer before first dispatch

Reuse captured composer readiness on local and paired execution hosts and revoke launching-shell paste anchors.

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>

* feat(opencode): probe execution-host CLI capabilities

* fix(opencode): select plugin default for execution host loader

* fix(opencode): limit prompt prefill capability to verified release

* feat(opencode): probe launch capabilities on the execution host

* fix(opencode): select plugin loader for the launched host binary

* fix(opencode): match WSL probe cwd and declared guest environment

* fix(opencode): preserve launch environment deletion boundaries

* STRICT launch CI contract correction

* CAPS launch CI contract correction

* test: initialize Claude prompt state in output retention fixture

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-03 23:13:06 -07:00

162 lines
5.3 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
getOpenCodeLaunchExecutable,
probeOpenCodeLaunchCapabilities
} from './opencode-launch-capabilities'
import { getOpenCodeCliCapabilities } from '../../shared/opencode-cli-version'
const mocks = vi.hoisted(() => ({ resolve: vi.fn(), probe: vi.fn(), wsl: vi.fn() }))
vi.mock('../ipc/command-path-resolver', () => ({ resolveCommandOnLocalPath: mocks.resolve }))
vi.mock('./opencode-cli-version', () => ({ probeOpenCodeCliVersion: mocks.probe }))
vi.mock('../wsl/wsl-runner', () => ({ runWslProcess: mocks.wsl }))
beforeEach(() => {
vi.clearAllMocks()
mocks.probe.mockResolvedValue(getOpenCodeCliCapabilities('2.0.16'))
mocks.wsl.mockResolvedValue({
code: 0,
timedOut: false,
stdout: '1.18.30',
environmentResolved: true
})
})
describe('OpenCode execution-host launch capability probe', () => {
it('refuses a successful version response from an unresolved WSL execution environment', async () => {
mocks.wsl.mockResolvedValue({
code: 0,
timedOut: false,
stdout: '1.18.30',
environmentResolved: false
})
await probeOpenCodeLaunchCapabilities({
command: 'opencode',
env: {},
wsl: { distro: 'Ubuntu' }
})
expect(await mocks.probe.mock.calls[0]?.[0].execute()).toMatchObject({ code: null })
})
it('recognizes quoted executables and preserves explicit run commands', () => {
expect(getOpenCodeLaunchExecutable('"/app dir/opencode" run task')).toBe('/app dir/opencode')
expect(getOpenCodeLaunchExecutable('custom-launcher --standalone', 'opencode')).toBe(
'custom-launcher'
)
expect(getOpenCodeLaunchExecutable('claude')).toBeNull()
})
it('uses native resolution with the execution environment and cwd', async () => {
mocks.resolve.mockResolvedValue('/bin/opencode')
const env = { PATH: '/bin', OPENCODE_CONFIG_DIR: '/private/config' }
await probeOpenCodeLaunchCapabilities({
command: 'opencode run task',
env,
cwd: '/repo',
hostIdentity: 'native-test'
})
expect(mocks.resolve).toHaveBeenCalledWith('opencode', { env, cwd: '/repo' })
expect(mocks.probe).toHaveBeenCalledWith({
executablePath: '/bin/opencode',
env,
cwd: '/repo',
hostIdentity: 'native-test'
})
expect(mocks.wsl).not.toHaveBeenCalled()
})
it('uses the relay resolver rather than client PATH resolution', async () => {
const resolveExecutable = vi.fn().mockResolvedValue('/host/opencode')
await probeOpenCodeLaunchCapabilities({
command: 'opencode',
env: {},
hostIdentity: 'relay:linux',
resolveExecutable
})
expect(resolveExecutable).toHaveBeenCalledWith('opencode')
expect(mocks.resolve).not.toHaveBeenCalled()
expect(mocks.probe).toHaveBeenCalledWith(
expect.objectContaining({ executablePath: '/host/opencode', hostIdentity: 'relay:linux' })
)
})
it('returns unknown when the host cannot resolve the binary', async () => {
mocks.resolve.mockResolvedValue(null)
expect(await probeOpenCodeLaunchCapabilities({ command: 'opencode', env: {} })).toEqual(
getOpenCodeCliCapabilities(null)
)
expect(mocks.probe).not.toHaveBeenCalled()
})
it('bounds WSL probes and matches guest cwd plus explicitly imported config roots', async () => {
const env = {
HOME: '/native',
PATH: '/native/bin',
OPENCODE_CONFIG_DIR: '/guest/config',
XDG_DATA_HOME: '/guest/data',
WSLENV: 'OPENCODE_CONFIG_DIR:XDG_DATA_HOME'
}
await probeOpenCodeLaunchCapabilities({
command: 'opencode --standalone',
env,
cwd: '\\\\wsl.localhost\\Ubuntu\\home\\user\\repo',
wsl: { distro: 'Debian' },
hostIdentity: 'host-a'
})
expect(mocks.resolve).not.toHaveBeenCalled()
const options = mocks.probe.mock.calls[0]?.[0]
const guestEnv = {
OPENCODE_CONFIG_DIR: '/guest/config',
XDG_DATA_HOME: '/guest/data',
WSLENV: env.WSLENV
}
expect(options).toEqual(
expect.objectContaining({
executablePath: 'opencode',
hostIdentity: 'host-a:wsl:Ubuntu',
cwd: '/home/user/repo',
env: guestEnv
})
)
await options.execute()
expect(mocks.wsl).toHaveBeenCalledWith({
distro: 'Ubuntu',
loginPath: 'preferred',
cwd: '/home/user/repo',
program: 'opencode',
args: ['--version'],
env: guestEnv,
timeoutMs: 5000,
maxOutputBytes: 4096
})
})
it('does not import native configuration the actual WSL pane would not receive', async () => {
await probeOpenCodeLaunchCapabilities({
command: 'opencode',
env: { OPENCODE_CONFIG_DIR: 'C:\\native' },
cwd: 'D:\\repo',
wsl: { distro: 'Ubuntu' }
})
expect(mocks.probe).toHaveBeenCalledWith(
expect.objectContaining({ cwd: '/mnt/d/repo', env: { WSLENV: '' } })
)
})
it('keeps WSL path translation flags and skips Windows-only values', async () => {
await probeOpenCodeLaunchCapabilities({
command: 'opencode',
env: {
WSLENV: 'XDG_DATA_HOME/p:WINDOWS_ONLY/w:HOME',
XDG_DATA_HOME: 'D:\\data',
WINDOWS_ONLY: 'private',
HOME: 'C:\\native'
},
wsl: {}
})
expect(mocks.probe).toHaveBeenCalledWith(
expect.objectContaining({
env: { WSLENV: 'XDG_DATA_HOME/p:WINDOWS_ONLY/w:HOME', XDG_DATA_HOME: 'D:\\data' }
})
)
})
})