mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
test: remove narrow release and migration checks
This commit is contained in:
@@ -41,8 +41,6 @@ const LAUNCH_TARGET = {
|
||||
|
||||
const OPEN_LAUNCHER = { launch: async () => ({ ...LAUNCH_TARGET }), available: () => true }
|
||||
|
||||
const AFTER_SETTLE_MS = 10 * 60 * 1000
|
||||
|
||||
function createAutomation(store: TestStore): Automation {
|
||||
store.addRepo(makeRepo())
|
||||
return store.createAutomation({
|
||||
@@ -117,24 +115,6 @@ describe('reconciling retained runs against a graph that has not published yet',
|
||||
rmSync(testState.dir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
it('does not fail a desktop-startup retained run while the window graph is empty', async () => {
|
||||
const store = await createStore()
|
||||
const automation = createAutomation(store)
|
||||
const retained = retainDispatchedRun(store, automation)
|
||||
const surface = createPaneSurface()
|
||||
const service = new AutomationService(store, {
|
||||
terminalObserver: surface.observer,
|
||||
terminalLauncher: { launch: async () => ({ ...LAUNCH_TARGET }), available: () => false }
|
||||
})
|
||||
|
||||
service.start()
|
||||
await vi.advanceTimersByTimeAsync(AFTER_SETTLE_MS)
|
||||
|
||||
expect(readRun(store, automation.id, retained.id).status).toBe('dispatched')
|
||||
expect(readRun(store, automation.id, retained.id).error).toBeNull()
|
||||
service.stop()
|
||||
})
|
||||
|
||||
it('re-attaches to the run once the restored pane mounts seconds later', async () => {
|
||||
const store = await createStore()
|
||||
const automation = createAutomation(store)
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
migrateAutomationHostFilterSshTargetId,
|
||||
migrateAutomationsForSshReadoption
|
||||
} from './automation-ssh-readoption-migration'
|
||||
import { hostStableKey } from '../../../shared/automations/ownership/automation-owner-key'
|
||||
import type { Automation } from '../../../shared/automations/automation-definition'
|
||||
import type { AutomationRun } from '../../../shared/automations/history/automation-run'
|
||||
import type { PersistedUIState } from '../../../shared/persisted-ui-state-types'
|
||||
|
||||
const NOW = 1_700_000_000_000
|
||||
const OLD_ID = 'ssh-1738-a9f3x'
|
||||
const NEW_ID = 'ssh-1799-b2k7z'
|
||||
|
||||
function makeAutomation(overrides: Partial<Automation> = {}): Automation {
|
||||
return {
|
||||
id: 'auto-1',
|
||||
name: 'Nightly',
|
||||
prompt: 'go',
|
||||
precheck: null,
|
||||
agentId: 'codex',
|
||||
projectId: 'repo-1',
|
||||
executionTargetType: 'ssh',
|
||||
executionTargetId: OLD_ID,
|
||||
executionTargetGeneration: 3,
|
||||
schedulerOwner: 'local_host_service',
|
||||
workspaceMode: 'new_per_run',
|
||||
workspaceId: null,
|
||||
baseBranch: null,
|
||||
reuseSession: false,
|
||||
timezone: 'UTC',
|
||||
rrule: 'FREQ=DAILY',
|
||||
dtstart: NOW,
|
||||
enabled: true,
|
||||
nextRunAt: NOW,
|
||||
missedRunPolicy: 'run_once_within_grace',
|
||||
missedRunGraceMinutes: 720,
|
||||
createdAt: NOW,
|
||||
updatedAt: NOW,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
function migrate(
|
||||
automations: Automation[],
|
||||
generation: number | undefined,
|
||||
runs: AutomationRun[] = []
|
||||
) {
|
||||
return migrateAutomationsForSshReadoption({
|
||||
automations,
|
||||
automationRuns: runs,
|
||||
oldTargetId: OLD_ID,
|
||||
newTargetId: NEW_ID,
|
||||
newTargetGeneration: generation
|
||||
})
|
||||
}
|
||||
|
||||
function runContext(hostId: string) {
|
||||
return {
|
||||
kind: 'workspace-run' as const,
|
||||
projectId: 'project-1',
|
||||
hostId: hostId as `ssh:${string}`,
|
||||
projectHostSetupId: 'setup-1',
|
||||
repoId: 'repo-1',
|
||||
path: '/srv/repo'
|
||||
}
|
||||
}
|
||||
|
||||
function uiWithFilter(hostKey: string): PersistedUIState {
|
||||
return { automationHostFilter: { kind: 'host', hostKey } } as PersistedUIState
|
||||
}
|
||||
|
||||
describe('migrateAutomationsForSshReadoption', () => {
|
||||
it('re-points the owner and adopts the re-added registration generation', () => {
|
||||
const automation = makeAutomation()
|
||||
expect(migrate([automation], 9)).toBe(true)
|
||||
expect(automation.executionTargetId).toBe(NEW_ID)
|
||||
expect(automation.executionTargetGeneration).toBe(9)
|
||||
})
|
||||
|
||||
it('drops a stale capture when the re-added target carries no generation', () => {
|
||||
const automation = makeAutomation()
|
||||
expect(migrate([automation], undefined)).toBe(true)
|
||||
expect(automation.executionTargetId).toBe(NEW_ID)
|
||||
expect('executionTargetGeneration' in automation).toBe(false)
|
||||
})
|
||||
|
||||
it('migrates run and source context host ids, including for local records', () => {
|
||||
const automation = makeAutomation({
|
||||
executionTargetType: 'local',
|
||||
executionTargetId: 'local',
|
||||
executionTargetGeneration: undefined,
|
||||
runContext: runContext(`ssh:${OLD_ID}`),
|
||||
sourceContext: {
|
||||
kind: 'task-source',
|
||||
provider: 'github',
|
||||
projectId: 'project-1',
|
||||
hostId: `ssh:${OLD_ID}`
|
||||
}
|
||||
})
|
||||
expect(migrate([automation], 9)).toBe(true)
|
||||
expect(automation.runContext?.hostId).toBe(`ssh:${NEW_ID}`)
|
||||
expect(automation.sourceContext?.hostId).toBe(`ssh:${NEW_ID}`)
|
||||
expect(automation.executionTargetId).toBe('local')
|
||||
expect(automation.executionTargetGeneration).toBeUndefined()
|
||||
})
|
||||
|
||||
it('re-points run history so workspace navigation survives re-adoption', () => {
|
||||
const run = {
|
||||
id: 'run-1',
|
||||
automationId: 'auto-1',
|
||||
runContext: runContext(`ssh:${OLD_ID}`)
|
||||
} as AutomationRun
|
||||
expect(migrate([], 9, [run])).toBe(true)
|
||||
expect(run.runContext?.hostId).toBe(`ssh:${NEW_ID}`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('migrateAutomationHostFilterSshTargetId', () => {
|
||||
it('never rewrites a runtime authority filter that reuses the same target id', () => {
|
||||
const hostKey = hostStableKey({
|
||||
authority: { kind: 'runtime', environmentId: 'env-1' },
|
||||
selector: { kind: 'ssh', targetId: OLD_ID }
|
||||
})
|
||||
const ui = uiWithFilter(hostKey)
|
||||
expect(migrateAutomationHostFilterSshTargetId(ui, OLD_ID, NEW_ID)).toBe(false)
|
||||
expect(ui.automationHostFilter).toEqual({ kind: 'host', hostKey })
|
||||
})
|
||||
})
|
||||
-79
@@ -75,84 +75,5 @@ export function registerWorkerReleaseArchiveTests(): void {
|
||||
await rm(directory, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('preserves payload clipping metadata in the released transcript snapshot', async () => {
|
||||
h.setup()
|
||||
const directory = await mkdtemp(join(tmpdir(), 'orca-worker-release-clipped-snapshot-'))
|
||||
const transcriptPath = join(directory, 'rollout.jsonl')
|
||||
try {
|
||||
await writeFile(
|
||||
transcriptPath,
|
||||
`${JSON.stringify({
|
||||
timestamp: '2026-08-03T12:00:00.000Z',
|
||||
type: 'event_msg',
|
||||
payload: { id: 'clipped-message', type: 'agent_message', message: 'x'.repeat(5_000) }
|
||||
})}\n`
|
||||
)
|
||||
vi.mocked(h.runtime.getExactWorkerProviderSession).mockReturnValue({
|
||||
agent: 'codex',
|
||||
processIncarnation: 'runtime_test:term_worker:1',
|
||||
providerSession: {
|
||||
key: 'codex:clipped-session',
|
||||
id: 'clipped-session',
|
||||
transcriptPath
|
||||
}
|
||||
} as never)
|
||||
const { dispatchId } = await h.startSettledWorker()
|
||||
await h.call('orchestration.workerRelease', { dispatch: dispatchId })
|
||||
|
||||
const read = (await h.call('orchestration.workerRead', { dispatch: dispatchId })) as {
|
||||
transcript: { limited: boolean }
|
||||
cursor: string
|
||||
contentComplete: boolean
|
||||
clipping: string[]
|
||||
warnings: string[]
|
||||
}
|
||||
|
||||
expect(read).toMatchObject({
|
||||
transcript: { limited: true },
|
||||
contentComplete: false,
|
||||
clipping: ['transcript_payload'],
|
||||
warnings: ['Oversized transcript text was clipped.']
|
||||
})
|
||||
expect(read.cursor).toMatch(/^owr1_/)
|
||||
expect(read.warnings).not.toContain(
|
||||
'Older transcript messages were omitted from the bounded archive.'
|
||||
)
|
||||
} finally {
|
||||
await rm(directory, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('recovers archive metadata when a prior attempt committed only the archive row', async () => {
|
||||
h.setup()
|
||||
const { dispatchId } = await h.startSettledWorker()
|
||||
const requested = h.db.requestWorkerTerminalRelease(dispatchId)
|
||||
expect(requested.disposition).toBe('requested')
|
||||
if (requested.disposition !== 'requested') {
|
||||
throw new Error('release request was not recorded')
|
||||
}
|
||||
h.db.storeWorkerTerminalArchive({
|
||||
dispatchId,
|
||||
resourceId: requested.resource.id,
|
||||
kind: 'terminal_tail',
|
||||
content: JSON.stringify({
|
||||
lines: ['archive survived the interrupted attempt'],
|
||||
truncated: false,
|
||||
terminalStatus: 'running',
|
||||
warnings: []
|
||||
})
|
||||
})
|
||||
|
||||
const release = (await h.call('orchestration.workerRelease', { dispatch: dispatchId })) as {
|
||||
archive: { source: string | null; status: string | null } | null
|
||||
}
|
||||
|
||||
expect(release.archive).toEqual({ source: 'terminal', status: 'captured' })
|
||||
expect(h.db.getWorkerTerminalResourceByOwner(dispatchId)).toMatchObject({
|
||||
archive_source: 'terminal',
|
||||
archive_status: 'captured'
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
-22
@@ -27,27 +27,5 @@ export function registerWorkerReleaseInventoryTests(): void {
|
||||
).resolves.toMatchObject({ state: 'released' })
|
||||
expect(h.runtime.closeTerminal).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('reports abandoned workers as retained instead of reclaimable', async () => {
|
||||
h.setup()
|
||||
const { dispatchId } = await h.startWorker()
|
||||
await h.call('orchestration.workerAbandon', { dispatch: dispatchId })
|
||||
|
||||
const listed = (await h.call('orchestration.workerList', { run: h.activeRunId })) as {
|
||||
workers: { dispatchId: string; terminalState: string | null }[]
|
||||
}
|
||||
|
||||
expect(listed.workers).toContainEqual(
|
||||
expect.objectContaining({ dispatchId, terminalState: 'retained' })
|
||||
)
|
||||
await expect(
|
||||
h.call('orchestration.workerRelease', { dispatch: dispatchId })
|
||||
).resolves.toMatchObject({
|
||||
state: 'retained',
|
||||
reason: 'identity_unproven',
|
||||
processAction: 'none'
|
||||
})
|
||||
expect(h.runtime.closeTerminal).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
-52
@@ -7,58 +7,6 @@ const h = createOrchestrationWorkerReleaseHarness()
|
||||
beforeEach(() => h.setup())
|
||||
afterEach(() => h.cleanup())
|
||||
|
||||
it.each(['local', 'ssh'])(
|
||||
'a handle-addressed phone Keep open fences %s worker release',
|
||||
async (host) => {
|
||||
if (host === 'ssh') {
|
||||
vi.mocked(h.runtime.getOrchestrationDispatchAuthority).mockImplementation((handle) =>
|
||||
handle === 'term_worker'
|
||||
? ({
|
||||
terminalHandle: handle,
|
||||
paneKey: h.workerPaneKey,
|
||||
processIncarnation: 'runtime_test:term_worker:1',
|
||||
hostScope: { kind: 'ssh', targetId: 'ssh-1' }
|
||||
} as never)
|
||||
: null
|
||||
)
|
||||
}
|
||||
const worker = await h.startSettledWorker()
|
||||
expect(h.db.getWorkerTerminalResourceByOwner(worker.dispatchId)?.host_scope).toContain(host)
|
||||
h.runtime.registerPreAllocatedHandleForPty('pty-worker', 'term_worker')
|
||||
registerBoundPty(h.runtime, 'pty-worker', 'repo::worktree', null, {
|
||||
tabId: 'tab_worker',
|
||||
leafId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb'
|
||||
})
|
||||
vi.mocked(h.runtime.getTerminalPaneKey).mockRestore()
|
||||
await expect(
|
||||
h.call('orchestration.workerKeepOpen', { terminal: 'term_worker', keep: true })
|
||||
).resolves.toEqual({ autoClose: { kept: true } })
|
||||
await expect(
|
||||
h.call('orchestration.workerRelease', { dispatch: worker.dispatchId })
|
||||
).resolves.toMatchObject({ state: 'retained', reason: 'user_kept' })
|
||||
expect(h.runtime.closeTerminal).not.toHaveBeenCalled()
|
||||
}
|
||||
)
|
||||
|
||||
it('an unknown handle does not fence another worker or access the database', async () => {
|
||||
const worker = await h.startSettledWorker()
|
||||
h.runtime.registerPreAllocatedHandleForPty('pty-worker', 'term_worker')
|
||||
registerBoundPty(h.runtime, 'pty-worker', 'repo::worktree', null, {
|
||||
tabId: 'tab_worker',
|
||||
leafId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb'
|
||||
})
|
||||
vi.mocked(h.runtime.getTerminalPaneKey).mockRestore()
|
||||
const db = vi.spyOn(h.runtime, 'getOrchestrationDb')
|
||||
await expect(
|
||||
h.call('orchestration.workerKeepOpen', { terminal: 'term_missing', keep: true })
|
||||
).resolves.toEqual({ autoClose: null })
|
||||
expect(db).not.toHaveBeenCalled()
|
||||
expect(h.db.getWorkerTerminalResourceByOwner(worker.dispatchId)?.ownership_state).toBe('owned')
|
||||
await expect(
|
||||
h.call('orchestration.workerRelease', { dispatch: worker.dispatchId })
|
||||
).resolves.toMatchObject({ state: 'released' })
|
||||
})
|
||||
|
||||
// Boot-wait Keep open must find custody written at terminal creation, or a later release closes a kept pane (#19337, #19608).
|
||||
it('a phone Keep open during the boot wait keeps the pane and fences the later release', async () => {
|
||||
const gate = h.deferred<unknown>()
|
||||
|
||||
Reference in New Issue
Block a user