mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 10:27:33 +00:00
test: remove low-value shared and workspace tests
This commit is contained in:
@@ -83,24 +83,14 @@ describe('headless dispatch prompt delivery', () => {
|
||||
}
|
||||
)
|
||||
|
||||
it('records launch evidence once the agent terminal exists, and keeps it after retention clears the pane', async () => {
|
||||
const { store, service, run, row, settle } = await heldLaunch('delivery')
|
||||
it('records launch evidence once the agent terminal exists even when delivery fails', async () => {
|
||||
const { service, row, settle } = await heldLaunch('delivery')
|
||||
await vi.waitFor(() => expect(row()?.terminalPaneKey).toBe(OPENED.terminalPaneKey))
|
||||
await settle("Aider started, but Orca could not send this run's prompt.")
|
||||
const launchedAt = row()?.launchedAt
|
||||
expect(launchedAt).toEqual(expect.any(Number))
|
||||
expect(row()?.dispatchedAt).toBeNull()
|
||||
|
||||
// Dead-PTY retention forgets the terminal but must not forget that the agent launched.
|
||||
store.updateAutomationRun({
|
||||
runId: run.id,
|
||||
status: 'dispatch_failed',
|
||||
error: row()?.error ?? null,
|
||||
terminalSessionId: null,
|
||||
terminalPaneKey: null,
|
||||
terminalPtyId: null
|
||||
})
|
||||
expect(row()).toMatchObject({ terminalPaneKey: null, launchedAt })
|
||||
service.stop()
|
||||
})
|
||||
})
|
||||
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
||||
import { useAppStore } from '@/store'
|
||||
import { makePaneKey } from '../../../../../../shared/workspace-layout/stable-pane-id'
|
||||
|
||||
import { createWorkspaceReferenceRefreshTargetsSelector } from './workspace-reference-refresh-targets'
|
||||
import {
|
||||
referenceAttachment,
|
||||
referenceRepo,
|
||||
referenceWorkspace
|
||||
} from './workspace-reference-fixtures.test-support'
|
||||
vi.mock('@/store', async () => {
|
||||
const browserWindow = globalThis.window
|
||||
const { createTestStore } = await import('@/store/github/github-slice-test-harness')
|
||||
globalThis.window = browserWindow
|
||||
return { useAppStore: createTestStore() }
|
||||
})
|
||||
const read = vi.hoisted(() => vi.fn(async () => null))
|
||||
vi.mock('./workspace-reference-detail-read', () => ({ readWorkspaceReferenceDetails: read }))
|
||||
const workspace = { ...referenceWorkspace, linkedPR: null, linkedItems: [referenceAttachment()] }
|
||||
const update = () => createWorkspaceReferenceRefreshTargetsSelector()(useAppStore.getState(), true)
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
vi.clearAllMocks()
|
||||
useAppStore.setState({
|
||||
settings: null,
|
||||
repos: [referenceRepo],
|
||||
worktreesByRepo: { repo: [workspace] },
|
||||
detectedWorktreesByRepo: {},
|
||||
visibleReviewWorktreeIds: [workspace.id],
|
||||
activeWorktreeId: null,
|
||||
activeWorkspaceExecutionHostId: null,
|
||||
visibleWorkspaceHostIds: null,
|
||||
workspaceHostScope: 'all',
|
||||
hostedReviewCache: {},
|
||||
prCache: {},
|
||||
tabsByWorktree: {},
|
||||
ptyIdsByTabId: {},
|
||||
browserTabsByWorktree: {
|
||||
[workspace.id]: [
|
||||
{
|
||||
id: 'browser',
|
||||
worktreeId: workspace.id,
|
||||
url: '',
|
||||
title: '',
|
||||
loading: false,
|
||||
faviconUrl: null,
|
||||
canGoBack: false,
|
||||
canGoForward: false,
|
||||
loadError: null,
|
||||
createdAt: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
unifiedTabsByWorktree: {},
|
||||
agentStatusByPaneKey: {},
|
||||
agentStatusEpoch: 0,
|
||||
sshConnectionStates: new Map()
|
||||
})
|
||||
})
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
it('retains both explicit owners when equal visible workspace IDs exist on two hosts', () => {
|
||||
const remoteRepo = {
|
||||
...referenceRepo,
|
||||
executionHostId: 'ssh:remote' as const,
|
||||
connectionId: 'remote'
|
||||
}
|
||||
useAppStore.setState({
|
||||
repos: [referenceRepo, remoteRepo],
|
||||
worktreesByRepo: {
|
||||
local: [{ ...workspace, hostId: 'local' }],
|
||||
remote: [{ ...workspace, hostId: 'ssh:remote' }]
|
||||
},
|
||||
sshConnectionStates: new Map([
|
||||
['remote', { targetId: 'remote', status: 'connected', error: null, reconnectAttempt: 0 }]
|
||||
])
|
||||
})
|
||||
const targets = update()
|
||||
expect(targets).toHaveLength(2)
|
||||
expect(new Set(targets.map((target) => target.key)).size).toBe(2)
|
||||
useAppStore.setState({ visibleWorkspaceHostIds: ['local'] })
|
||||
expect(update()).toHaveLength(1)
|
||||
useAppStore.setState({
|
||||
activeWorktreeId: workspace.id,
|
||||
activeWorkspaceExecutionHostId: 'ssh:remote'
|
||||
})
|
||||
expect(
|
||||
createWorkspaceReferenceRefreshTargetsSelector({ selectedOnly: true })(
|
||||
useAppStore.getState(),
|
||||
true
|
||||
)
|
||||
).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('adds and removes exact review targets when a headless agent wakes and sleeps its workspace', () => {
|
||||
useAppStore.setState({ browserTabsByWorktree: {} })
|
||||
const select = createWorkspaceReferenceRefreshTargetsSelector()
|
||||
expect(select(useAppStore.getState(), true)).toEqual([])
|
||||
const paneKey = makePaneKey('headless', '11111111-1111-4111-8111-111111111111')
|
||||
useAppStore.setState({
|
||||
agentStatusByPaneKey: {
|
||||
[paneKey]: {
|
||||
paneKey,
|
||||
worktreeId: workspace.id,
|
||||
agentType: 'codex',
|
||||
state: 'working',
|
||||
prompt: '',
|
||||
updatedAt: Date.now(),
|
||||
stateStartedAt: Date.now(),
|
||||
stateHistory: []
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(select(useAppStore.getState(), false)).toMatchObject([
|
||||
{ selected: false, fetchedAt: null }
|
||||
])
|
||||
useAppStore.setState({ agentStatusByPaneKey: {} })
|
||||
expect(select(useAppStore.getState(), false)).toEqual([])
|
||||
})
|
||||
-46
@@ -9,7 +9,6 @@ import {
|
||||
} from '@/components/sidebar/worktree-card-slot-pane-index'
|
||||
import { createTestStore } from '../../../testing/app-store-test-fixture'
|
||||
import { makeWorktree } from '../worktree.test-fixture'
|
||||
import { applyRemoveWorktreeSuccessState } from './remove-worktree-store-cleanup'
|
||||
import { buildWorktreePurgeState } from './worktree-purge-state'
|
||||
|
||||
function displayers(paneKey: string): string[] {
|
||||
@@ -18,51 +17,6 @@ function displayers(paneKey: string): string[] {
|
||||
return ids
|
||||
}
|
||||
|
||||
it('retires displayed pane owners after explicit workspace removal and identity reuse', () => {
|
||||
const store = createTestStore()
|
||||
const identity = composeWorktreeHostIdentity('local', 'removed-slot')
|
||||
store.setState({
|
||||
workspaceUnreadRaisedByMarkers: {
|
||||
[identity]: true,
|
||||
[composeWorktreeHostIdentity('runtime:box', 'removed-slot')]: true,
|
||||
[composeWorktreeHostIdentity('local', 'surviving-slot')]: true
|
||||
},
|
||||
workspacesMarkedUnreadByUser: {
|
||||
[identity]: true,
|
||||
[composeWorktreeHostIdentity('runtime:box', 'removed-slot')]: true,
|
||||
[composeWorktreeHostIdentity('local', 'surviving-slot')]: true
|
||||
}
|
||||
})
|
||||
recordDisplayedPanes(identity, ['old-pane', 'reused-pane'])
|
||||
applyRemoveWorktreeSuccessState(store.setState, 'removed-slot', new Set())
|
||||
expect(displayers('old-pane')).toEqual([])
|
||||
expect(displayers('reused-pane')).toEqual([])
|
||||
expect(store.getState().workspaceUnreadRaisedByMarkers).toEqual({ 'local|surviving-slot': true })
|
||||
expect(store.getState().workspacesMarkedUnreadByUser).toEqual({ 'local|surviving-slot': true })
|
||||
|
||||
recordDisplayedPanes(identity, ['new-pane'])
|
||||
expect(displayers('new-pane')).toEqual(['removed-slot'])
|
||||
applyRemoveWorktreeSuccessState(store.setState, 'removed-slot', new Set())
|
||||
expect(displayers('new-pane')).toEqual([])
|
||||
})
|
||||
|
||||
it('retires historical owners during repeated bulk workspace churn', () => {
|
||||
const store = createTestStore()
|
||||
for (let index = 0; index < 32; index += 1) {
|
||||
const id = `churn-slot-${index}`
|
||||
recordDisplayedPanes(composeWorktreeHostIdentity('local', id), ['churn-pane'])
|
||||
recordDisplayedPanes(composeWorktreeHostIdentity('runtime:box', id), ['churn-pane'])
|
||||
store.setState({
|
||||
workspaceUnreadRaisedByMarkers: { [composeWorktreeHostIdentity('local', id)]: true },
|
||||
workspacesMarkedUnreadByUser: { [composeWorktreeHostIdentity('runtime:box', id)]: true }
|
||||
})
|
||||
store.setState(buildWorktreePurgeState(store.getState(), [id]))
|
||||
}
|
||||
expect(displayers('churn-pane')).toEqual([])
|
||||
expect(store.getState().workspaceUnreadRaisedByMarkers).toEqual({})
|
||||
expect(store.getState().workspacesMarkedUnreadByUser).toEqual({})
|
||||
})
|
||||
|
||||
it('preserves the surviving host twin when retiring a pane owner', () => {
|
||||
const store = createTestStore()
|
||||
store.setState({
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { agentLaunchSettingsMutationUpdates } from './agent-launch-settings'
|
||||
import { applyAgentPermissionMode } from '../permissions/tui-agent-permissions'
|
||||
|
||||
describe('agent launch permission projection compatibility', () => {
|
||||
it('changes only agents Auto does not cover', () => {
|
||||
const automatic = applyAgentPermissionMode({
|
||||
mode: 'auto',
|
||||
...applyAgentPermissionMode({ mode: 'yolo' })
|
||||
})
|
||||
const updates = agentLaunchSettingsMutationUpdates(
|
||||
automatic,
|
||||
{ type: 'permissions', mode: 'manual', exceptAuto: true },
|
||||
'darwin'
|
||||
)
|
||||
expect(updates.agentDefaultArgs?.gemini).toBe('')
|
||||
expect(() =>
|
||||
agentLaunchSettingsMutationUpdates(
|
||||
automatic,
|
||||
{ type: 'permissions', mode: 'auto', exceptAuto: true },
|
||||
'darwin'
|
||||
)
|
||||
).toThrow('Auto cannot apply to agents without automatic permission review')
|
||||
})
|
||||
})
|
||||
@@ -12,35 +12,6 @@ function deferred<T>() {
|
||||
}
|
||||
|
||||
describe('createCoalescingKeyedRunner', () => {
|
||||
it('queues a caller with a different share key behind the run in flight, never beside it', async () => {
|
||||
const run = createCoalescingKeyedRunner<string>()
|
||||
const first = deferred<string>()
|
||||
let active = 0
|
||||
let maxActive = 0
|
||||
const tracked = (work: () => Promise<string>) => async () => {
|
||||
active += 1
|
||||
maxActive = Math.max(maxActive, active)
|
||||
try {
|
||||
return await work()
|
||||
} finally {
|
||||
active -= 1
|
||||
}
|
||||
}
|
||||
const leadingWork = tracked(() => first.promise)
|
||||
const leading = run('repo', 'origin', leadingWork)
|
||||
const other = vi.fn(async () => 'upstream result')
|
||||
const queued = run('repo', 'upstream', tracked(other))
|
||||
await Promise.resolve()
|
||||
expect(other).not.toHaveBeenCalled()
|
||||
|
||||
first.resolve('origin result')
|
||||
await expect(Promise.all([leading, queued])).resolves.toEqual([
|
||||
'origin result',
|
||||
'upstream result'
|
||||
])
|
||||
expect(maxActive).toBe(1)
|
||||
})
|
||||
|
||||
it('answers every caller from a run of its own share key, in arrival order', async () => {
|
||||
const run = createCoalescingKeyedRunner<string>()
|
||||
const first = deferred<string>()
|
||||
@@ -83,18 +54,4 @@ describe('createCoalescingKeyedRunner', () => {
|
||||
held.resolve('held')
|
||||
await expect(heldResult).resolves.toBe('held')
|
||||
})
|
||||
|
||||
it('starts fresh once a key has settled, instead of joining a finished run', async () => {
|
||||
const run = createCoalescingKeyedRunner<number>()
|
||||
let runs = 0
|
||||
const work = async () => ++runs
|
||||
|
||||
await expect(run('repo', 'target', work)).resolves.toBe(1)
|
||||
await expect(run('repo', 'target', work)).resolves.toBe(2)
|
||||
|
||||
const leading = run('repo', 'target', work)
|
||||
const trailing = run('repo', 'target', work)
|
||||
await expect(Promise.all([leading, trailing])).resolves.toEqual([3, 4])
|
||||
await expect(run('repo', 'target', work)).resolves.toBe(5)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -26,79 +26,6 @@ const message = {
|
||||
}
|
||||
afterEach(() => vi.useRealTimers())
|
||||
|
||||
it.each(['initial', 'renew', 'reacquire'] as const)(
|
||||
'end during pending %s suppresses delivery and releases exactly after owned work',
|
||||
async (phase) => {
|
||||
vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] })
|
||||
const host = queuedEditHost('session', message)
|
||||
const onLost = vi.fn()
|
||||
const events: string[] = []
|
||||
let finish!: () => void
|
||||
let count = 0
|
||||
const request = vi.fn(
|
||||
async (
|
||||
params: AgentSessionQueuedMessageEditHoldParams
|
||||
): Promise<AgentSessionQueuedMessageEditHoldResult> => {
|
||||
events.push(`start:${params.action}`)
|
||||
const key = { callerKey: 'client', editId: params.editId }
|
||||
if (params.action === 'release') {
|
||||
host.leases.release(key, params.messageId)
|
||||
events.push('end:release')
|
||||
return { status: 'released' }
|
||||
}
|
||||
count++
|
||||
if (count === (phase === 'initial' ? 1 : phase === 'renew' ? 2 : 3)) {
|
||||
await new Promise<void>((resolve) => {
|
||||
finish = resolve
|
||||
})
|
||||
}
|
||||
const answer =
|
||||
params.action === 'acquire'
|
||||
? host.acquire(params.editId, params.expectedBodyFingerprint ?? '')
|
||||
: host.leases.renew(key, params.messageId)
|
||||
events.push(`end:${params.action}:${answer.status}`)
|
||||
return answer
|
||||
}
|
||||
)
|
||||
const lease = startQueuedEditLease({
|
||||
sessionId: 'session',
|
||||
messageId: 'card',
|
||||
editId: 'edit',
|
||||
expectedBodyFingerprint: host.row!.fingerprint,
|
||||
request,
|
||||
onLost
|
||||
})
|
||||
if (phase !== 'initial') {
|
||||
await lease.acquired
|
||||
if (phase === 'reacquire') {
|
||||
host.leases.retire('card')
|
||||
}
|
||||
await vi.advanceTimersByTimeAsync(QUEUED_EDIT_LEASE_RENEW_MS)
|
||||
}
|
||||
expect(finish).toBeTypeOf('function')
|
||||
lease.end()
|
||||
lease.end()
|
||||
expect(events.at(-1)).toBe(`start:${phase === 'renew' ? 'renew' : 'acquire'}`)
|
||||
expect(request.mock.calls.some(([params]) => params.action === 'release')).toBe(false)
|
||||
host.depart()
|
||||
finish()
|
||||
await vi.advanceTimersByTimeAsync(0)
|
||||
expect(events.slice(-2)).toEqual(['start:release', 'end:release'])
|
||||
expect(onLost).not.toHaveBeenCalled()
|
||||
expect(request.mock.calls.filter(([params]) => params.action === 'release')).toHaveLength(1)
|
||||
expect(request.mock.calls.at(-1)?.[0]).toEqual({
|
||||
sessionId: 'session',
|
||||
messageId: 'card',
|
||||
editId: 'edit',
|
||||
action: 'release'
|
||||
})
|
||||
const calls = request.mock.calls.length
|
||||
await vi.advanceTimersByTimeAsync(4 * QUEUED_EDIT_LEASE_RENEW_MS)
|
||||
expect(request).toHaveBeenCalledTimes(calls)
|
||||
host.leases.dispose()
|
||||
}
|
||||
)
|
||||
|
||||
it('keeps the existing held/failure/expired-to-held request protocol without terminal delivery', async () => {
|
||||
vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] })
|
||||
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
|
||||
@@ -71,55 +71,6 @@ function outline(rows: readonly HostSectionRow[]): string[] {
|
||||
})
|
||||
}
|
||||
|
||||
it('retires recorded deletions through paired feed, pins and lineage', () => {
|
||||
const rows = [
|
||||
normalizePairedServerRow('runtime:a', {
|
||||
worktreeId: 'r::/p',
|
||||
repoId: 'r',
|
||||
worktreeInstanceId: 'p',
|
||||
isPinned: true,
|
||||
removing: true,
|
||||
removalRecorded: true
|
||||
}),
|
||||
normalizePairedServerRow('runtime:a', {
|
||||
worktreeId: 'r::/c',
|
||||
repoId: 'r',
|
||||
worktreeInstanceId: 'c',
|
||||
parentWorktreeId: 'r::/p',
|
||||
lineageWorktreeInstanceId: 'c',
|
||||
parentWorktreeInstanceId: 'p'
|
||||
}),
|
||||
normalizePairedServerRow('runtime:a', {
|
||||
worktreeId: 'r::/legacy',
|
||||
repoId: 'r',
|
||||
removing: true
|
||||
}),
|
||||
normalizePairedServerRow('runtime:a', {
|
||||
worktreeId: 'r::/failed',
|
||||
repoId: 'r',
|
||||
removalError: 'Cleanup failed. Retry deletion.'
|
||||
}),
|
||||
normalizePairedServerRow('runtime:b', { worktreeId: 'r::/p', repoId: 'r' })
|
||||
].flatMap((row) => row ?? [])
|
||||
const repos = [
|
||||
repo('r', { executionHostId: 'runtime:a' }),
|
||||
repo('r', { executionHostId: 'runtime:b' })
|
||||
]
|
||||
const inputs = domains({
|
||||
pairedServerHosts: new Map(
|
||||
['runtime:a', 'runtime:b'].map((hostId) => [
|
||||
hostId,
|
||||
{ freshness: 'live' as const, host: null }
|
||||
])
|
||||
),
|
||||
pairedServerRows: new Map(rows.map((row) => [row.key, row]))
|
||||
})
|
||||
const settings = view({ pinnedDisplayPolicy: 'duplicate-in-groups' })
|
||||
const built = buildSidebarFeedRows(inputs, catalog(repos), settings)
|
||||
expect(outline(built.sectionRows)).toContain('runtime:a|r::/c@0')
|
||||
expect(outline(built.sectionRows)).not.toContain('runtime:a|r::/p@0')
|
||||
})
|
||||
|
||||
describe('buildSidebarFeedRows nests each paired server’s own lineage', () => {
|
||||
const serverRows = (hostId: 'runtime:a' | 'runtime:b', recentAt: number) =>
|
||||
[
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { WorktreeOccurrence, WorktreeOccurrencePlan } from '../worktree-occurrence-plan'
|
||||
import type { Worktree } from '../worktree/types'
|
||||
import { buildWorktreeLineageRanges } from './worktree-lineage-range'
|
||||
|
||||
function row(id: string, overrides: Partial<Worktree> = {}): Worktree {
|
||||
return {
|
||||
id,
|
||||
repoId: 'repo',
|
||||
hostId: 'local',
|
||||
path: `/tmp/${id}`,
|
||||
head: 'abc123',
|
||||
branch: `refs/heads/${id}`,
|
||||
isBare: false,
|
||||
isMainWorktree: false,
|
||||
displayName: id,
|
||||
comment: '',
|
||||
linkedIssue: null,
|
||||
linkedPR: null,
|
||||
linkedLinearIssue: null,
|
||||
isArchived: false,
|
||||
isUnread: false,
|
||||
isPinned: false,
|
||||
sortOrder: 0,
|
||||
lastActivityAt: 0,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
/** One section: a parent at depth 0 with two children at depth 1. */
|
||||
function plan(rows: readonly Worktree[], depths = [0, 1, 1]): WorktreeOccurrencePlan<Worktree> {
|
||||
const occurrences = rows.map((worktree, index): WorktreeOccurrence<Worktree> => ({
|
||||
key: `section:${worktree.id}`,
|
||||
row: worktree,
|
||||
sectionKey: 'section',
|
||||
placement: 'natural',
|
||||
sectionPath: { key: 'section' },
|
||||
depth: depths[index]!,
|
||||
childCount: index === 0 ? rows.length - 1 : 0,
|
||||
isLastChild: index === rows.length - 1,
|
||||
showProjectName: false
|
||||
}))
|
||||
return { bySection: new Map([['section', occurrences]]), preferredByIdentity: new Map() }
|
||||
}
|
||||
|
||||
describe('buildWorktreeLineageRanges', () => {
|
||||
it('keeps the tree and ranges when a row is replaced without moving', () => {
|
||||
const rows = [row('parent'), row('a'), row('b')]
|
||||
const before = buildWorktreeLineageRanges(plan(rows))
|
||||
const replaced = row('a', { isUnread: true })
|
||||
const after = buildWorktreeLineageRanges(plan([rows[0]!, replaced, rows[2]!]))
|
||||
|
||||
const parent = after.get('section:parent')!
|
||||
expect(parent).toBe(before.get('section:parent'))
|
||||
// The reused tree carries the new row so fallback reads see it, and names the index.
|
||||
expect(parent.tree.worktrees[1]).toBe(replaced)
|
||||
expect(parent.tree.replacedRows).toEqual([[1]])
|
||||
})
|
||||
|
||||
it('rebuilds the tree when depth changes', () => {
|
||||
const rows = [row('parent'), row('a'), row('b')]
|
||||
const before = buildWorktreeLineageRanges(plan(rows))
|
||||
const after = buildWorktreeLineageRanges(plan([...rows], [0, 1, 2]))
|
||||
expect(after.get('section:parent')!.tree).not.toBe(before.get('section:parent')!.tree)
|
||||
expect(after.get('section:a')!.length).toBe(1)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user