Files
orca/src/cli/index-project-setup.test.ts
T
Brennan Benson c93514db5b refactor(protocol): each area owns its published runtime capability list (#30641)
* refactor(protocol): each area owns its published runtime capability list

RUNTIME_CAPABILITIES now spreads one array per area instead of a flat
hand-appended list, and runtime-capability-constants.ts is split into
the areas that own each constant. The published set is unchanged and
pinned by an exact sorted test.

* test: preserve capability baseline while allowing additions
2026-10-20 17:12:19 -07:00

857 lines
26 KiB
TypeScript

import path from 'node:path'
import { describe, expect, it, vi } from 'vitest'
const {
callMock,
runtimeClientConstructorMock,
serveOrcaAppMock,
getDefaultUserDataPathMock,
addEnvironmentFromPairingCodeMock,
listEnvironmentsMock,
spawnMock
} = vi.hoisted(() => ({
callMock: vi.fn(),
runtimeClientConstructorMock: vi.fn(),
serveOrcaAppMock: vi.fn(),
getDefaultUserDataPathMock: vi.fn(() => '/tmp/orca-user-data'),
addEnvironmentFromPairingCodeMock: vi.fn(),
listEnvironmentsMock: vi.fn(),
spawnMock: vi.fn()
}))
vi.mock('./runtime-client', async () => {
const { createRuntimeClientModuleMock } = await import('./index-test-harness.js')
return createRuntimeClientModuleMock({
callMock,
runtimeClientConstructorMock,
serveOrcaAppMock,
getDefaultUserDataPathMock
})
})
vi.mock('./runtime/environments', () => ({
addEnvironmentFromPairingCode: addEnvironmentFromPairingCodeMock,
listEnvironments: listEnvironmentsMock,
removeEnvironment: vi.fn(),
resolveEnvironment: vi.fn()
}))
vi.mock('child_process', async () => {
const { createChildProcessModuleMock } = await import('./index-test-harness.js')
return createChildProcessModuleMock(spawnMock)
})
import { main } from './index'
import { okFixture, queueFixtures } from './test-fixtures'
import { PROJECT_HOST_SETUP_HOST_TARGET_RUNTIME_CAPABILITY } from '../shared/worktree-runtime-capabilities'
import { pairRuntimeEnvironment, useWorktreeAwarenessEnvironment } from './index-test-harness'
const ORCA_PROJECT = {
id: 'github:stablyai/orca',
displayName: 'Orca',
badgeColor: '#7c3aed',
sourceRepoIds: ['repo-1'],
createdAt: 1,
updatedAt: 1
}
const PROJECT_LIST_FIXTURE = okFixture('req_project_list', { projects: [ORCA_PROJECT] })
describe('orca cli worktree awareness', () => {
useWorktreeAwarenessEnvironment({
callMock,
serveOrcaAppMock,
getDefaultUserDataPathMock,
addEnvironmentFromPairingCodeMock,
listEnvironmentsMock,
spawnMock
})
it('resolves repo.add paths against the invoking cli cwd', async () => {
queueFixtures(
callMock,
okFixture('req_repo_add', {
repo: {
id: 'repo-1',
path: path.resolve('/tmp/repo/apps/web'),
displayName: 'web'
}
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(['repo', 'add', '--path', './apps/web', '--json'], '/tmp/repo')
expect(callMock).toHaveBeenCalledWith('repo.add', {
path: path.resolve('/tmp/repo/apps/web')
})
})
it('lists projects through the project-first runtime API', async () => {
queueFixtures(
callMock,
okFixture('req_project_list', {
projects: [
{
id: 'github:stablyai/orca',
displayName: 'Orca',
badgeColor: '#7c3aed',
providerIdentity: {
provider: 'github',
owner: 'stablyai',
repo: 'orca'
},
sourceRepoIds: ['repo-1'],
createdAt: 1,
updatedAt: 1
}
]
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(['project', 'list', '--json'], '/tmp/repo')
expect(callMock).toHaveBeenCalledWith('project.list')
})
it('routes a runtime host filter to that paired server and keeps its own local rows', async () => {
pairRuntimeEnvironment(listEnvironmentsMock, 'gpu')
queueFixtures(
callMock,
okFixture('req_project_setups', {
setups: [
{
id: 'setup-local',
projectId: 'github:stablyai/orca',
hostId: 'local',
repoId: 'repo-local',
path: '/tmp/orca',
displayName: 'Orca',
setupState: 'ready',
setupMethod: 'legacy-repo',
createdAt: 1,
updatedAt: 1
},
{
id: 'setup-remote',
projectId: 'github:stablyai/orca',
hostId: 'runtime:gpu',
repoId: 'repo-remote',
path: '/srv/orca',
displayName: 'Orca',
setupState: 'ready',
setupMethod: 'legacy-repo',
createdAt: 1,
updatedAt: 1
}
]
})
)
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
await main(
['project', 'setups', '--project', 'github:stablyai/orca', '--host', 'runtime:gpu'],
'/tmp/repo'
)
expect(runtimeClientConstructorMock).toHaveBeenCalledWith(null, 'gpu')
expect(callMock).toHaveBeenCalledWith('projectHostSetup.list')
expect(logSpy.mock.calls[0]?.[0]).toContain('setup-remote')
expect(logSpy.mock.calls[0]?.[0]).toContain('setup-local')
})
it('keeps --host local a filter on the selected environment rather than a second selector', async () => {
pairRuntimeEnvironment(listEnvironmentsMock, 'prod')
queueFixtures(
callMock,
okFixture('req_project_setups', {
setups: [
{
id: 'setup-on-box',
projectId: 'github:stablyai/orca',
hostId: 'local',
repoId: 'repo-on-box',
path: '/srv/orca',
displayName: 'Orca',
setupState: 'ready',
setupMethod: 'legacy-repo',
createdAt: 1,
updatedAt: 1
},
{
id: 'setup-by-client',
projectId: 'github:stablyai/orca',
hostId: 'runtime:prod',
repoId: 'repo-by-client',
path: '/srv/orca-2',
displayName: 'Orca',
setupState: 'ready',
setupMethod: 'legacy-repo',
createdAt: 1,
updatedAt: 1
}
]
})
)
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
await main(['project', 'setups', '--environment', 'prod', '--host', 'local'], '/tmp/repo')
expect(runtimeClientConstructorMock).toHaveBeenCalledWith(undefined, 'prod')
expect(logSpy.mock.calls[0]?.[0]).toContain('setup-on-box')
expect(logSpy.mock.calls[0]?.[0]).not.toContain('setup-by-client')
})
// Why: --host runtime:<id> routes to a paired server, so an older one is reachable without the
// caller meaning to. A raw method_not_found reads as an Orca bug rather than a version gap.
it('names the version gap when the server predates project host setup', async () => {
pairRuntimeEnvironment(listEnvironmentsMock, 'old-server')
const { RuntimeClientError } = await import('./runtime/types.js')
callMock.mockRejectedValueOnce(
new RuntimeClientError('method_not_found', 'Unknown method: projectHostSetup.list')
)
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const priorExitCode = process.exitCode
await main(['project', 'setups', '--host', 'runtime:old-server', '--json'], '/tmp/repo')
const printed = [...logSpy.mock.calls, ...errSpy.mock.calls].flat().join('\n')
expect(printed).toContain('does not support project host setup yet')
expect(printed).not.toContain('Unknown method')
expect(process.exitCode).toBe(1)
process.exitCode = priorExitCode
})
it('rejects a runtime host id that no paired server owns instead of answering empty', async () => {
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const priorExitCode = process.exitCode
await main(['project', 'setups', '--host', 'runtime:not-a-real-env', '--json'], '/tmp/repo')
// The command itself never reached a runtime; only the suggestion lookup did.
expect(callMock).not.toHaveBeenCalledWith('projectHostSetup.list')
const printed = [...logSpy.mock.calls, ...errSpy.mock.calls].flat().join('\n')
expect(printed).toContain('no paired Orca server is named or has id not-a-real-env')
// An agent reads the code and the retry candidates, not the prose.
expect(JSON.parse(printed).error.code).toBe('invalid_argument')
expect(JSON.parse(printed).error.data.knownEnvironments).toEqual([])
expect(process.exitCode).toBe(1)
process.exitCode = priorExitCode
})
// Why: ssh: was never validated, so an unknown target answered ok:true with an empty list —
// the same silent wrong-machine answer unknown runtime ids used to give.
it('rejects an unknown ssh host instead of answering empty', async () => {
queueFixtures(callMock, okFixture('req_ssh_targets', { targets: [] }))
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const priorExitCode = process.exitCode
await main(['project', 'setups', '--host', 'ssh:openclaw', '--json'], '/tmp/repo')
expect(callMock).not.toHaveBeenCalledWith('projectHostSetup.list')
expect([...logSpy.mock.calls, ...errSpy.mock.calls].flat().join('\n')).toContain(
'no SSH target named or with id openclaw'
)
expect(process.exitCode).toBe(1)
process.exitCode = priorExitCode
})
it('tells a caller reaching for a paired server by ssh that it is an environment', async () => {
pairRuntimeEnvironment(listEnvironmentsMock, 'awin')
queueFixtures(callMock, okFixture('req_ssh_targets', { targets: [] }))
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const priorExitCode = process.exitCode
await main(['project', 'setups', '--host', 'ssh:awin', '--json'], '/tmp/repo')
expect([...logSpy.mock.calls, ...errSpy.mock.calls].flat().join('\n')).toContain(
'--environment awin'
)
expect(process.exitCode).toBe(1)
process.exitCode = priorExitCode
})
it('resolves an ssh label to its target id before filtering', async () => {
queueFixtures(
callMock,
okFixture('req_ssh_targets', { targets: [{ id: 'ssh-123-abc', label: 'openclaw' }] }),
okFixture('req_project_setups', {
setups: [
{
id: 'setup-openclaw',
projectId: 'github:stablyai/orca',
hostId: 'ssh:ssh-123-abc',
repoId: 'repo-openclaw',
path: '/home/me/orca',
displayName: 'Orca',
setupState: 'ready',
setupMethod: 'legacy-repo',
createdAt: 1,
updatedAt: 1
}
]
})
)
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
await main(['project', 'setups', '--host', 'ssh:openclaw'], '/tmp/repo')
expect(logSpy.mock.calls[0]?.[0]).toContain('setup-openclaw')
})
// Why: `runtime:<id>` is a persisted token — it lands in ProjectHostSetup.hostId and is
// embedded in generated setup ids. Accepting a name is only safe because it is canonicalized
// to the id before anything downstream sees it; this pins that.
it('never lets an environment name reach a persisted host id', async () => {
pairRuntimeEnvironment(listEnvironmentsMock, 'env-uuid-1', 'awin')
queueFixtures(
callMock,
PROJECT_LIST_FIXTURE,
okFixture('req_project_setup_create', {
result: {
project: {
id: 'github:stablyai/orca',
displayName: 'Orca',
badgeColor: '#7c3aed',
sourceRepoIds: [],
createdAt: 1,
updatedAt: 1
},
setup: {
id: 'setup-awin',
projectId: 'github:stablyai/orca',
hostId: 'local',
repoId: '',
path: '',
displayName: 'awin',
setupState: 'setting-up',
setupMethod: 'provisioned',
createdAt: 1,
updatedAt: 2
}
}
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(
[
'project',
'setup-create',
'--project',
'github:stablyai/orca',
'--host',
'runtime:awin',
'--json'
],
'/tmp/repo'
)
expect(runtimeClientConstructorMock).toHaveBeenCalledWith(null, 'env-uuid-1')
expect(callMock).toHaveBeenCalledWith(
'projectHostSetup.create',
expect.objectContaining({ hostId: 'runtime:env-uuid-1' })
)
})
it('rejects a malformed --host value before contacting any runtime', async () => {
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const priorExitCode = process.exitCode
await main(['project', 'setups', '--host', 'runtime:', '--json'], '/tmp/repo')
expect(callMock).not.toHaveBeenCalled()
expect([...logSpy.mock.calls, ...errSpy.mock.calls].flat().join('\n')).toContain(
'Invalid --host value: runtime:'
)
expect(process.exitCode).toBe(1)
process.exitCode = priorExitCode
})
it('refuses a runtime host id alongside an unrelated --pairing-code connection', async () => {
pairRuntimeEnvironment(listEnvironmentsMock, 'gpu')
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const priorExitCode = process.exitCode
await main(
['project', 'setups', '--host', 'runtime:gpu', '--pairing-code', 'remote-runtime', '--json'],
'/tmp/repo'
)
expect(callMock).not.toHaveBeenCalled()
expect([...logSpy.mock.calls, ...errSpy.mock.calls].flat().join('\n')).toContain(
'use either --host runtime:<id> or --pairing-code, not both'
)
expect(process.exitCode).toBe(1)
process.exitCode = priorExitCode
})
it('sets up an existing project folder with a path resolved against the local cli cwd', async () => {
queueFixtures(
callMock,
PROJECT_LIST_FIXTURE,
okFixture('req_project_setup', {
result: {
project: {
id: 'github:stablyai/orca',
displayName: 'Orca',
badgeColor: '#7c3aed',
sourceRepoIds: ['repo-1'],
createdAt: 1,
updatedAt: 1
},
setup: {
id: 'setup-local',
projectId: 'github:stablyai/orca',
hostId: 'local',
repoId: 'repo-1',
path: path.resolve('/tmp/orca'),
displayName: 'Orca',
setupState: 'ready',
setupMethod: 'imported-existing-folder',
createdAt: 1,
updatedAt: 1
},
repo: {
id: 'repo-1',
path: path.resolve('/tmp/orca'),
displayName: 'Orca',
badgeColor: '#7c3aed',
addedAt: 1
}
}
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(
[
'project',
'setup-existing-folder',
'--project',
'github:stablyai/orca',
'--host',
'local',
'--path',
'..',
'--kind',
'git',
'--display-name',
'Orca',
'--json'
],
'/tmp/orca/worktrees/feature'
)
expect(callMock).toHaveBeenCalledWith('projectHostSetup.setupExistingFolder', {
projectId: 'github:stablyai/orca',
hostId: 'local',
path: path.resolve('/tmp/orca/worktrees'),
kind: 'git',
displayName: 'Orca'
})
})
it('rejects remote project setup relative paths instead of resolving against client cwd', async () => {
pairRuntimeEnvironment(listEnvironmentsMock, 'gpu')
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const priorExitCode = process.exitCode
await main(
[
'project',
'setup-existing-folder',
'--project',
'github:stablyai/orca',
'--host',
'runtime:gpu',
'--path',
'./orca',
'--json'
],
'/tmp/repo'
)
expect(callMock).not.toHaveBeenCalled()
expect([...logSpy.mock.calls, ...errSpy.mock.calls].flat().join('\n')).toContain(
'Remote project setup requires --path to be an absolute path on the remote server.'
)
expect(process.exitCode).toBe(1)
process.exitCode = priorExitCode
})
it('rejects SSH project setup relative paths, which name the client filesystem', async () => {
// A local CLI reaching an `ssh:*` host is still off-client: resolving `./orca` against the
// CLI cwd would register a path that exists on the wrong machine.
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const priorExitCode = process.exitCode
await main(
[
'project',
'setup-existing-folder',
'--project',
'github:stablyai/orca',
'--host',
'ssh:openclaw',
'--path',
'./orca',
'--json'
],
'/tmp/repo'
)
expect(callMock).not.toHaveBeenCalled()
expect([...logSpy.mock.calls, ...errSpy.mock.calls].flat().join('\n')).toContain(
'Remote project setup requires --path to be an absolute path on the remote server.'
)
expect(process.exitCode).toBe(1)
process.exitCode = priorExitCode
})
it('rejects remote repo.add relative paths instead of resolving against client cwd', async () => {
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const priorExitCode = process.exitCode
await main(
['repo', 'add', '--path', './apps/web', '--pairing-code', 'remote-runtime', '--json'],
'/tmp/repo'
)
expect(callMock).not.toHaveBeenCalled()
expect([...logSpy.mock.calls, ...errSpy.mock.calls].flat().join('\n')).toContain(
'Remote repo add requires --path to be an absolute path on the remote server.'
)
expect(process.exitCode).toBe(1)
process.exitCode = priorExitCode
})
it('sends remote repo.add absolute paths unchanged', async () => {
queueFixtures(
callMock,
okFixture('req_repo_add', {
repo: {
id: 'repo-1',
path: '/srv/orca/web',
displayName: 'web'
}
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(
['repo', 'add', '--path', '/srv/orca/web', '--pairing-code', 'remote-runtime', '--json'],
'/tmp/repo'
)
expect(callMock).toHaveBeenCalledWith('repo.add', {
path: '/srv/orca/web'
})
})
it.each(['C:\\repo', 'C:/repo', '\\\\server\\share\\repo', '//server/share/repo'])(
'sends remote repo.add server absolute path %s unchanged',
async (serverPath) => {
queueFixtures(
callMock,
okFixture('req_repo_add', {
repo: {
id: 'repo-1',
path: serverPath,
displayName: 'web'
}
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(
['repo', 'add', '--path', serverPath, '--pairing-code', 'remote-runtime', '--json'],
'/tmp/repo'
)
expect(callMock).toHaveBeenCalledWith('repo.add', {
path: serverPath
})
}
)
// Why: STA-4792 defect 2. `--host runtime:<id>` used to leave the client local, so a Windows
// destination fell into resolve(cwd, ...) and became a literal directory next to the caller.
// Routing makes the client remote, which is what sends the path through untouched.
it('sends a windows destination to the routed host instead of joining it to the local cwd', async () => {
pairRuntimeEnvironment(listEnvironmentsMock, 'awin')
queueFixtures(
callMock,
PROJECT_LIST_FIXTURE,
okFixture('req_project_setup_clone', {
result: {
project: {
id: 'github:stablyai/orca',
displayName: 'Orca',
badgeColor: '#7c3aed',
sourceRepoIds: [],
createdAt: 1,
updatedAt: 1
},
setup: {
id: 'setup-awin',
projectId: 'github:stablyai/orca',
hostId: 'local',
repoId: 'repo-awin',
path: 'C:\\orca-probe\\orca',
displayName: 'Orca',
setupState: 'ready',
setupMethod: 'cloned',
createdAt: 1,
updatedAt: 1
},
repo: {
id: 'repo-awin',
path: 'C:\\orca-probe\\orca',
displayName: 'Orca',
badgeColor: '#7c3aed',
addedAt: 1
}
}
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(
[
'project',
'setup-clone',
'--project',
'github:stablyai/orca',
'--host',
'runtime:awin',
'--url',
'https://github.com/stablyai/orca.git',
'--destination',
'C:\\orca-probe',
'--json'
],
'/Users/nwparker/orca/workspaces/orca/IME-koko'
)
expect(runtimeClientConstructorMock).toHaveBeenCalledWith(null, 'awin')
expect(callMock).toHaveBeenCalledWith(
'projectHostSetup.clone',
expect.objectContaining({ destination: 'C:\\orca-probe' })
)
})
it('updates project host setup metadata through the project-first runtime API', async () => {
queueFixtures(
callMock,
okFixture('req_project_setup_update', {
result: {
project: {
id: 'github:stablyai/orca',
displayName: 'Orca',
badgeColor: '#7c3aed',
sourceRepoIds: [],
createdAt: 1,
updatedAt: 1
},
setup: {
id: 'setup-gpu',
projectId: 'github:stablyai/orca',
hostId: 'runtime:gpu',
repoId: '',
path: '/srv/orca',
displayName: 'GPU VM',
setupState: 'ready',
setupMethod: 'imported-existing-folder',
createdAt: 1,
updatedAt: 2
}
}
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(
[
'project',
'setup-update',
'--setup',
'setup-gpu',
'--display-name',
'GPU VM',
'--path',
'/srv/orca',
'--worktree-base-path',
'../worktrees',
'--state',
'ready',
'--method',
'imported-existing-folder',
'--json'
],
'/tmp/repo'
)
expect(callMock).toHaveBeenCalledWith('projectHostSetup.update', {
setupId: 'setup-gpu',
updates: {
displayName: 'GPU VM',
path: path.resolve('/tmp/repo', '/srv/orca'),
worktreeBasePath: '../worktrees',
gitUsername: undefined,
kind: undefined,
setupState: 'ready',
setupMethod: 'imported-existing-folder'
}
})
})
it('creates independent project host setup metadata through the project-first runtime API', async () => {
pairRuntimeEnvironment(listEnvironmentsMock, 'gpu')
queueFixtures(
callMock,
PROJECT_LIST_FIXTURE,
okFixture('req_project_setup_create', {
result: {
project: {
id: 'github:stablyai/orca',
displayName: 'Orca',
badgeColor: '#7c3aed',
sourceRepoIds: [],
createdAt: 1,
updatedAt: 1
},
setup: {
id: 'setup-gpu',
projectId: 'github:stablyai/orca',
hostId: 'runtime:gpu',
repoId: '',
path: '',
displayName: 'GPU VM',
setupState: 'setting-up',
setupMethod: 'provisioned',
createdAt: 1,
updatedAt: 2
}
}
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(
[
'project',
'setup-create',
'--project',
'github:stablyai/orca',
'--host',
'runtime:gpu',
'--setup-id',
'setup-gpu',
'--display-name',
'GPU VM',
'--state',
'setting-up',
'--method',
'provisioned',
'--json'
],
'/tmp/repo'
)
expect(callMock).toHaveBeenCalledWith('projectHostSetup.create', {
projectId: 'github:stablyai/orca',
hostId: 'runtime:gpu',
setupId: 'setup-gpu',
path: undefined,
kind: undefined,
displayName: 'GPU VM',
worktreeBasePath: undefined,
gitUsername: undefined,
setupState: 'setting-up',
setupMethod: 'provisioned'
})
})
it.each([
{ flags: [], force: false, hostId: undefined },
{ flags: ['--force'], force: true, hostId: undefined },
// Why: a project on two hosts has two setups with one id; --host names the twin.
{ flags: ['--host', 'local'], force: false, hostId: 'local' }
])('deletes project host setup metadata with $flags', async ({ flags, force, hostId }) => {
queueFixtures(
callMock,
...(hostId ? [hostTargetStatusFixture(true)] : []),
okFixture('req_project_setup_delete', {
result: {
project: {
id: 'github:stablyai/orca',
displayName: 'Orca',
badgeColor: '#7c3aed',
sourceRepoIds: [],
createdAt: 1,
updatedAt: 1
},
setup: {
id: 'setup-gpu',
projectId: 'github:stablyai/orca',
hostId: 'runtime:gpu',
repoId: '',
path: '/srv/orca',
displayName: 'GPU VM',
setupState: 'ready',
setupMethod: 'imported-existing-folder',
createdAt: 1,
updatedAt: 2
}
}
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(['project', 'setup-delete', '--setup', 'setup-gpu', ...flags, '--json'], '/tmp/repo')
expect(callMock).toHaveBeenCalledWith('projectHostSetup.delete', {
setupId: 'setup-gpu',
hostId,
force
})
})
it('refuses --host on a server that would ignore it and act on the first same-id twin', async () => {
queueFixtures(callMock, hostTargetStatusFixture(false))
vi.spyOn(console, 'error').mockImplementation(() => {})
await main(
['project', 'setup-delete', '--setup', 'dup', '--host', 'local', '--force', '--json'],
'/tmp/repo'
)
expect(callMock).not.toHaveBeenCalledWith('projectHostSetup.delete', expect.anything())
})
})
function hostTargetStatusFixture(supported: boolean) {
return okFixture('req_status', {
capabilities: supported ? [PROJECT_HOST_SETUP_HOST_TARGET_RUNTIME_CAPABILITY] : []
})
}