mirror of
https://github.com/stablyai/orca.git
synced 2026-11-04 11:07:42 +00:00
test: remove 19 low-value cases and three suites (#34628)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -10467,7 +10467,6 @@
|
||||
"src/renderer/src/lib/ai-vault-resume-shell.test.ts": 487,
|
||||
"src/renderer/src/lib/ai-vault-resume-target.test.ts": 199,
|
||||
"src/renderer/src/lib/ai-vault-session-drag.test.ts": 45,
|
||||
"src/renderer/src/lib/ai-vault-session-fork-startup.test.ts": 315,
|
||||
"src/renderer/src/lib/ai-vault-session-resume-preparation.test.ts": 135,
|
||||
"src/renderer/src/lib/ai-vault-tab-title-sync-inputs.test.ts": 192,
|
||||
"src/renderer/src/lib/ai-vault-tab-title-sync.test.ts": 803,
|
||||
@@ -11039,7 +11038,6 @@
|
||||
"src/renderer/src/lib/worktree-agent-live-surface-adoption-layout-owner.test.ts": 1430,
|
||||
"src/renderer/src/lib/worktree-agent-structured-inventory.test.ts": 155,
|
||||
"src/renderer/src/lib/worktree-attachment-label.test.ts": 34,
|
||||
"src/renderer/src/lib/worktree-creation-agent-seeding.test.ts": 846,
|
||||
"src/renderer/src/lib/worktree-creation-agent-seeds.test.ts": 1098,
|
||||
"src/renderer/src/lib/worktree-creation-backend-startup-focus.test.ts": 1775,
|
||||
"src/renderer/src/lib/worktree-creation-background-mount.test.ts": 2109,
|
||||
@@ -11056,7 +11054,6 @@
|
||||
"src/renderer/src/lib/worktree-creation-structured-session-host-decline.test.ts": 2373,
|
||||
"src/renderer/src/lib/worktree-creation-structured-session.test.ts": 837,
|
||||
"src/renderer/src/lib/worktree-default-display-name.test.ts": 48,
|
||||
"src/renderer/src/lib/worktree-default-terminal-tabs-layout-owner.test.ts": 1147,
|
||||
"src/renderer/src/lib/worktree-display-name-order.test.ts": 57,
|
||||
"src/renderer/src/lib/worktree-host-connection-phase.test.ts": 1364,
|
||||
"src/renderer/src/lib/worktree-initial-terminal-close-records.test.ts": 2576,
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
import { getDefaultSettings } from '../../../shared/constants'
|
||||
import { TEST_REPO, makeWorktree } from '../store/slices/worktrees-slice-test-fixtures'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { AppState } from '@/store/types'
|
||||
import { buildAiVaultForkStartupForWorktree } from './ai-vault-session-fork-startup'
|
||||
|
||||
vi.mock('@/lib/new-workspace', () => ({
|
||||
CLIENT_PLATFORM: 'darwin'
|
||||
}))
|
||||
|
||||
type ForkState = Pick<
|
||||
AppState,
|
||||
| 'activeRepoId'
|
||||
| 'activeWorktreeId'
|
||||
| 'folderWorkspaces'
|
||||
| 'projectGroups'
|
||||
| 'endpointProjects'
|
||||
| 'endpointProjectHostSetups'
|
||||
| 'repos'
|
||||
| 'settings'
|
||||
| 'worktreesByRepo'
|
||||
>
|
||||
|
||||
function makeState(): ForkState {
|
||||
return {
|
||||
activeRepoId: 'repo-1',
|
||||
activeWorktreeId: 'repo-1::worktree-1',
|
||||
folderWorkspaces: [],
|
||||
projectGroups: [],
|
||||
repos: [{ ...TEST_REPO, id: 'repo-1', path: '/Users/ada/repo' }],
|
||||
endpointProjects: [
|
||||
{
|
||||
id: 'repo-1',
|
||||
displayName: 'repo',
|
||||
badgeColor: '',
|
||||
sourceRepoIds: ['repo-1'],
|
||||
createdAt: 0,
|
||||
updatedAt: 0
|
||||
}
|
||||
],
|
||||
endpointProjectHostSetups: [],
|
||||
settings: {
|
||||
...getDefaultSettings('/tmp'),
|
||||
agentPermissionDefault: 'own',
|
||||
agentDefaultArgs: { claude: '', codex: '' },
|
||||
agentDefaultEnv: { claude: {}, codex: {} }
|
||||
},
|
||||
worktreesByRepo: {
|
||||
'repo-1': [
|
||||
makeWorktree({ id: 'repo-1::worktree-1', repoId: 'repo-1', path: '/Users/ada/repo' })
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SESSION_ID = '019fd532-7c11-7a90-b6de-4e1a2c3d5f60'
|
||||
|
||||
describe('AI Vault fork into the CLI', () => {
|
||||
it('forks Claude in the recorded folder without claiming the chat-owned session', () => {
|
||||
const startup = buildAiVaultForkStartupForWorktree({
|
||||
state: makeState(),
|
||||
worktreeId: 'repo-1::worktree-1',
|
||||
session: { agent: 'claude', sessionId: SESSION_ID, cwd: '/Users/ada/repo', codexHome: null }
|
||||
})
|
||||
|
||||
expect(startup).toMatchObject({
|
||||
command: `claude '--resume' '${SESSION_ID}' '--fork-session'`,
|
||||
cwd: '/Users/ada/repo'
|
||||
})
|
||||
// A cold restore of the tab must never re-enter the conversation the chat still owns.
|
||||
expect(startup).not.toHaveProperty('providerSession')
|
||||
})
|
||||
|
||||
it('forks Codex under the home the transcript was found in', () => {
|
||||
const startup = buildAiVaultForkStartupForWorktree({
|
||||
state: makeState(),
|
||||
worktreeId: 'repo-1::worktree-1',
|
||||
session: {
|
||||
agent: 'codex',
|
||||
sessionId: SESSION_ID,
|
||||
cwd: '/Users/ada/repo',
|
||||
codexHome: '/Users/ada/.orca/codex-accounts/a1/home'
|
||||
}
|
||||
})
|
||||
|
||||
expect(startup?.command).toBe(
|
||||
`CODEX_HOME='/Users/ada/.orca/codex-accounts/a1/home' codex '-c' 'tui.resume_cwd=current' 'fork' '${SESSION_ID}'`
|
||||
)
|
||||
expect(startup).not.toHaveProperty('providerSession')
|
||||
})
|
||||
|
||||
it('returns null for an agent that cannot fork instead of a plain resume', () => {
|
||||
expect(
|
||||
buildAiVaultForkStartupForWorktree({
|
||||
state: makeState(),
|
||||
worktreeId: 'repo-1::worktree-1',
|
||||
session: { agent: 'gemini', sessionId: SESSION_ID, cwd: '/Users/ada/repo', codexHome: null }
|
||||
})
|
||||
).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -23,75 +23,6 @@ describe('resolveGitHubPrStartPointForRepo', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('resolves local PR heads through worktree IPC with PR branch hints', async () => {
|
||||
vi.mocked(window.api.worktrees.resolvePrBase).mockResolvedValue({
|
||||
baseBranch: 'abc123',
|
||||
compareBaseRef: 'refs/remotes/origin/main',
|
||||
branchNameOverride: 'feature/fix',
|
||||
pushTarget: { remoteName: 'origin', branchName: 'feature/fix' }
|
||||
})
|
||||
|
||||
await expect(
|
||||
resolveGitHubPrStartPointForRepo({
|
||||
repoId: 'repo-1',
|
||||
prNumber: 42,
|
||||
target: { kind: 'local' as const },
|
||||
headRefName: 'feature/fix',
|
||||
baseRefName: 'main',
|
||||
isCrossRepository: false
|
||||
})
|
||||
).resolves.toMatchObject({
|
||||
baseBranch: 'abc123',
|
||||
branchNameOverride: 'feature/fix'
|
||||
})
|
||||
|
||||
expect(window.api.worktrees.resolvePrBase).toHaveBeenCalledWith({
|
||||
repoId: 'repo-1',
|
||||
prNumber: 42,
|
||||
headRefName: 'feature/fix',
|
||||
baseRefName: 'main',
|
||||
isCrossRepository: false
|
||||
})
|
||||
expect(callRuntimeRpc).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('routes runtime-owned PR head resolution through runtime RPC', async () => {
|
||||
vi.mocked(callRuntimeRpc).mockResolvedValue({
|
||||
baseBranch: 'def456',
|
||||
compareBaseRef: 'refs/remotes/origin/develop',
|
||||
branchNameOverride: 'feature/runtime',
|
||||
pushTarget: { remoteName: 'fork', branchName: 'feature/runtime' }
|
||||
})
|
||||
|
||||
await expect(
|
||||
resolveGitHubPrStartPointForRepo({
|
||||
repoId: 'repo-runtime',
|
||||
prNumber: 7,
|
||||
target: { kind: 'environment' as const, environmentId: 'env-1' },
|
||||
headRefName: 'feature/runtime',
|
||||
baseRefName: 'develop',
|
||||
isCrossRepository: true
|
||||
})
|
||||
).resolves.toMatchObject({
|
||||
baseBranch: 'def456',
|
||||
branchNameOverride: 'feature/runtime'
|
||||
})
|
||||
|
||||
expect(callRuntimeRpc).toHaveBeenCalledWith(
|
||||
{ kind: 'environment', environmentId: 'env-1' },
|
||||
'worktree.resolvePrBase',
|
||||
{
|
||||
repo: 'repo-runtime',
|
||||
prNumber: 7,
|
||||
headRefName: 'feature/runtime',
|
||||
baseRefName: 'develop',
|
||||
isCrossRepository: true
|
||||
},
|
||||
{ timeoutMs: 30_000 }
|
||||
)
|
||||
expect(window.api.worktrees.resolvePrBase).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('throws resolver errors so submit can abort before creating from the wrong base', async () => {
|
||||
vi.mocked(window.api.worktrees.resolvePrBase).mockResolvedValue({
|
||||
error: 'Could not resolve PR head.'
|
||||
|
||||
@@ -41,28 +41,6 @@ describe('beginDirectWorkItemStructuredLaunch', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('opens the provisional chat synchronously and preserves the requested tab id', () => {
|
||||
const order: string[] = []
|
||||
mocks.beginStructuredAgentSessionProvisionalLaunch.mockImplementation((args) => {
|
||||
order.push('begin')
|
||||
args.beforeOpen('session-1')
|
||||
order.push('open')
|
||||
return { sessionId: 'session-1', tab: { id: 'agent-session:session-1' } }
|
||||
})
|
||||
|
||||
expect(
|
||||
beginDirectWorkItemStructuredLaunch({
|
||||
plan: structuredPlan,
|
||||
primaryTabId: null,
|
||||
beforeOpen: (sessionId) => {
|
||||
order.push(`reveal:${sessionId}`)
|
||||
return true
|
||||
}
|
||||
})
|
||||
).toEqual({ completed: true, structuredLaunch: true, primaryTabId: 'agent-session:session-1' })
|
||||
expect(order).toEqual(['begin', 'reveal:session-1', 'open'])
|
||||
})
|
||||
|
||||
it('does not claim completion when the provisional opener is refused', () => {
|
||||
mocks.beginStructuredAgentSessionProvisionalLaunch.mockReturnValue(null)
|
||||
|
||||
@@ -74,20 +52,6 @@ describe('beginDirectWorkItemStructuredLaunch', () => {
|
||||
})
|
||||
).toEqual({ completed: false, structuredLaunch: true, primaryTabId: 'setup-shell-tab' })
|
||||
})
|
||||
|
||||
it('skips structured opening for non-structured routes', () => {
|
||||
expect(
|
||||
beginDirectWorkItemStructuredLaunch({
|
||||
plan: adoptAgentSessionLaunchVerdict({
|
||||
...structuredPlan,
|
||||
route: 'terminal-tui'
|
||||
}),
|
||||
primaryTabId: null,
|
||||
beforeOpen: vi.fn()
|
||||
})
|
||||
).toEqual({ completed: false, structuredLaunch: false, primaryTabId: null })
|
||||
expect(mocks.beginStructuredAgentSessionProvisionalLaunch).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
// Why (#29599): a paired server's new workspace picks from that workspace's agents on the server.
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
/**
|
||||
* What the deleted `resolveRuntimeOwnerForWorktree` answered over the owner matrix (main @
|
||||
* 37c314bcc960), so its replacements can list every deliberate difference.
|
||||
*/
|
||||
export const LEGACY_RUNTIME_OWNER_GOLDEN: Readonly<Record<string, string | null>> = {
|
||||
'focused | null': null,
|
||||
'focused | global-floating-terminal': null,
|
||||
'focused | local-repo::wt': null,
|
||||
'focused | legacy-repo::wt': null,
|
||||
'focused | runtime-repo::wt': 'owner-env',
|
||||
'focused | ssh-repo::wt': null,
|
||||
'focused | hub-repo::wt': 'hub-env',
|
||||
'focused | twin-repo::wt': 'ambiguous',
|
||||
'focused | repo-twin-only::wt': 'ambiguous',
|
||||
'focused | stale-hub-repo::wt': null,
|
||||
'focused | rival-repo::wt': 'ambiguous',
|
||||
'focused | conflict-repo::wt': 'ambiguous',
|
||||
'focused | projected-twin::wt': 'ambiguous',
|
||||
'focused | detected-repo::wt': 'owner-env',
|
||||
'focused | unknown-repo::wt': null,
|
||||
'focused | folder:local-folder': null,
|
||||
'focused | folder:runtime-folder': 'owner-env',
|
||||
'focused | folder:ssh-folder': null,
|
||||
'focused | folder:twin-folder': null,
|
||||
'focused | folder:group-twin-folder': null,
|
||||
'focused | folder:unstamped-folder': null,
|
||||
'focused | folder:restored-folder': 'owner-env',
|
||||
'focused | folder:missing-folder': null,
|
||||
'no focus | null': null,
|
||||
'no focus | global-floating-terminal': null,
|
||||
'no focus | local-repo::wt': null,
|
||||
'no focus | legacy-repo::wt': null,
|
||||
'no focus | runtime-repo::wt': 'owner-env',
|
||||
'no focus | ssh-repo::wt': null,
|
||||
'no focus | hub-repo::wt': 'hub-env',
|
||||
'no focus | twin-repo::wt': 'ambiguous',
|
||||
'no focus | repo-twin-only::wt': 'ambiguous',
|
||||
'no focus | stale-hub-repo::wt': null,
|
||||
'no focus | rival-repo::wt': 'ambiguous',
|
||||
'no focus | conflict-repo::wt': 'ambiguous',
|
||||
'no focus | projected-twin::wt': 'ambiguous',
|
||||
'no focus | detected-repo::wt': 'owner-env',
|
||||
'no focus | unknown-repo::wt': null,
|
||||
'no focus | folder:local-folder': null,
|
||||
'no focus | folder:runtime-folder': 'owner-env',
|
||||
'no focus | folder:ssh-folder': null,
|
||||
'no focus | folder:twin-folder': null,
|
||||
'no focus | folder:group-twin-folder': null,
|
||||
'no focus | folder:unstamped-folder': null,
|
||||
'no focus | folder:restored-folder': 'owner-env',
|
||||
'no focus | folder:missing-folder': null,
|
||||
'single focus | null': null,
|
||||
'single focus | global-floating-terminal': null,
|
||||
'single focus | local-repo::wt': null,
|
||||
'single focus | legacy-repo::wt': 'focused-env',
|
||||
'single focus | runtime-repo::wt': 'owner-env',
|
||||
'single focus | ssh-repo::wt': null,
|
||||
'single focus | hub-repo::wt': 'hub-env',
|
||||
'single focus | twin-repo::wt': 'ambiguous',
|
||||
'single focus | repo-twin-only::wt': 'ambiguous',
|
||||
'single focus | stale-hub-repo::wt': null,
|
||||
'single focus | rival-repo::wt': 'ambiguous',
|
||||
'single focus | conflict-repo::wt': 'ambiguous',
|
||||
'single focus | projected-twin::wt': 'ambiguous',
|
||||
'single focus | detected-repo::wt': 'owner-env',
|
||||
'single focus | unknown-repo::wt': null,
|
||||
'single focus | folder:local-folder': null,
|
||||
'single focus | folder:runtime-folder': 'owner-env',
|
||||
'single focus | folder:ssh-folder': null,
|
||||
'single focus | folder:twin-folder': 'focused-env',
|
||||
'single focus | folder:group-twin-folder': 'focused-env',
|
||||
'single focus | folder:unstamped-folder': 'focused-env',
|
||||
'single focus | folder:restored-folder': 'owner-env',
|
||||
'single focus | folder:missing-folder': 'focused-env',
|
||||
'active local | twin-repo::wt': null,
|
||||
'active ssh:box | twin-repo::wt': null,
|
||||
'active local | rival-repo::wt': null,
|
||||
'active ssh:box | rival-repo::wt': null,
|
||||
'active local | conflict-repo::wt': null,
|
||||
'active ssh:box | conflict-repo::wt': 'hub-a',
|
||||
'active local | folder:twin-folder': null,
|
||||
'active ssh:box | folder:twin-folder': null,
|
||||
'active local | folder:group-twin-folder': null,
|
||||
'active ssh:box | folder:group-twin-folder': null
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
import type { ILink, ILinkProvider, Terminal } from '@xterm/xterm'
|
||||
import type { ILink, ILinkProvider } from '@xterm/xterm'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
guardLinkProvider,
|
||||
installGuardedLinkProviderRegistration
|
||||
} from './terminal-link-provider-guard'
|
||||
import { guardLinkProvider } from './terminal-link-provider-guard'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
recordRendererCrashBreadcrumb: vi.fn()
|
||||
@@ -51,17 +48,6 @@ describe('guardLinkProvider', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('passes provided links through unchanged when the provider succeeds', () => {
|
||||
const links = [{ text: 'term_abc' }] as unknown as ILink[]
|
||||
const provider: ILinkProvider = {
|
||||
provideLinks: (_lineNumber, callback) => callback(links)
|
||||
}
|
||||
const guarded = guardLinkProvider(provider, 'orca-handle')
|
||||
|
||||
expect(collectLinks(guarded)).toBe(links)
|
||||
expect(mocks.recordRendererCrashBreadcrumb).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not double-invoke the callback when the provider throws after resolving', () => {
|
||||
const links = [{ text: 'file.ts' }] as unknown as ILink[]
|
||||
const provider: ILinkProvider = {
|
||||
@@ -79,31 +65,3 @@ describe('guardLinkProvider', () => {
|
||||
expect(mocks.recordRendererCrashBreadcrumb).toHaveBeenCalledOnce()
|
||||
})
|
||||
})
|
||||
|
||||
describe('installGuardedLinkProviderRegistration', () => {
|
||||
it('guards every provider registered after install (addon-internal included)', () => {
|
||||
const registered: ILinkProvider[] = []
|
||||
const terminal = {
|
||||
registerLinkProvider: (provider: ILinkProvider) => {
|
||||
registered.push(provider)
|
||||
return { dispose: vi.fn() }
|
||||
}
|
||||
} as unknown as Terminal
|
||||
|
||||
installGuardedLinkProviderRegistration(terminal)
|
||||
|
||||
terminal.registerLinkProvider({
|
||||
provideLinks: () => {
|
||||
throw new RangeError('Invalid array length')
|
||||
}
|
||||
})
|
||||
|
||||
expect(registered).toHaveLength(1)
|
||||
expect(() => collectLinks(registered[0])).not.toThrow()
|
||||
expect(collectLinks(registered[0])).toBeUndefined()
|
||||
expect(mocks.recordRendererCrashBreadcrumb).toHaveBeenCalledWith(
|
||||
'terminal_link_provider_error',
|
||||
expect.objectContaining({ provider: 'provider-1', errorName: 'RangeError' })
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/** Hand-reviewed owner per matrix case for `resolveOwnerOrLocal` (round-3 design §5). */
|
||||
export const OWNER_OR_LOCAL_ACCEPTANCE: Record<string, string> = {
|
||||
'focused | global-floating-terminal': 'self@local',
|
||||
'focused | local-repo::wt': 'self@local',
|
||||
'focused | legacy-repo::wt': 'self@local',
|
||||
'focused | runtime-repo::wt': 'env:owner-env@local',
|
||||
'focused | ssh-repo::wt': 'self@ssh:box',
|
||||
'focused | hub-repo::wt': 'env:hub-env@ssh:box',
|
||||
'focused | twin-repo::wt': 'ambiguous',
|
||||
'focused | repo-twin-only::wt': 'self@local',
|
||||
'focused | stale-hub-repo::wt': 'env:hub-env@ssh:target',
|
||||
'focused | rival-repo::wt': 'ambiguous',
|
||||
'focused | conflict-repo::wt': 'ambiguous',
|
||||
'focused | projected-twin::wt': 'ambiguous',
|
||||
'focused | detected-repo::wt': 'env:owner-env@local',
|
||||
'focused | unknown-repo::wt': 'self@local',
|
||||
'focused | folder:local-folder': 'self@local',
|
||||
'focused | folder:runtime-folder': 'env:owner-env@ssh:inner-ssh',
|
||||
'focused | folder:ssh-folder': 'self@ssh:box',
|
||||
'focused | folder:twin-folder': 'ambiguous',
|
||||
'focused | folder:group-twin-folder': 'ambiguous',
|
||||
'focused | folder:unstamped-folder': 'self@local',
|
||||
'focused | folder:restored-folder': 'env:owner-env@local',
|
||||
'focused | folder:missing-folder': 'self@local',
|
||||
'no focus | global-floating-terminal': 'self@local',
|
||||
'no focus | local-repo::wt': 'self@local',
|
||||
'no focus | legacy-repo::wt': 'self@local',
|
||||
'no focus | runtime-repo::wt': 'env:owner-env@local',
|
||||
'no focus | ssh-repo::wt': 'self@ssh:box',
|
||||
'no focus | hub-repo::wt': 'env:hub-env@ssh:box',
|
||||
'no focus | twin-repo::wt': 'ambiguous',
|
||||
'no focus | repo-twin-only::wt': 'self@local',
|
||||
'no focus | stale-hub-repo::wt': 'env:hub-env@ssh:target',
|
||||
'no focus | rival-repo::wt': 'ambiguous',
|
||||
'no focus | conflict-repo::wt': 'ambiguous',
|
||||
'no focus | projected-twin::wt': 'ambiguous',
|
||||
'no focus | detected-repo::wt': 'env:owner-env@local',
|
||||
'no focus | unknown-repo::wt': 'self@local',
|
||||
'no focus | folder:local-folder': 'self@local',
|
||||
'no focus | folder:runtime-folder': 'env:owner-env@ssh:inner-ssh',
|
||||
'no focus | folder:ssh-folder': 'self@ssh:box',
|
||||
'no focus | folder:twin-folder': 'ambiguous',
|
||||
'no focus | folder:group-twin-folder': 'ambiguous',
|
||||
'no focus | folder:unstamped-folder': 'self@local',
|
||||
'no focus | folder:restored-folder': 'env:owner-env@local',
|
||||
'no focus | folder:missing-folder': 'self@local',
|
||||
'single focus | global-floating-terminal': 'self@local',
|
||||
'single focus | local-repo::wt': 'self@local',
|
||||
'single focus | legacy-repo::wt': 'self@local',
|
||||
'single focus | runtime-repo::wt': 'env:owner-env@local',
|
||||
'single focus | ssh-repo::wt': 'self@ssh:box',
|
||||
'single focus | hub-repo::wt': 'env:hub-env@ssh:box',
|
||||
'single focus | twin-repo::wt': 'ambiguous',
|
||||
'single focus | repo-twin-only::wt': 'self@local',
|
||||
'single focus | stale-hub-repo::wt': 'env:hub-env@ssh:target',
|
||||
'single focus | rival-repo::wt': 'ambiguous',
|
||||
'single focus | conflict-repo::wt': 'ambiguous',
|
||||
'single focus | projected-twin::wt': 'ambiguous',
|
||||
'single focus | detected-repo::wt': 'env:owner-env@local',
|
||||
'single focus | unknown-repo::wt': 'self@local',
|
||||
'single focus | folder:local-folder': 'self@local',
|
||||
'single focus | folder:runtime-folder': 'env:owner-env@ssh:inner-ssh',
|
||||
'single focus | folder:ssh-folder': 'self@ssh:box',
|
||||
'single focus | folder:twin-folder': 'ambiguous',
|
||||
'single focus | folder:group-twin-folder': 'ambiguous',
|
||||
'single focus | folder:unstamped-folder': 'self@local',
|
||||
'single focus | folder:restored-folder': 'env:owner-env@local',
|
||||
'single focus | folder:missing-folder': 'self@local',
|
||||
'active local | twin-repo::wt': 'self@local',
|
||||
'active ssh:box | twin-repo::wt': 'self@ssh:box',
|
||||
'active local | rival-repo::wt': 'self@local',
|
||||
'active ssh:box | rival-repo::wt': 'ambiguous',
|
||||
'active local | conflict-repo::wt': 'self@local',
|
||||
'active ssh:box | conflict-repo::wt': 'ambiguous',
|
||||
'active local | folder:twin-folder': 'self@local',
|
||||
'active ssh:box | folder:twin-folder': 'self@ssh:box',
|
||||
'active local | folder:group-twin-folder': 'self@local',
|
||||
'active ssh:box | folder:group-twin-folder': 'self@ssh:box'
|
||||
}
|
||||
@@ -3,8 +3,7 @@ import type { ExecutionHostOwnerMatch } from '../../../shared/worktree-execution
|
||||
import type { HostAuthority } from '../../../shared/host-authority'
|
||||
import { resolveOwnerOrLocal } from './worktree-runtime-owner'
|
||||
import { OWNER_RESOLUTION_CASES } from './runtime-owner-alias-matrix.test-fixture'
|
||||
import { OWNER_OR_LOCAL_ACCEPTANCE } from './resolve-owner-acceptance.test-fixture'
|
||||
import { LEGACY_RUNTIME_OWNER_GOLDEN } from './legacy-runtime-owner-answers.test-fixture'
|
||||
|
||||
import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state'
|
||||
|
||||
function describeMatch(match: ExecutionHostOwnerMatch<HostAuthority>): string {
|
||||
@@ -15,55 +14,6 @@ function describeMatch(match: ExecutionHostOwnerMatch<HostAuthority>): string {
|
||||
return `${endpoint.kind === 'self' ? 'self' : `env:${endpoint.environmentId}`}@${at}`
|
||||
}
|
||||
|
||||
const casesWithIds = OWNER_RESOLUTION_CASES.flatMap((c) =>
|
||||
c.worktreeId === null ? [] : [{ ...c, worktreeId: c.worktreeId }]
|
||||
)
|
||||
|
||||
// Each deliberate change from the deprecated resolver, with the design's reason (§5).
|
||||
const DELIBERATE_CHANGES: Record<string, string> = {
|
||||
'twin-repo::wt': 'D77: a twin is never picked by default',
|
||||
'projected-twin::wt': 'D77',
|
||||
'folder:twin-folder': 'D77',
|
||||
'folder:group-twin-folder': 'D77',
|
||||
'rival-repo::wt': 'B: servers contest the SSH host',
|
||||
'conflict-repo::wt': 'B',
|
||||
'stale-hub-repo::wt': 'C: reached through the server whose repo row names the target',
|
||||
'legacy-repo::wt': 'an unstamped row is local; the Active Server focus never claims it',
|
||||
'folder:unstamped-folder': 'same',
|
||||
'folder:missing-folder': 'same'
|
||||
}
|
||||
|
||||
describe('resolveOwnerOrLocal acceptance (round-3 design §5)', () => {
|
||||
it('pins the owner of every matrix case', () => {
|
||||
const actual = Object.fromEntries(
|
||||
casesWithIds.map((c) => [
|
||||
c.name,
|
||||
describeMatch(resolveOwnerOrLocal(c.state, { workspaceId: c.worktreeId }))
|
||||
])
|
||||
)
|
||||
expect(actual).toEqual(OWNER_OR_LOCAL_ACCEPTANCE)
|
||||
})
|
||||
|
||||
it('differs from the deprecated resolver only in the deliberate cases', () => {
|
||||
const changed = casesWithIds.filter((c) => {
|
||||
const owner = resolveOwnerOrLocal(c.state, { workspaceId: c.worktreeId })
|
||||
const environmentId =
|
||||
owner.kind === 'resolved' && owner.owner.endpoint.kind === 'environment'
|
||||
? owner.owner.endpoint.environmentId
|
||||
: null
|
||||
const legacy = LEGACY_RUNTIME_OWNER_GOLDEN[c.name]
|
||||
// Why: the deprecated environment-id reader collapsed `ambiguous` to `null`.
|
||||
return owner.kind !== 'resolved' || environmentId !== (legacy === 'ambiguous' ? null : legacy)
|
||||
})
|
||||
for (const c of changed) {
|
||||
expect(DELIBERATE_CHANGES[c.worktreeId], c.name).toBeDefined()
|
||||
}
|
||||
expect(new Set(changed.map((c) => c.worktreeId))).toEqual(
|
||||
new Set(Object.keys(DELIBERATE_CHANGES))
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('resolveOwnerOrLocal probes', () => {
|
||||
const base = OWNER_RESOLUTION_CASES.find((c) => c.name === 'focused | stale-hub-repo::wt')!.state
|
||||
const single = OWNER_RESOLUTION_CASES.find(
|
||||
|
||||
@@ -2,48 +2,10 @@ import { describe, expect, it } from 'vitest'
|
||||
import { resolveTerminalHostOwnership, resolveTerminalPtyOwner } from './terminal-worktree-route'
|
||||
import { resolveOwner } from './resolve-owner'
|
||||
import { OWNER_RESOLUTION_CASES } from './runtime-owner-alias-matrix.test-fixture'
|
||||
import { LEGACY_RUNTIME_OWNER_GOLDEN } from './legacy-runtime-owner-answers.test-fixture'
|
||||
|
||||
import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state'
|
||||
|
||||
const describeOwner = (owner: ReturnType<typeof resolveTerminalPtyOwner>) =>
|
||||
owner.kind === 'runtime' ? owner.environmentId : owner.kind === 'ambiguous' ? 'ambiguous' : null
|
||||
|
||||
// Each deliberate change from the deleted resolver's teardown answer (round-3 design §3, §4, §7).
|
||||
const DELIBERATE_CHANGES: Record<string, string | null> = {
|
||||
// C: teardown follows spawn, which already starts these PTYs on the server.
|
||||
'focused | stale-hub-repo::wt': 'hub-env',
|
||||
'no focus | stale-hub-repo::wt': 'hub-env',
|
||||
'single focus | stale-hub-repo::wt': 'hub-env',
|
||||
// D77: a folder twin names no host; only each PTY's own `remote:<env>` id may.
|
||||
'focused | folder:twin-folder': 'ambiguous',
|
||||
'no focus | folder:twin-folder': 'ambiguous',
|
||||
'single focus | folder:twin-folder': 'ambiguous',
|
||||
'focused | folder:group-twin-folder': 'ambiguous',
|
||||
'no focus | folder:group-twin-folder': 'ambiguous',
|
||||
'single focus | folder:group-twin-folder': 'ambiguous',
|
||||
// B: two servers contest the selected SSH host, so spawn refuses and teardown must too.
|
||||
'active ssh:box | rival-repo::wt': 'ambiguous',
|
||||
'active ssh:box | conflict-repo::wt': 'ambiguous',
|
||||
// An unstamped row is this machine's; the Active Server focus never claims its PTYs.
|
||||
'single focus | legacy-repo::wt': null,
|
||||
'single focus | folder:unstamped-folder': null,
|
||||
'single focus | folder:missing-folder': null
|
||||
}
|
||||
|
||||
describe('resolveTerminalPtyOwner acceptance', () => {
|
||||
it('answers as the deleted resolver did, except the deliberate cases', () => {
|
||||
const actual = Object.fromEntries(
|
||||
OWNER_RESOLUTION_CASES.map((c) => [
|
||||
c.name,
|
||||
describeOwner(resolveTerminalPtyOwner(c.state, c.worktreeId))
|
||||
])
|
||||
)
|
||||
expect(actual).toEqual({ ...LEGACY_RUNTIME_OWNER_GOLDEN, ...DELIBERATE_CHANGES })
|
||||
for (const name of Object.keys(DELIBERATE_CHANGES)) {
|
||||
expect(LEGACY_RUNTIME_OWNER_GOLDEN[name], name).not.toBe(DELIBERATE_CHANGES[name])
|
||||
}
|
||||
})
|
||||
|
||||
it('keeps a setup terminal and a row-less worktree local under single focus', () => {
|
||||
const single = OWNER_RESOLUTION_CASES.find((c) => c.name === 'single focus | legacy-repo::wt')!
|
||||
expect(resolveTerminalPtyOwner(single.state, 'ephemeral-setup-terminal:skill-panel')).toEqual({
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { WorktreeCreationRequest } from './pending-worktree-creation'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
activateAndRevealWorktree: vi.fn(),
|
||||
completeWorktreeCreation: vi.fn(),
|
||||
ensureWorktreeHasInitialTerminal: vi.fn(),
|
||||
ensureWebRuntimeWorktreeTerminalAfterWake: vi.fn()
|
||||
}))
|
||||
|
||||
const store = {
|
||||
activePendingCreationId: null as string | null,
|
||||
activeView: 'tasks' as 'tasks' | 'terminal',
|
||||
createWorktree: vi.fn(),
|
||||
pendingWorktreeCreations: {} as Record<string, unknown>,
|
||||
repos: []
|
||||
}
|
||||
|
||||
vi.mock('@/store', () => ({ useAppStore: { getState: () => store } }))
|
||||
vi.mock('@/lib/worktree-activation', () => ({
|
||||
activateAndRevealWorktree: mocks.activateAndRevealWorktree
|
||||
}))
|
||||
vi.mock('@/lib/worktree-initial-terminal-seeding', () => ({
|
||||
ensureWorktreeHasInitialTerminal: mocks.ensureWorktreeHasInitialTerminal
|
||||
}))
|
||||
vi.mock('@/lib/worktree-creation-completion', () => ({
|
||||
completeWorktreeCreation: mocks.completeWorktreeCreation
|
||||
}))
|
||||
vi.mock('@/lib/web-runtime-worktree-terminal-after-wake', () => ({
|
||||
ensureWebRuntimeWorktreeTerminalAfterWake: mocks.ensureWebRuntimeWorktreeTerminalAfterWake
|
||||
}))
|
||||
|
||||
import { executeWorktreeCreation } from './worktree-creation-flow-execute'
|
||||
|
||||
const request: WorktreeCreationRequest = {
|
||||
repoId: 'repo-1',
|
||||
name: 'feature',
|
||||
setupDecision: 'inherit',
|
||||
agent: 'codex',
|
||||
agentLaunchRoute: 'terminal-tui',
|
||||
pendingFirstAgentMessageRename: false,
|
||||
note: '',
|
||||
startupPlan: null,
|
||||
quickPrompt: '',
|
||||
quickTelemetry: null
|
||||
}
|
||||
|
||||
describe('executeWorktreeCreation agent seeding', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
store.activePendingCreationId = null
|
||||
store.activeView = 'tasks'
|
||||
store.pendingWorktreeCreations = { 'creation-1': { creationId: 'creation-1' } }
|
||||
store.createWorktree.mockResolvedValue({
|
||||
worktree: { id: 'worktree-1', repoId: request.repoId }
|
||||
})
|
||||
})
|
||||
|
||||
it('treats a background blank workspace as empty instead of waiting for the host', async () => {
|
||||
await executeWorktreeCreation('creation-1', { ...request, agent: null })
|
||||
|
||||
expect(mocks.ensureWebRuntimeWorktreeTerminalAfterWake).toHaveBeenCalledWith('worktree-1', {
|
||||
startup: undefined,
|
||||
agent: null,
|
||||
activate: false,
|
||||
unknownHostCountIsEmpty: true
|
||||
})
|
||||
})
|
||||
|
||||
it('routes a background agent selection through host-aware surface creation', async () => {
|
||||
await executeWorktreeCreation('creation-1', request)
|
||||
|
||||
expect(mocks.activateAndRevealWorktree).not.toHaveBeenCalled()
|
||||
expect(mocks.ensureWorktreeHasInitialTerminal).not.toHaveBeenCalled()
|
||||
expect(mocks.ensureWebRuntimeWorktreeTerminalAfterWake).toHaveBeenCalledOnce()
|
||||
expect(mocks.ensureWebRuntimeWorktreeTerminalAfterWake).toHaveBeenCalledWith('worktree-1', {
|
||||
startup: undefined,
|
||||
agent: 'codex',
|
||||
activate: false,
|
||||
unknownHostCountIsEmpty: true
|
||||
})
|
||||
expect(mocks.completeWorktreeCreation).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ primaryTabId: null })
|
||||
)
|
||||
})
|
||||
|
||||
it('passes the agent selection through an active reveal', async () => {
|
||||
store.activePendingCreationId = 'creation-1'
|
||||
store.activeView = 'terminal'
|
||||
mocks.activateAndRevealWorktree.mockReturnValue({ primaryTabId: null })
|
||||
|
||||
await executeWorktreeCreation('creation-1', request)
|
||||
|
||||
expect(mocks.activateAndRevealWorktree).toHaveBeenCalledWith(
|
||||
'worktree-1',
|
||||
expect.objectContaining({ agent: 'codex' })
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,116 +0,0 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { useAppStore, type AppState } from '@/store'
|
||||
import { makeWorktree, seedStore } from '@/store/slices/store-test-helpers'
|
||||
import { createStoreCascadesMockApi } from '@/store/slices/store-cascades-test-harness'
|
||||
import { LayoutApplyParams } from '../../../shared/rpc-contract/workspace-layout-params'
|
||||
import { applyDefaultTerminalTabs } from './worktree-default-terminal-tabs'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
layoutOwnerIsMain: true,
|
||||
callRuntimeRpc: vi.fn(async (_target: unknown, _method: string, _params: unknown) => ({
|
||||
ok: true,
|
||||
result: {}
|
||||
}))
|
||||
}))
|
||||
|
||||
vi.mock('../../../shared/layout-owner-switch', () => ({
|
||||
get ORCA_LAYOUT_OWNER_IS_MAIN() {
|
||||
return mocks.layoutOwnerIsMain
|
||||
}
|
||||
}))
|
||||
vi.mock('@/runtime/runtime-rpc-client', () => ({ callRuntimeRpc: mocks.callRuntimeRpc }))
|
||||
vi.mock('sonner', () => ({
|
||||
toast: { info: vi.fn(), success: vi.fn(), error: vi.fn(), warning: vi.fn() }
|
||||
}))
|
||||
|
||||
createStoreCascadesMockApi()
|
||||
|
||||
const WT = 'repo1::/path/wt1'
|
||||
const NO_TEMPLATES = { runCommands: false, tabs: [] }
|
||||
|
||||
const store = useAppStore
|
||||
let initialState: AppState
|
||||
|
||||
beforeEach(() => {
|
||||
mocks.layoutOwnerIsMain = true
|
||||
mocks.callRuntimeRpc.mockClear()
|
||||
initialState = store.getState()
|
||||
seedStore(store, {
|
||||
worktreesByRepo: { repo1: [makeWorktree({ id: WT, repoId: 'repo1', path: '/path/wt1' })] },
|
||||
activeWorktreeId: WT
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
store.setState(initialState, true)
|
||||
})
|
||||
|
||||
function applyNoTemplates(): string | null {
|
||||
return applyDefaultTerminalTabs(
|
||||
store.getState(),
|
||||
WT,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
NO_TEMPLATES,
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
}
|
||||
|
||||
function sentCommands() {
|
||||
return mocks.callRuntimeRpc.mock.calls.map(([, method, params]) => {
|
||||
expect(method).toBe('layout.apply')
|
||||
return LayoutApplyParams.parse(params).command
|
||||
})
|
||||
}
|
||||
|
||||
describe('the default-tabs mark in a layout-owner build', () => {
|
||||
it('records the mark in main when a workspace has no default tab templates', () => {
|
||||
expect(applyNoTemplates()).toBeNull()
|
||||
|
||||
expect(store.getState().tabsByWorktree[WT]).toBeUndefined()
|
||||
expect(sentCommands()).toEqual([{ type: 'setDefaultTabsApplied', workspace: WT }])
|
||||
})
|
||||
|
||||
it('records the mark after the default tabs, so main never holds it without their rows', () => {
|
||||
const tabId = applyDefaultTerminalTabs(
|
||||
store.getState(),
|
||||
WT,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
{ runCommands: false, tabs: [{ title: 'one' }, { title: 'two' }] },
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
|
||||
expect(tabId).not.toBeNull()
|
||||
expect(sentCommands().map((command) => command.type)).toEqual([
|
||||
'createTerminalTab',
|
||||
'renameTab',
|
||||
'createTerminalTab',
|
||||
'renameTab',
|
||||
'setDefaultTabsApplied'
|
||||
])
|
||||
})
|
||||
|
||||
it('records it once: a workspace already marked sends nothing', () => {
|
||||
applyNoTemplates()
|
||||
mocks.callRuntimeRpc.mockClear()
|
||||
|
||||
store.getState().markDefaultTerminalTabsApplied(WT)
|
||||
applyNoTemplates()
|
||||
|
||||
expect(mocks.callRuntimeRpc).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('sends nothing with the switch off', () => {
|
||||
mocks.layoutOwnerIsMain = false
|
||||
|
||||
applyNoTemplates()
|
||||
|
||||
expect(store.getState().defaultTerminalTabsAppliedByWorktreeId[WT]).toBe(true)
|
||||
expect(mocks.callRuntimeRpc).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user