Files
orca/src/cli/index-worktree-create-target.test.ts
T
Jinwoo Hong 8f15f217a2 Preserve user-set workspace names across branch changes (#17448)
* fix(worktrees): preserve user workspace names across branch changes

* test(worktrees): cover pinned rename metadata

* fix(workspaces): address display-name review edge cases

* fix(workspaces): keep automatic names fresh across refreshes

* fix(workspaces): preserve legacy CLI labels

* fix(workspaces): preserve display-name provenance across hosts

* fix(workspaces): honor legacy display-name provenance

* fix(workspaces): fence display-name refresh races

* fix(workspaces): accept peer renames from provenance-less hosts

The old-host preserve fence kept a pinned local label on every refresh,
which also suppressed a legitimate rename another client persisted
through the same host until app restart. Narrow it to labels the host
re-derived itself (branch short name, or path basename when detached);
any other changed label in a mode-less response is explicit meta a peer
wrote there. Stale prior-label responses stay covered by the downstream
staleness fence, in-flight writes by the pending fence.

* refactor(workspaces): unify display-name pin derivation

Three call sites (renderer optimistic update, local IPC updateMeta
handler, remote worktree.set handler) each restated the same formula;
a future edit to one would silently skew provenance between paths.
2026-08-31 19:08:05 -04:00

307 lines
8.7 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 { buildWorktree, okFixture, queueFixtures, worktreeListFixture } from './test-fixtures'
import { pairRuntimeEnvironment, useWorktreeAwarenessEnvironment } from './index-test-harness'
describe('orca cli worktree awareness', () => {
useWorktreeAwarenessEnvironment({
callMock,
serveOrcaAppMock,
getDefaultUserDataPathMock,
addEnvironmentFromPairingCodeMock,
listEnvironmentsMock,
spawnMock
})
it('passes explicit activation through worktree.create', async () => {
queueFixtures(
callMock,
worktreeListFixture([buildWorktree('/tmp/repo', 'main', 'abc', 'repo-1')]),
okFixture('req_create', {
worktree: buildWorktree('/tmp/repo/feature', 'feature', 'abc', 'repo-1')
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(
['worktree', 'create', '--repo', 'id:repo-1', '--name', 'feature', '--activate', '--json'],
'/tmp/repo'
)
expect(callMock).toHaveBeenNthCalledWith(2, 'worktree.create', {
repo: 'id:repo-1',
name: 'feature',
displayName: 'feature',
displayNameKind: 'user',
baseBranch: undefined,
linkedIssue: undefined,
comment: undefined,
runHooks: false,
activate: true,
navigation: 'all',
parentWorktree: undefined,
cwdParentWorktree: 'id:repo-1::/tmp/repo',
noParent: false,
callerTerminalHandle: undefined,
cliProvenanceRequest: {}
})
})
it('resolves project and host flags to the matching repo for worktree.create', async () => {
pairRuntimeEnvironment(listEnvironmentsMock, 'gpu')
queueFixtures(
callMock,
okFixture('req_project_setups', {
setups: [
{
id: 'setup-local',
projectId: 'github:stablyai/orca',
hostId: 'local',
repoId: 'repo-local',
path: '/tmp/orca',
displayName: 'Orca',
setupState: 'ready',
setupMethod: 'legacy-repo',
createdAt: 1,
updatedAt: 1
},
{
id: 'setup-gpu',
projectId: 'github:stablyai/orca',
hostId: 'runtime:gpu',
repoId: 'repo-gpu',
path: '/srv/orca',
displayName: 'Orca',
setupState: 'ready',
setupMethod: 'legacy-repo',
createdAt: 1,
updatedAt: 1
}
]
}),
okFixture('req_create', {
worktree: buildWorktree('/srv/orca/feature', 'feature', 'abc', 'repo-gpu'),
lineage: null,
warnings: []
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(
[
'worktree',
'create',
'--project',
'github:stablyai/orca',
'--host',
'runtime:gpu',
'--name',
'feature',
'--no-parent',
'--json'
],
'/tmp/repo'
)
expect(runtimeClientConstructorMock).toHaveBeenCalledWith(null, 'gpu')
expect(callMock).toHaveBeenNthCalledWith(1, 'projectHostSetup.list')
expect(callMock).toHaveBeenNthCalledWith(2, 'worktree.create', {
repo: 'id:repo-gpu',
name: 'feature',
displayName: 'feature',
displayNameKind: 'user',
baseBranch: undefined,
linkedIssue: undefined,
comment: undefined,
runHooks: false,
activate: false,
parentWorktree: undefined,
noParent: true,
callerTerminalHandle: undefined,
cliProvenanceRequest: {}
})
})
it('resolves project-host-setup directly for worktree.create', async () => {
queueFixtures(
callMock,
okFixture('req_project_setups', {
setups: [
{
id: 'setup-gpu',
projectId: 'github:stablyai/orca',
hostId: 'runtime:gpu',
repoId: 'repo-gpu',
path: '/srv/orca',
displayName: 'Orca',
setupState: 'ready',
setupMethod: 'legacy-repo',
createdAt: 1,
updatedAt: 1
}
]
}),
okFixture('req_create', {
worktree: buildWorktree('/srv/orca/feature', 'feature', 'abc', 'repo-gpu'),
lineage: null,
warnings: []
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
await main(
[
'worktree',
'create',
'--project-host-setup',
'setup-gpu',
'--name',
'feature',
'--no-parent',
'--json'
],
'/tmp/repo'
)
expect(callMock).toHaveBeenNthCalledWith(
2,
'worktree.create',
expect.objectContaining({ repo: 'id:repo-gpu' })
)
})
it('rejects mixing repo and project target flags on worktree.create', async () => {
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const priorExitCode = process.exitCode
await main(
[
'worktree',
'create',
'--repo',
'id:repo-local',
'--project',
'github:stablyai/orca',
'--name',
'feature',
'--json'
],
'/tmp/repo'
)
expect(callMock).not.toHaveBeenCalled()
expect([...logSpy.mock.calls, ...errSpy.mock.calls].flat().join('\n')).toContain(
'Choose either --repo or project target flags, not both.'
)
expect(process.exitCode).toBe(1)
process.exitCode = priorExitCode
})
it('passes caller terminal handle through worktree.create with cwd fallback', async () => {
process.env.ORCA_TERMINAL_HANDLE = 'term_parent'
queueFixtures(
callMock,
worktreeListFixture([buildWorktree('/tmp/repo', 'main', 'abc', 'repo-1')]),
okFixture('req_create', {
worktree: buildWorktree('/tmp/repo/child', 'child', 'abc', 'repo-1'),
lineage: null,
warnings: []
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
vi.spyOn(console, 'error').mockImplementation(() => {})
await main(
['worktree', 'create', '--repo', 'id:repo-1', '--name', 'child', '--json'],
'/tmp/repo'
)
expect(callMock).toHaveBeenCalledTimes(2)
expect(callMock).toHaveBeenNthCalledWith(2, 'worktree.create', {
repo: 'id:repo-1',
name: 'child',
displayName: 'child',
displayNameKind: 'user',
baseBranch: undefined,
linkedIssue: undefined,
comment: undefined,
runHooks: false,
activate: false,
parentWorktree: undefined,
cwdParentWorktree: 'id:repo-1::/tmp/repo',
noParent: false,
callerTerminalHandle: 'term_parent',
cliProvenanceRequest: { callerTerminalHandle: 'term_parent' }
})
})
it('marks every worktree.create as CLI-created even from an external shell', async () => {
// Why: the sidebar badge/filter must catch hand-typed creates too, so the
// provenance request is sent with no terminal handle rather than omitted.
delete process.env.ORCA_TERMINAL_HANDLE
queueFixtures(
callMock,
okFixture('req_create_external', {
worktree: buildWorktree('/tmp/repo/child', 'child', 'abc', 'repo-1'),
lineage: null,
warnings: []
})
)
vi.spyOn(console, 'log').mockImplementation(() => {})
vi.spyOn(console, 'error').mockImplementation(() => {})
await main(
['worktree', 'create', '--repo', 'id:repo-1', '--name', 'child', '--no-parent', '--json'],
'/tmp/repo'
)
expect(callMock).toHaveBeenCalledWith(
'worktree.create',
expect.objectContaining({ cliProvenanceRequest: {} })
)
})
})