mirror of
https://github.com/stablyai/orca.git
synced 2026-10-28 08:02:54 +00:00
Enable automations to run on multiple hosts (SSH targets and local) with owner-fenced mutations, scoped list queries per host, and conflict resolution. Introduces desktop and runtime authorities as distinct automation storage owners, with per-host caching, invalidation, and retry scheduling on the renderer. Captures registration generations for SSH hosts to survive re-adoption. Adds CLI support for destination selection and conflict recovery.
169 lines
4.4 KiB
TypeScript
169 lines
4.4 KiB
TypeScript
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 { localRepoDestinationFixtures, okFixture, queueFixtures } from './test-fixtures'
|
|
|
|
const SELF_OWNER = { selector: { kind: 'self' } }
|
|
import { useWorktreeAwarenessEnvironment } from './index-test-harness'
|
|
|
|
describe('orca cli worktree awareness', () => {
|
|
useWorktreeAwarenessEnvironment({
|
|
callMock,
|
|
serveOrcaAppMock,
|
|
getDefaultUserDataPathMock,
|
|
addEnvironmentFromPairingCodeMock,
|
|
listEnvironmentsMock,
|
|
spawnMock
|
|
})
|
|
|
|
it('passes automation source context JSON through create', async () => {
|
|
const sourceContext = {
|
|
kind: 'task-source',
|
|
provider: 'github',
|
|
projectId: 'github:stablyai/orca',
|
|
hostId: 'runtime:gpu',
|
|
projectHostSetupId: 'setup-gpu',
|
|
repoId: 'repo-gpu',
|
|
providerIdentity: { provider: 'github', owner: 'stablyai', repo: 'orca' },
|
|
accountLabel: 'gpu-bot'
|
|
}
|
|
queueFixtures(
|
|
callMock,
|
|
...localRepoDestinationFixtures('repo-gpu'),
|
|
okFixture('req_automation_create', {
|
|
automation: { id: 'auto-1', name: 'GPU task review' }
|
|
})
|
|
)
|
|
vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
|
|
await main(
|
|
[
|
|
'automations',
|
|
'create',
|
|
'--name',
|
|
'GPU task review',
|
|
'--trigger',
|
|
'daily',
|
|
'--prompt',
|
|
'Review open work',
|
|
'--provider',
|
|
'codex',
|
|
'--repo',
|
|
'id:repo-gpu',
|
|
'--source-context',
|
|
JSON.stringify(sourceContext),
|
|
'--json'
|
|
],
|
|
'/tmp/repo'
|
|
)
|
|
|
|
expect(callMock).toHaveBeenNthCalledWith(
|
|
2,
|
|
'automation.create',
|
|
expect.objectContaining({
|
|
repo: 'id:repo-gpu',
|
|
sourceContext
|
|
})
|
|
)
|
|
})
|
|
|
|
it('clears automation source context on edit with null', async () => {
|
|
queueFixtures(
|
|
callMock,
|
|
okFixture('req_owner', { automation: { id: 'auto-1' }, owner: SELF_OWNER }),
|
|
okFixture('req_edit', {
|
|
automation: { id: 'auto-1', name: 'GPU task review' }
|
|
})
|
|
)
|
|
vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
|
|
await main(['automations', 'edit', 'auto-1', '--source-context', 'null', '--json'], '/tmp/repo')
|
|
|
|
expect(callMock).toHaveBeenNthCalledWith(
|
|
2,
|
|
'automation.update',
|
|
expect.objectContaining({
|
|
id: 'auto-1',
|
|
updates: expect.objectContaining({
|
|
sourceContext: null
|
|
})
|
|
})
|
|
)
|
|
})
|
|
|
|
it('rejects invalid automation source context JSON before calling the runtime', async () => {
|
|
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
const priorExitCode = process.exitCode
|
|
|
|
await main(
|
|
[
|
|
'automations',
|
|
'create',
|
|
'--name',
|
|
'GPU task review',
|
|
'--trigger',
|
|
'daily',
|
|
'--prompt',
|
|
'Review open work',
|
|
'--provider',
|
|
'codex',
|
|
'--repo',
|
|
'id:repo-gpu',
|
|
'--source-context',
|
|
'{nope',
|
|
'--json'
|
|
],
|
|
'/tmp/repo'
|
|
)
|
|
|
|
expect(callMock).not.toHaveBeenCalled()
|
|
expect([...logSpy.mock.calls, ...errSpy.mock.calls].flat().join('\n')).toContain(
|
|
'--source-context must be valid JSON'
|
|
)
|
|
expect(process.exitCode).toBe(1)
|
|
|
|
process.exitCode = priorExitCode
|
|
})
|
|
})
|