Files
orca/src/main/runtime/opencode-wsl-folder-launch-host.test.ts
T
Brennan Benson 0266b5f95f fix(opencode): allow model picks when discovery is unavailable (#31985)
* fix(opencode): allow picked models across CLI updates

* test(opencode): prune obsolete timeout ratchet allowance

* fix(opencode): use resolved agents and refuse unsafe workers upfront

* fix(i18n): include OpenCode refusal messages in runtime catalog
2026-10-26 13:12:53 -07:00

114 lines
5.2 KiB
TypeScript

import './rpc/unused-default-rpc-methods.test-fixture'
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { OrcaRuntimeService } from './orca-runtime'
import { RpcDispatcher } from './rpc/dispatcher'
import { AGENT_SESSION_METHODS } from './rpc/methods/agent-session'
import { _resetWslCachesForTests, _setWslCachesForTests } from '../wsl'
import { PROJECT_RUNTIME_REPAIR_REQUIRED_CODE } from '../../shared/project-runtime-repair-message'
import {
partialRuntimeStore,
storeFolderWorkspace,
storeProject,
storeProjectGroup,
storeRepo,
type PartialRuntimeStore
} from './runtime-store-partial-fixture'
vi.mock('../opencode/opencode-launch-capabilities', () => ({
probeOpenCodeLaunchCapabilities: async () => ({
version: '1.18.30',
pluginApi: 'v1',
promptMode: 'submit'
})
}))
vi.mock('../opencode/opencode-model-executable', () => ({
resolveOpenCodeDirectModelExecutable: async () => 'opencode'
}))
const repo = storeRepo({ id: 'r', path: 'C:\\repo', kind: 'git', projectGroupId: 'g' })
const share = storeRepo({ id: 's', path: '\\\\wsl$\\Ubuntu\\s', kind: 'git', projectGroupId: 'g' })
const folder = storeFolderWorkspace({ id: 'f', projectGroupId: 'g', folderPath: 'C:\\work' })
const wsl = { kind: 'wsl', distro: 'Ubuntu' } as const
const store: PartialRuntimeStore = {
// A WSL terminal setting must not redirect the Windows model check.
getSettings: () => ({
disabledTuiAgents: [],
terminalWindowsShell: 'wsl.exe',
terminalWindowsWslDistro: 'Ubuntu'
}),
getProjects: () => [
storeProject({ id: 'p', sourceRepoIds: ['r'], localWindowsRuntimePreference: wsl }),
storeProject({
id: 'q',
sourceRepoIds: ['s'],
localWindowsRuntimePreference: { kind: 'windows-host' }
})
],
getRepo: (id: string) => [repo, share].find((entry) => entry.id === id),
getRepos: () => [repo],
getFolderWorkspaces: () => [folder],
getProjectGroups: () => [storeProjectGroup({ id: 'g' })]
}
const scope = { id: 'f', path: 'C:\\work', connectionId: null, repo: null, folderWorkspace: folder }
const clientOperationId = `${Date.now()}-0123456789abcdef0123456789abcdef`
let runtime: OrcaRuntimeService
let spawned: () => (string | undefined)[]
const original = process.platform
const setPlatform = (value: string) =>
Object.defineProperty(process, 'platform', { configurable: true, value })
beforeEach(() => {
setPlatform('win32')
runtime = new OrcaRuntimeService(partialRuntimeStore(store))
Object.assign(runtime, {
resolveTerminalWorkspaceLaunchScope: async () => scope,
executionOwnerSupportsAgentSessionOperation: async () => true
})
const launches: (string | undefined)[] = []
// Capture host-planned launch scope before spawning.
vi.spyOn(runtime, 'createTerminal').mockImplementation(async (selector = '', opts = {}) => {
const launchScope = await runtime.resolveTerminalWorkspaceLaunchScope(selector)
const planned = await runtime.resolveAgentTerminalCreateOptions(launchScope, opts)
launches.push(planned.launchRuntime?.platform)
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: callers read only the handle.
return { handle: 't' } as never
})
spawned = () => launches
})
afterEach(() => {
setPlatform(original)
_resetWslCachesForTests()
})
// TEMPORARY until STA-10251: WSL-folder OpenCode create/resume/model checks all retain main's Windows runtime.
it('starts, resumes and checks models for OpenCode in a WSL folder on Windows', async () => {
await runtime.createAgentSession({ clientOperationId, worktree: 'id:f', agent: 'opencode' })
const providerSession = { key: 'session_id', id: 'ses_1' } as const
const resume = { kind: 'explicit', worktree: 'id:f', agent: 'opencode', providerSession } as const
await runtime.ensureAgentSession(resume)
expect(spawned()).toEqual(['win32', 'win32'])
const target = { worktree: 'id:f', model: 'm' }
expect(await runtime.probeOrchestrationOpenCodeModelLaunchSupport(target)).toBe(true)
})
it('refuses an agent whose WSL distro is gone, under its own code', async () => {
_setWslCachesForTests({ available: true, distros: ['Debian'] })
const params = { clientOperationId, worktree: 'id:f', agent: 'codex' }
const call = { id: '1', authToken: 't', method: 'terminal.createAgentSession', params }
const response = await new RpcDispatcher({ runtime, methods: AGENT_SESSION_METHODS }).dispatch(
call
)
const message =
'No agent was started. Ubuntu is not installed in WSL. Choose an installed distro or switch this project to Windows.'
expect(response).toMatchObject({ error: { code: PROJECT_RUNTIME_REPAIR_REQUIRED_CODE, message } })
})
// WSL-share OpenCode runs inside WSL; refuse model selection until that host can verify it.
it('refuses an OpenCode model in a WSL-share repo set to Windows host', async () => {
const shareScope = { ...scope, id: 's', path: share.path, repo: share, folderWorkspace: null }
Object.assign(runtime, { resolveTerminalWorkspaceLaunchScope: async () => shareScope })
const request = { clientOperationId, worktree: 'id:s', agent: 'opencode' } as const
const picked = runtime.createAgentSession({ ...request, launchPreferences: { model: 'm' } })
await expect(picked).rejects.toThrow('OpenCode cannot apply this model choice')
expect(spawned()).toEqual([])
})