Files
orca/src/main/runtime/runtime-terminal-spawn-push-target-materialization.test.ts
T
Neil c4a60815b2 fix(worktrees): a sort order and a minted push remote land on the copy that named them (#30976)
* fix(worktrees): a sort order and a minted push remote land on the copy that named them, not skipped for a twin

The renderer already knows each id's host when it splits a sort order; it now sends it
(an optional parallel hostIds; older peers ignore it and keep the skip-twins rule). An IPC
git handler knows the host it required, so a minted push remote records on that copy.

* fix(worktrees): a twinned repo re-lists instead of an id-keyed sort patch; client hosts must name a registered repo

* fix(worktrees): a sort order naming an unregistered host skips that id instead of falling back

* fix(worktrees): a moving project's sort order is fenced before any host-qualified read

* fix(worktrees): a client's local sort-order host matches this server's own runtime stamp

* fix(worktrees): an exact host match wins before the own-runtime alias

* fix(worktrees): terminal-spawn materialization serves only a local row and records on the local copy

* test: drop casts from the spawn twin tests

* fix(worktrees): adopt evidence and the spawn hook stay on their host; an unmatched local keeps the id-only rule

* fix(worktrees): adopt evidence reads the host's own metadata rows

* fix(worktrees): reusing a same-URL remote reads ownership from this host only

* fix(worktrees): a server's own runtime-stamped row still materializes its push remote
2026-10-19 20:20:23 -07:00

155 lines
5.5 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { GitPushTarget } from '../../shared/worktree/types'
import type { Repo } from '../../shared/repo-types'
import type { Store } from '../persistence'
import { hostWorktreeTarget } from '../../shared/worktree/host-qualified-identity'
const { materializeLocalMock, getLocalProjectWorktreeGitOptionsMock } = vi.hoisted(() => ({
materializeLocalMock: vi.fn(),
getLocalProjectWorktreeGitOptionsMock: vi.fn()
}))
vi.mock('../ipc/worktree-remote', () => ({
materializeWorktreePushTargetRemote: materializeLocalMock
}))
vi.mock('../project-runtime-git-options', () => ({
getLocalProjectWorktreeGitOptions: getLocalProjectWorktreeGitOptionsMock
}))
import { triggerTerminalSpawnPushTargetMaterialization } from './runtime-terminal-spawn-push-target-materialization'
const WORKTREE_PATH = '/repo/worktree'
const FORK_URL = 'git@github.com:contributor/orca.git'
const REPO_ID = 'repo-1'
const STORE = {} as Store
const LOCAL_REPO = { id: REPO_ID, path: '/repo', connectionId: null } as unknown as Repo
const SSH_REPO = { id: REPO_ID, path: '/repo', connectionId: 'conn-1' } as unknown as Repo
function forkTarget(overrides: Partial<GitPushTarget> = {}): GitPushTarget {
return {
remoteName: 'pr-contributor-orca',
branchName: 'contributor/fix',
remoteUrl: FORK_URL,
...overrides
}
}
// Flush the fire-and-forget microtask queue so assertions see the dispatched call.
const flush = (): Promise<void> => new Promise((resolve) => setImmediate(resolve))
describe('triggerTerminalSpawnPushTargetMaterialization', () => {
let warnSpy: ReturnType<typeof vi.spyOn>
beforeEach(() => {
materializeLocalMock.mockReset().mockResolvedValue(undefined)
getLocalProjectWorktreeGitOptionsMock.mockReset().mockReturnValue({})
warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
})
afterEach(() => {
warnSpy.mockRestore()
})
it('is a no-op when there is no push target', () => {
triggerTerminalSpawnPushTargetMaterialization(WORKTREE_PATH, undefined, LOCAL_REPO, STORE)
expect(materializeLocalMock).not.toHaveBeenCalled()
})
it('is a no-op for a same-repo push target with no remoteUrl', () => {
triggerTerminalSpawnPushTargetMaterialization(
WORKTREE_PATH,
forkTarget({ remoteUrl: undefined }),
LOCAL_REPO,
STORE
)
expect(materializeLocalMock).not.toHaveBeenCalled()
})
it('is a no-op when the target already reports remoteCreated', () => {
triggerTerminalSpawnPushTargetMaterialization(
WORKTREE_PATH,
forkTarget({ remoteCreated: true }),
LOCAL_REPO,
STORE
)
expect(materializeLocalMock).not.toHaveBeenCalled()
})
it('materializes over the local transport with resolved WSL git options, repoId and worktreeId, fire-and-forget', () => {
getLocalProjectWorktreeGitOptionsMock.mockReturnValue({ wslDistro: 'Ubuntu' })
const target = forkTarget()
const result = triggerTerminalSpawnPushTargetMaterialization(
WORKTREE_PATH,
target,
LOCAL_REPO,
STORE,
REPO_ID,
'worktree-1'
)
expect(result).toBeUndefined()
expect(getLocalProjectWorktreeGitOptionsMock).toHaveBeenCalledWith(STORE, LOCAL_REPO)
expect(materializeLocalMock).toHaveBeenCalledWith(
WORKTREE_PATH,
target,
STORE,
REPO_ID,
{ wslDistro: 'Ubuntu' },
hostWorktreeTarget({ id: 'worktree-1', hostId: 'local' })
)
})
it('is a no-op for an SSH repo, never materializing on this machine', () => {
triggerTerminalSpawnPushTargetMaterialization(WORKTREE_PATH, forkTarget(), SSH_REPO, STORE)
// A row stamped with a remote host but no connection id is just as remote.
const stampedOnly: Repo = { ...LOCAL_REPO, executionHostId: 'ssh:box' }
triggerTerminalSpawnPushTargetMaterialization(WORKTREE_PATH, forkTarget(), stampedOnly, STORE)
expect(materializeLocalMock).not.toHaveBeenCalled()
expect(getLocalProjectWorktreeGitOptionsMock).not.toHaveBeenCalled()
})
it("records on a server's own runtime-stamped row, whose git runs here", () => {
const serverRow: Repo = { ...LOCAL_REPO, executionHostId: 'runtime:env-1' }
triggerTerminalSpawnPushTargetMaterialization(
WORKTREE_PATH,
forkTarget(),
serverRow,
STORE,
REPO_ID,
'worktree-1'
)
expect(materializeLocalMock.mock.calls[0]?.[5]).toEqual(
hostWorktreeTarget({ id: 'worktree-1', hostId: 'runtime:env-1' })
)
})
it('falls back to default git options when WSL project runtime resolution throws', () => {
getLocalProjectWorktreeGitOptionsMock.mockImplementation(() => {
throw new Error('repair-required')
})
triggerTerminalSpawnPushTargetMaterialization(WORKTREE_PATH, forkTarget(), LOCAL_REPO, STORE)
expect(materializeLocalMock).toHaveBeenCalledWith(
WORKTREE_PATH,
forkTarget(),
STORE,
undefined,
{},
undefined
)
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining('failed to resolve local git options'),
expect.any(Error)
)
})
it('swallows a materialize rejection instead of crashing the caller', async () => {
materializeLocalMock.mockRejectedValue(new Error('remote add failed'))
expect(() =>
triggerTerminalSpawnPushTargetMaterialization(WORKTREE_PATH, forkTarget(), LOCAL_REPO, STORE)
).not.toThrow()
await flush()
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining('failed to materialize push target remote'),
expect.any(Error)
)
})
})