mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
* 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
155 lines
5.5 KiB
TypeScript
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)
|
|
)
|
|
})
|
|
})
|