Files
orca/src/cli/index-reference.test.ts
T
OrcaWinandOrcaWin 225f940453 test: strengthen CLI expectations and consolidate repeated scenarios (#32976)
* test: strengthen CLI refusals and consolidate serve validation

* test: consolidate terminal creation and strengthen restore output

* test: pin dashboard telemetry clock away from midnight

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 17:11:16 -07:00

434 lines
14 KiB
TypeScript

import { afterEach, beforeEach, 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 { buildWorktree, okFixture } from './test-fixtures'
import { getWorkspaceReferenceIdentity } from '../shared/workspace-reference-identity'
import type { WorkspaceAttachment } from '../shared/worktree/types'
import { WORKTREE_LINKED_ITEMS_DELTA_RUNTIME_CAPABILITY } from '../shared/workspace-attachment-capabilities'
import { useWorktreeAwarenessEnvironment } from './index-test-harness'
const PR = 'https://github.com/acme/api/pull/5123'
const TASK = 'https://linear.app/acme/issue/STA-1234'
const PR_KEY = '["github","pr","github.com","/acme/api/pull/5123"]'
const parsedPR: WorkspaceAttachment = { provider: 'github', type: 'pr', number: 5123, url: PR }
const parsedTask: WorkspaceAttachment = {
provider: 'linear',
type: 'issue',
number: 0,
identifier: 'STA-1234',
linearIdentifier: 'STA-1234',
linearOrganizationUrlKey: 'acme',
url: TASK
}
function listResult(references = [parsedPR], kind: 'worktree' | 'folder' = 'worktree') {
return {
worktree: {
...buildWorktree('/tmp/api', 'api'),
id: kind === 'folder' ? 'folder:folder1' : 'repo::/tmp/api',
name: 'api',
kind,
repo: 'repo'
},
references: references.map((item) => ({
...item,
key: getWorkspaceReferenceIdentity(item),
selected: true
}))
}
}
function mockCalls(
before = listResult(),
after = before,
capabilities = [WORKTREE_LINKED_ITEMS_DELTA_RUNTIME_CAPABILITY]
) {
let lists = 0
callMock.mockImplementation(async (method: string) => {
if (method === 'status.get') {
return okFixture('status', { capabilities })
}
if (method === 'reference.list') {
return okFixture('list', lists++ === 0 ? before : after)
}
if (method === 'reference.find') {
return okFixture('find', { kind: 'reference_matches', matches: [], truncated: false })
}
return okFixture('mutation', { worktree: before.worktree })
})
vi.spyOn(console, 'log').mockImplementation(() => {})
vi.spyOn(console, 'error').mockImplementation(() => {})
}
describe('reference CLI', () => {
const priorExitCode = process.exitCode
beforeEach(() => {
process.exitCode = undefined
})
afterEach(() => {
process.exitCode = priorExitCode
})
useWorktreeAwarenessEnvironment({
callMock,
serveOrcaAppMock,
getDefaultUserDataPathMock,
addEnvironmentFromPairingCodeMock,
listEnvironmentsMock,
spawnMock
})
it('adds several URLs through the existing delta merge and never invents origins', async () => {
mockCalls(listResult([]), listResult([parsedPR, parsedTask]))
await main(['reference', 'add', PR, TASK, '--worktree', 'name:api', '--json'], '/tmp/api')
expect(callMock).toHaveBeenCalledWith('worktree.set', {
worktree: 'id:repo::/tmp/api',
linkedItemsBase: [],
linkedItems: [parsedPR, parsedTask],
linkedItemsSelectionChanged: false
})
expect(JSON.parse(vi.mocked(console.log).mock.calls[0][0]).result.changes).toEqual([
{ key: PR_KEY, operation: 'add', changed: true },
{
key: '["linear","issue","linear.app","/acme/issue/STA-1234"]',
operation: 'add',
changed: true
}
])
})
it('validates every URL before any request', async () => {
mockCalls()
await main(['reference', 'add', PR, 'github:pr:4', '--worktree', 'name:api'], '/tmp/api')
expect(callMock).not.toHaveBeenCalled()
expect(console.error).toHaveBeenCalled()
})
it('requires the delta capability even if the host supports linked items', async () => {
mockCalls(listResult(), listResult(), ['worktree.linked-items.v1'])
await main(['reference', 'add', TASK, '--worktree', 'name:api'], '/tmp/api')
expect(callMock).toHaveBeenCalledExactlyOnceWith('status.get')
expect(console.error).toHaveBeenCalledWith(expect.stringContaining('cannot merge'))
})
it('duplicate adds preserve current metadata and produce a no-op', async () => {
mockCalls(listResult([{ ...parsedPR, title: 'Saved title' }]))
await main(['reference', 'add', PR, PR, '--worktree', 'name:api', '--json'], '/tmp/api')
expect(callMock.mock.calls.some(([method]) => method === 'worktree.set')).toBe(false)
expect(JSON.parse(vi.mocked(console.log).mock.calls[0][0]).result.references[0].title).toBe(
'Saved title'
)
expect(JSON.parse(vi.mocked(console.log).mock.calls[0][0]).result.changes).toEqual([
{
key: PR_KEY,
operation: 'add',
changed: false,
reason: 'already_linked'
}
])
})
it('removes only requested keys from folders via existing folder storage', async () => {
mockCalls(listResult([parsedPR, parsedTask], 'folder'), listResult([parsedTask], 'folder'))
await main(['reference', 'remove', '--key', PR_KEY, '--worktree', 'folder:folder1'], '/tmp/api')
expect(callMock).toHaveBeenCalledWith('folderWorkspace.update', {
folderWorkspaceId: 'folder1',
updates: {
linkedItemsBase: [parsedPR, parsedTask],
linkedItems: [parsedTask],
linkedItemsSelectionChanged: false
}
})
})
it('removes a URL without touching the same review number in another repository', async () => {
const other = { ...parsedPR, url: 'https://github.com/acme/other/pull/5123' }
mockCalls(listResult([parsedPR, other]), listResult([other]))
await main(['reference', 'remove', PR, '--worktree', 'name:api'], '/tmp/api')
expect(callMock).toHaveBeenCalledWith(
'worktree.set',
expect.objectContaining({ linkedItemsBase: [parsedPR, other], linkedItems: [other] })
)
})
const localTwin = {
...parsedPR,
title: 'Local',
taskSourceContext: {
kind: 'task-source' as const,
provider: 'github' as const,
projectId: 'p1',
hostId: 'local' as const
}
}
const remoteTwin = {
...parsedPR,
title: 'Remote',
taskSourceContext: {
...localTwin.taskSourceContext,
hostId: 'runtime:other' as const
}
}
it('keeps same-URL twins with different source contexts on an unrelated add', async () => {
mockCalls(listResult([localTwin, remoteTwin]))
await main(['reference', 'add', TASK, '--worktree', 'name:api'], '/tmp/api')
expect(callMock).toHaveBeenCalledWith(
'worktree.set',
expect.objectContaining({
linkedItemsBase: [localTwin, remoteTwin],
linkedItems: [localTwin, remoteTwin, parsedTask]
})
)
})
it('removing a URL removes every twin that shares its identity', async () => {
mockCalls(listResult([localTwin, parsedTask, remoteTwin]), listResult([parsedTask]))
await main(['reference', 'remove', PR, '--worktree', 'name:api'], '/tmp/api')
expect(callMock).toHaveBeenCalledWith(
'worktree.set',
expect.objectContaining({
linkedItemsBase: [localTwin, parsedTask, remoteTwin],
linkedItems: [parsedTask]
})
)
})
it('removes by the host-provided key even when the local identity differs', async () => {
const before = listResult([parsedPR, parsedTask])
before.references[0].key = 'host-v2:pr-key'
mockCalls(before, listResult([parsedTask]))
await main(
['reference', 'remove', '--key', 'host-v2:pr-key', '--worktree', 'name:api', '--json'],
'/tmp/api'
)
expect(callMock).toHaveBeenCalledWith(
'worktree.set',
expect.objectContaining({
linkedItemsBase: [parsedPR, parsedTask],
linkedItems: [parsedTask]
})
)
expect(JSON.parse(vi.mocked(console.log).mock.calls[0][0]).result.changes).toEqual([
{ key: 'host-v2:pr-key', operation: 'remove', changed: true }
])
})
it('removing an absent URL is a no-op', async () => {
mockCalls()
await main(['reference', 'remove', TASK, '--worktree', 'name:api', '--json'], '/tmp/api')
expect(callMock.mock.calls.some(([method]) => method === 'worktree.set')).toBe(false)
expect(JSON.parse(vi.mocked(console.log).mock.calls[0][0]).result.changes[0]).toMatchObject({
changed: false,
reason: 'not_linked'
})
})
it('rejects remote current without resolving the local cwd on the paired host', async () => {
mockCalls()
await main(
['reference', 'find', 'STA-1234', '--worktree', 'current', '--pairing-code', 'remote'],
'/tmp/api'
)
expect(callMock).not.toHaveBeenCalled()
expect(console.error).toHaveBeenCalledWith(expect.stringContaining('remote host'))
})
it('forwards local caller path context so WSL selectors resolve without a scan', async () => {
mockCalls()
const cwd = String.raw`\\wsl$\Ubuntu\home\me\repo`
await main(['reference', 'find', 'STA-1234', '--worktree', 'path:/home/me/repo'], cwd)
expect(callMock).toHaveBeenCalledExactlyOnceWith(
'reference.find',
expect.objectContaining({ worktree: 'path:/home/me/repo', cwd })
)
})
it('passes explicit paired-host selectors unchanged', async () => {
mockCalls()
await main(
['reference', 'find', 'STA-1234', '--worktree', 'path:/srv/api', '--pairing-code', 'remote'],
'/tmp/api'
)
expect(callMock).toHaveBeenCalledExactlyOnceWith(
'reference.find',
expect.objectContaining({ worktree: 'path:/srv/api' })
)
expect(callMock.mock.calls[0][1]).not.toHaveProperty('cwd')
})
it('uses metadata current lookup instead of worktree.list or Git scanning', async () => {
mockCalls()
await main(
[
'reference',
'find',
'STA-1234',
'--worktree',
'current',
'--include-archived',
'--limit',
'10'
],
'/tmp/api/subdir'
)
expect(callMock).toHaveBeenCalledExactlyOnceWith('reference.find', {
query: 'STA-1234',
worktree: 'current',
cwd: '/tmp/api/subdir',
repo: undefined,
includeArchived: true,
limit: 10
})
})
it.each([
{ args: ['5123'], message: 'Expected a full reference URL (https://…).' },
{
args: ['STA-1234', '--worktree', 'name:api', '--repo', 'id:repo'],
message: 'Pass --worktree or --repo, not both.'
},
{ args: ['STA-1234', '--limit', '0'], message: 'Invalid positive integer for --limit' }
])('rejects invalid find arguments $args before querying', async ({ args, message }) => {
mockCalls()
await main(['reference', 'find', ...args, '--json'], '/tmp/api')
expect(callMock).not.toHaveBeenCalled()
expect(JSON.parse(vi.mocked(console.log).mock.calls[0][0])).toMatchObject({
ok: false,
error: { code: 'invalid_argument', message }
})
expect(process.exitCode).toBe(1)
})
it('seeds repeated references in the create request before setup or agent startup', async () => {
mockCalls()
await main(
[
'worktree',
'create',
'--name',
'fix',
'--repo',
'id:repo',
'--no-parent',
'--reference',
PR,
'--reference',
TASK
],
'/tmp/api'
)
expect(callMock).toHaveBeenCalledWith(
'worktree.create',
expect.objectContaining({ linkedItems: [parsedPR, parsedTask] })
)
})
it.each(['pr', 'issue', 'linear-issue', 'gitlab-issue', 'gitlab-mr'])(
'rejects create --reference mixed with --%s',
async (flag) => {
mockCalls()
await main(
[
'worktree',
'create',
'--name',
'fix',
'--repo',
'id:repo',
'--no-parent',
'--reference',
PR,
`--${flag}`,
'4',
'--json'
],
'/tmp/api'
)
expect(callMock).not.toHaveBeenCalled()
expect(JSON.parse(vi.mocked(console.log).mock.calls[0][0])).toMatchObject({
ok: false,
error: { code: 'invalid_argument', message: `Cannot combine --reference with --${flag}.` }
})
expect(process.exitCode).toBe(1)
}
)
// Why: hosts built before reference.* still advertise the delta capability.
it.each([
[['reference', 'list', '--worktree', 'name:api']],
[['reference', 'find', '--query', 'STA-1234']],
[['reference', 'add', TASK, '--worktree', 'name:api']]
])('names the version gap when the host lacks reference RPCs: %j', async (argv) => {
mockCalls()
const { RuntimeClientError } = await import('./runtime/types.js')
const base = callMock.getMockImplementation()
callMock.mockImplementation(async (method: string, ...rest: unknown[]) => {
if (method.startsWith('reference.')) {
throw new RuntimeClientError('method_not_found', `Unknown method: ${method}`)
}
return base?.(method, ...rest)
})
await main([...argv, '--json'], '/tmp/api')
expect(JSON.parse(vi.mocked(console.log).mock.calls[0][0])).toMatchObject({
ok: false,
error: {
code: 'incompatible_runtime',
message:
'This Orca host does not support reference commands yet. Update Orca on the execution host.'
}
})
expect(process.exitCode).toBe(1)
})
it('validates create flags locally before probing reference capability', async () => {
mockCalls()
await main(
['worktree', 'create', '--repo', 'id:repo', '--no-parent', '--reference', PR],
'/tmp/api'
)
expect(callMock).not.toHaveBeenCalledWith('status.get', expect.anything())
expect(callMock).not.toHaveBeenCalledWith('status.get')
expect(console.error).toHaveBeenCalledWith(expect.stringContaining('--name'))
})
})