test: remove repeated wiring and suspended orchestration suites (#41302)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-11-11 16:16:48 -08:00
committed by GitHub
co-authored by OrcaWin
parent d9d3dfdd08
commit 4509730131
9 changed files with 2 additions and 612 deletions
-35
View File
@@ -42,15 +42,6 @@ describe('DroidHookService', () => {
removeTreeSync(hoisted.home)
})
it('does not register PermissionRequest', () => {
const status = new DroidHookService().install()
expect(status.state).toBe('installed')
const hooks = readSettings().hooks
expect(hooks?.PermissionRequest).toBeUndefined()
expect(JSON.stringify(hooks?.Notification)).toContain('droid-hook')
})
it('removes a managed PermissionRequest command and keeps a user command', () => {
mkdirSync(dirname(settingsPath()), { recursive: true })
writeFileSync(
@@ -90,30 +81,4 @@ describe('DroidHookService', () => {
}
])
})
it('drops a previously managed PermissionRequest hook but keeps the user hook on it', () => {
const configPath = join(hoisted.home, '.factory', 'settings.json')
const userDefinition = {
matcher: 'Edit',
hooks: [{ type: 'command', command: '/usr/local/bin/my-approval.sh' }]
}
const managedCommand = join(hoisted.home, '.orca', 'agent-hooks', 'droid-hook.sh')
mkdirSync(dirname(configPath), { recursive: true })
writeFileSync(
configPath,
JSON.stringify({
hooks: {
PermissionRequest: [
{ matcher: '*', hooks: [{ type: 'command', command: managedCommand }] },
userDefinition
]
}
})
)
expect(new DroidHookService().install().state).toBe('installed')
const installed = readSettings()
expect(installed.hooks?.PermissionRequest).toEqual([userDefinition])
})
})
@@ -2,7 +2,6 @@
import { describe, expect, it, vi } from 'vitest'
import { HEADLESS_RUNTIME_WINDOW_ID } from '../../../shared/runtime-session-contracts'
import type { RuntimeMobileSessionTabsResult } from '../../../shared/runtime-session-contracts'
import * as selection from './mobile-session-tab-selection'
vi.mock('../../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: true }))
@@ -19,43 +18,9 @@ const WORKTREE = `id:${TEST_WORKTREE_ID}`
const CHAT = 'agent-session:s1'
/** The three saved tabs on a server with no window, whose spawns bind their pane as the host's do. */
async function headlessRuntime(count = 3) {
async function headlessRuntime() {
const harness = threeTabSurfaceRuntime()
const { runtime, bindPane } = harness
if (count > 3) {
const session = harness.store.getSession()
const tabs = session.tabsByWorktree[TEST_WORKTREE_ID]!
const extra = Array.from({ length: count - 3 }, (_, index) => ({
...tabs[0]!,
id: `00000000-0000-4000-8000-${index.toString().padStart(12, '0')}`,
ptyId: null,
sortOrder: index + 3,
createdAt: index + 4
}))
harness.setSession({
...session,
tabsByWorktree: { ...session.tabsByWorktree, [TEST_WORKTREE_ID]: [...tabs, ...extra] },
terminalLayoutsByTabId: {
...session.terminalLayoutsByTabId,
...Object.fromEntries(
extra.map((tab, index) => {
const leafId = `10000000-0000-4000-8000-${index.toString().padStart(12, '0')}`
return [
tab.id,
{ root: { type: 'leaf', leafId }, activeLeafId: leafId, expandedLeafId: null }
]
})
)
},
tabGroups: {
...session.tabGroups,
[TEST_WORKTREE_ID]: session.tabGroups![TEST_WORKTREE_ID]!.map((group) => ({
...group,
tabOrder: [...group.tabOrder, ...extra.map((tab) => tab.id)]
}))
}
})
}
runtime.syncWindowGraph(HEADLESS_RUNTIME_WINDOW_ID, { tabs: [], leaves: [] })
runtime.setPtyController({
spawn: vi.fn(async (args: { worktreeId?: string; tabId?: string; leafId?: string }) => {
@@ -83,60 +48,6 @@ const orderOf = (rows: readonly { type: string; id: string; parentTabId?: string
]
describe("a phone's layout change on a windowless server (switch on)", () => {
it('bounds explicit split selection traversal at N and 10N tabs', async () => {
const measurements: number[] = []
const select = selection.selectMobileSessionTab
for (const count of [50, 500]) {
const { runtime } = await headlessRuntime(count)
let visits = 0
function observed<T>(items: T[]): T[] {
const rows = [...items]
items.forEach((item, index) => {
Object.defineProperty(rows, index, {
get() {
visits++
return item
}
})
})
return rows
}
let selectionVisits = 0
const selected = vi
.spyOn(selection, 'selectMobileSessionTab')
.mockImplementation((snapshot, active) => {
const tabs = observed(snapshot.tabs)
const tabGroups = snapshot.tabGroups?.map((group) => ({
...group,
tabOrder: observed(group.tabOrder)
}))
visits = 0
const result = select({ ...snapshot, tabs, tabGroups }, active)
selectionVisits += visits
return result
})
await runtime.moveMobileSessionTab(WORKTREE, {
kind: 'split',
tabId: SECOND_TAB_ID,
targetGroupId: SAVED_GROUP_ID,
splitDirection: 'right'
})
expect(selected).toHaveBeenCalledTimes(1)
// The original tab has two panes: resolve + flags + one row copy, plus one tab-order scan.
expect(selectionVisits).toBeGreaterThan(0)
expect(selectionVisits).toBeLessThanOrEqual(3 * (count + 1) + count)
measurements.push(selectionVisits)
const stored = runtime.mobileSessionTabsByWorktree.get(TEST_WORKTREE_ID)!
expect(stored.tabs).toHaveLength(count + 1)
expect(stored.activeTabId).toBe(`${SECOND_TAB_ID}::77777777-7777-4777-8777-777777777777`)
expect(stored.tabGroups?.[1]?.tabOrder).toEqual([SECOND_TAB_ID])
expect(stored.tabs.filter((row) => row.isActive).map((row) => row.id)).toEqual([
`${SECOND_TAB_ID}::77777777-7777-4777-8777-777777777777`
])
selected.mockRestore()
}
expect(measurements[1]).toBeLessThanOrEqual(10 * measurements[0]!)
})
it("splits a tab into a group of its own in main's layout, and selects it there", async () => {
const { runtime, store } = await headlessRuntime()
const selected = `${SECOND_TAB_ID}::77777777-7777-4777-8777-777777777777`
@@ -217,23 +128,6 @@ describe("a phone's layout change on a windowless server (switch on)", () => {
)
})
it("answers a split main refuses with main's refusal code", async () => {
const { runtime } = await headlessRuntime()
vi.spyOn(runtime, 'applyLayoutCommand').mockResolvedValue({ ok: false, code: 'invalid_params' })
await expect(
runtime.moveMobileSessionTab(WORKTREE, {
kind: 'split',
tabId: SECOND_TAB_ID,
targetGroupId: SAVED_GROUP_ID,
splitDirection: 'right'
})
).rejects.toThrow('invalid_params')
expect(tabStrip(await runtime.listMobileSessionTabs(WORKTREE))).toEqual([
[SURFACE_TAB_ID, SECOND_TAB_ID, THIRD_TAB_ID]
])
})
it.each([
{
client: 'a phone with split-group placement',
@@ -76,27 +76,6 @@ describe('app icon selection', () => {
expect(dockSetIconMock.mock.lastCall?.[0].isEmpty()).toBe(true)
})
it('keeps an imported icon out of the app bundle and leaves the live Dock tile alone', async () => {
const commands: string[][] = []
dockSetIconMock.mockClear()
persistMacDockIcon('custom', {
appBundlePath: '/Applications/Orca.app',
execFile: (file, args, _options, callback) => {
commands.push([file, ...args])
callback?.(null)
},
isDevApp: false,
platform: 'darwin'
})
await waitForQueuedPersistence()
expect(commands.some((command) => command.join(' ').includes('setIcon:image'))).toBe(false)
expect(commands.some((command) => command.join(' ').includes('setIcon:(missing value)'))).toBe(
true
)
expect(dockSetIconMock).not.toHaveBeenCalled()
})
it('serializes rapid macOS dock icon persistence so the last icon request wins', async () => {
const pendingCallbacks: (() => void)[] = []
const execFile = vi.fn(
@@ -373,25 +373,6 @@ it('refuses normalized data-image HTML without dropping surrounding text', async
expect(imageUploads.upload).not.toHaveBeenCalled()
})
it('leaves ordinary HTML paste on the editor parser path', async () => {
const view = render(<GitHubMarkdownComposer value="" onChange={() => {}} placeholder="HTML" />)
await waitFor(() => expect(view.container.querySelector('.tiptap')).not.toBeNull())
const parse = vi.spyOn(DOMParser.prototype, 'parseFromString')
try {
fireEvent.paste(getComposerEditor(view.container), {
clipboardData: {
files: [],
getData: (type: string) =>
type === 'text/html' ? '<p>ordinary <strong>formatted</strong> text</p>' : ''
}
})
expect(getComposerEditor(view.container).textContent).toBe('ordinary formatted text')
expect(parse).not.toHaveBeenCalled()
} finally {
parse.mockRestore()
}
})
it('shows a failed hosted image as its filename link without changing the uploaded Markdown', async () => {
imageUploads.upload
.mockReset()
@@ -1,145 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { runIssueUpdate, runWorkItemBodyUpdate } from './github-work-item-edit-mutations'
import type { GitHubWorkItem } from '../../../../shared/github/work-item-types'
import type { TaskSourceContext } from '../../../../shared/work-items/task-source-context'
import { callRuntimeRpc } from '@/runtime/rpc/calls/runtime-rpc-call'
vi.mock('@/runtime/rpc/calls/runtime-rpc-call', () => ({
callRuntimeRpc: vi.fn()
}))
vi.mock('@/store', () => ({ useAppStore: { getState: vi.fn(() => ({ repos: [] })) } }))
vi.mock('@/components/github/github-work-item-comment-mutations', () => ({
notifyWorkItemDetailsMutation: vi.fn()
}))
const ORIGIN = { owner: 'fork-owner', repo: 'widgets', host: 'github.com' }
const UPSTREAM = { owner: 'upstream-owner', repo: 'widgets', host: 'github.com' }
const projectOrigin = {
...UPSTREAM,
number: 12,
type: 'issue' as const,
projectId: 'project-1',
projectItemId: 'project-item-12',
cacheKey: 'project-cache'
}
const localSource: TaskSourceContext = {
kind: 'task-source',
provider: 'github',
projectId: 'project-1',
hostId: 'local',
repoId: 'repo-1'
}
const item: GitHubWorkItem = {
id: 'issue:12',
type: 'issue',
number: 12,
title: 'Origin issue',
state: 'open',
url: 'https://github.com/fork-owner/widgets/issues/12',
labels: [],
updatedAt: '',
author: null,
repoId: 'repo-1'
}
describe('issue edits retain the displayed repository', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.mocked(callRuntimeRpc).mockResolvedValue({ ok: true })
vi.stubGlobal('window', {
api: {
gh: {
updateIssue: vi.fn().mockResolvedValue({ ok: true }),
updateIssueBySlug: vi.fn(),
updatePullRequestBySlug: vi.fn()
}
}
})
})
describe.each(['local', 'runtime:project-source'] as const)('Project source %s', (hostId) => {
const sourceContext = { ...localSource, hostId }
it('saves the Project row body through the original exact slug actor', async () => {
const bodyImageUrls = {
'https://github.com/user-attachments/assets/ce11040a-fb66-4289-927f-547b16dfc488':
'https://private-user-images.githubusercontent.com/1/image.png?jwt=TEST'
}
vi.mocked(window.api.gh.updateIssueBySlug).mockResolvedValue({ ok: true, bodyImageUrls })
vi.mocked(callRuntimeRpc).mockResolvedValue({ ok: true, bodyImageUrls })
const result = await runWorkItemBodyUpdate({
item: { ...item, url: 'https://github.com/upstream-owner/widgets/issues/12' },
repoPath: '/home/fixture/fork-widgets',
sourceContext,
projectOrigin,
body: 'Project edit',
parsedSlug: UPSTREAM
})
expect(result).toEqual({ bodyImageUrls })
})
})
it.each([
{ state: 'closed' as const },
{ addLabels: ['bug'] },
{ addAssignees: ['fork-assignee'] }
])('pins field edits to the opened issue repository: %j', async (updates) => {
await runIssueUpdate({
repoPath: 'C:\\workspace\\widgets',
repoId: 'repo-1',
projectOrigin: undefined,
number: 12,
issueRepo: ORIGIN,
updates
})
expect(window.api.gh.updateIssue).toHaveBeenCalledWith(
expect.objectContaining({
repoPath: 'C:\\workspace\\widgets',
ownerRepo: ORIGIN,
updates
})
)
expect(window.api.gh.updateIssueBySlug).not.toHaveBeenCalled()
})
it.each([true, false])(
'forwards local PR body PATCH maps without adding a reread (%s)',
async (hasMap) => {
const body =
'![shot](https://github.com/user-attachments/assets/ce11040a-fb66-4289-927f-547b16dfc488)'
vi.mocked(window.api.gh.updatePullRequestBySlug).mockResolvedValue(
hasMap
? {
ok: true,
bodyImageUrls: {
'https://github.com/user-attachments/assets/ce11040a-fb66-4289-927f-547b16dfc488':
'https://private-user-images.githubusercontent.com/1/image.png?jwt=TEST'
}
}
: { ok: true }
)
const result = await runWorkItemBodyUpdate({
item: { ...item, type: 'pr' },
repoPath: '/repo',
sourceContext: localSource,
projectOrigin: undefined,
body,
parsedSlug: ORIGIN
})
expect(result).toEqual({
bodyImageUrls: hasMap
? {
'https://github.com/user-attachments/assets/ce11040a-fb66-4289-927f-547b16dfc488':
'https://private-user-images.githubusercontent.com/1/image.png?jwt=TEST'
}
: undefined
})
expect(window.api.gh.updatePullRequestBySlug).toHaveBeenCalledWith(
expect.objectContaining({
updates: {
body: '![shot](https://github.com/user-attachments/assets/ce11040a-fb66-4289-927f-547b16dfc488)'
}
})
)
}
)
})
@@ -166,41 +166,4 @@ describe('EphemeralVmsPane', () => {
expect(container.textContent).toContain('Newest Recipe')
expect(container.textContent).not.toContain('Stale Recipe')
})
it('names the credential-like variables a recipe inherits, without values', async () => {
const container = await renderPane()
await vi.waitFor(() =>
expect(container.querySelector('[data-testid="recipe-env-exposure"]')?.textContent).toContain(
'ANTHROPIC_API_KEY, GITHUB_TOKEN'
)
)
expect(container.textContent).toContain('Set passEnv on the recipe')
})
it('lists only allowlisted credentials when the recipe sets passEnv', async () => {
const listRecipeCatalog = window.api.ephemeralVm.listRecipeCatalog as ReturnType<typeof vi.fn>
listRecipeCatalog.mockReset().mockResolvedValue([
{
repoId: 'repo-1',
repoName: 'Repo',
repoPath: '/repo',
diagnostics: [],
recipes: [
{
id: 'limited',
name: 'Limited',
create: 'create',
passEnv: ['GITHUB_TOKEN']
}
]
}
])
const container = await renderPane()
await vi.waitFor(() =>
expect(container.querySelector('[data-testid="recipe-env-exposure"]')?.textContent).toContain(
'from passEnv: GITHUB_TOKEN.'
)
)
expect(container.textContent).not.toContain('ANTHROPIC_API_KEY')
})
})
@@ -1,4 +1,4 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const { getStateMock, inspectRuntimeTerminalProcessMock, getStatusFeedMock } = vi.hoisted(() => ({
getStateMock: vi.fn(),
@@ -22,14 +22,6 @@ import { assessWindowCloseRunningWork } from './window-close-running-work'
const LOCAL_PTY = 'pty-local'
const SSH_PTY = 'ssh:openclaw@@pty-7'
const RUNTIME_PTY = 'remote:env-1@@handle-1'
const OWNERLESS_RUNTIME_PTY = 'remote:handle-2'
const BUSY = {
foregroundProcess: 'pnpm build',
hasChildProcesses: true,
foregroundProcessEvidence: {}
}
const UNVERIFIABLE = {
foregroundProcess: null,
@@ -81,41 +73,7 @@ beforeEach(() => {
getStatusFeedMock.mockReturnValue(feed(null, 'unverifiable'))
})
afterEach(() => {
vi.useRealTimers()
})
describe('assessWindowCloseRunningWork', () => {
it.each(['working', 'attention'])(
'warns for a live %s chat without a PTY or ownership metadata',
async (status) => {
setState([])
representChat()
getStatusFeedMock.mockReturnValue(feed(status))
await expect(assessWindowCloseRunningWork({ isQuitting: false })).resolves.toEqual({
kind: 'running'
})
expect(inspectRuntimeTerminalProcessMock).not.toHaveBeenCalled()
}
)
it.each([
['working', 'unverifiable', {}],
['idle', 'live', { hostExecutionOwned: true, children: [{ kind: 'agent', state: 'working' }] }],
['idle', 'live', { children: [{ kind: 'agent', state: 'unverifiable' }] }],
[null, 'live', {}],
[undefined, 'live', {}]
])('does not warn for status %s observed as %s', async (status, observation, extra) => {
setState([])
representChat()
getStatusFeedMock.mockReturnValue(feed(status, observation, extra))
await expect(assessWindowCloseRunningWork({ isQuitting: false })).resolves.toEqual({
kind: 'none'
})
})
it('keeps equal chat IDs separate across hosts and resolves legacy tabs through their workspace owner', async () => {
setState([])
const state = getStateMock()
@@ -135,17 +93,6 @@ describe('assessWindowCloseRunningWork', () => {
})
})
it('does not use a local observation for an unserved SSH chat', async () => {
setState([])
representChat('ssh:openclaw')
getStatusFeedMock.mockReturnValue(feed('working'))
await expect(assessWindowCloseRunningWork({ isQuitting: false })).resolves.toEqual({
kind: 'none'
})
expect(getStatusFeedMock).not.toHaveBeenCalled()
})
it('uses current chat membership, owner and status after terminal probes settle', async () => {
setState([LOCAL_PTY])
representChat()
@@ -183,72 +130,4 @@ describe('assessWindowCloseRunningWork', () => {
})
expect(getStatusFeedMock).not.toHaveBeenCalled()
})
// #29916: a paired runtime owns its terminals and keeps them after this client quits.
it('does not warn on quit, or probe, for a live process on a paired runtime host', async () => {
setState([RUNTIME_PTY, OWNERLESS_RUNTIME_PTY])
answerWith({ [RUNTIME_PTY]: BUSY, [OWNERLESS_RUNTIME_PTY]: BUSY })
await expect(assessWindowCloseRunningWork({ isQuitting: true })).resolves.toEqual({
kind: 'none'
})
expect(inspectRuntimeTerminalProcessMock).not.toHaveBeenCalled()
})
it('still warns when closing the window over a live process on a paired runtime host', async () => {
setState([RUNTIME_PTY])
answerWith({ [RUNTIME_PTY]: BUSY })
await expect(assessWindowCloseRunningWork({ isQuitting: false })).resolves.toEqual({
kind: 'running'
})
})
// Unreachable remote work is unverifiable, never proof of exit; quitting silently could orphan it.
it('warns rather than quitting silently when a remote host answers unverifiable', async () => {
setState([SSH_PTY])
answerWith({ [SSH_PTY]: UNVERIFIABLE })
await expect(assessWindowCloseRunningWork({ isQuitting: true })).resolves.toEqual({
kind: 'unverifiable'
})
})
it('reports a live process even when a sibling remote pane is only unverifiable', async () => {
setState([SSH_PTY, RUNTIME_PTY])
answerWith({ [SSH_PTY]: UNVERIFIABLE, [RUNTIME_PTY]: BUSY })
await expect(assessWindowCloseRunningWork({ isQuitting: false })).resolves.toEqual({
kind: 'running'
})
})
it('still warns about a live local process when closing the window', async () => {
setState([LOCAL_PTY])
answerWith({ [LOCAL_PTY]: BUSY })
await expect(assessWindowCloseRunningWork({ isQuitting: false })).resolves.toEqual({
kind: 'running'
})
})
// Local probe failure has no lost-transport ambiguity and must not block closing.
it('does not warn when only a local probe is unverifiable', async () => {
setState([LOCAL_PTY])
answerWith({ [LOCAL_PTY]: UNVERIFIABLE })
await expect(assessWindowCloseRunningWork({ isQuitting: false })).resolves.toEqual({
kind: 'none'
})
})
// Quit consents to end this machine’s processes (#524); it does not consent to end remote work.
it('leaves local-only quit unprompted, and never probes for it', async () => {
setState([LOCAL_PTY])
answerWith({ [LOCAL_PTY]: BUSY })
await expect(assessWindowCloseRunningWork({ isQuitting: true })).resolves.toEqual({
kind: 'none'
})
expect(inspectRuntimeTerminalProcessMock).not.toHaveBeenCalled()
})
})