Files
orca/src/main/ipc/worktree-git-status-ref-watch.test.ts
T
Brennan Benson de64337c26 fix(worktree-watcher): refresh status after external pushes (#12361)
* fix(worktree-watcher): surface external push -u through the git-common watch

An external-shell 'git push -u' writes only the common .git/config (plus
refs/remotes/<remote>/<branch>), both invisible to the git-common event
filter, so the Checks panel stayed on 'No upstream configured' until the
renderer safety poll. Classify the common config and remote-tracking refs
as status-tier signals, poll config alongside the other primary-checkout
metadata files, and keep FETCH_HEAD/reflog/ref-lock churn ignored.

* fix(worktree-watcher): refresh after subsequent pushes
2026-08-05 11:26:13 -07:00

108 lines
3.3 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from 'vitest'
import {
clearActiveGitStatusRefBinding,
updateActiveGitStatusRefBinding,
type GitStatusRefBindingRequest,
type GitStatusRefWatchTarget
} from './worktree-git-status-ref-watch'
function makeTarget(): GitStatusRefWatchTarget {
return {
kind: 'git-common',
path: 'C:\\repo\\.git',
repos: new Map([['repo-1', { repoId: 'repo-1', repoName: 'repo', nestWorkspaces: false }]]),
gitStatusRefPaths: new Set()
}
}
describe('worktree git status ref watch binding', () => {
afterEach(() => clearActiveGitStatusRefBinding())
function request(overrides: Partial<GitStatusRefBindingRequest> = {}) {
return {
worktreeId: 'repo-1::C:\\repo',
worktreePath: 'C:\\repo',
executionHostId: 'local',
branch: 'refs/heads/feature/main',
upstreamName: 'origin/feature/main',
...overrides
}
}
it('binds a custom refspec destination exactly', async () => {
const target = makeTarget()
await updateActiveGitStatusRefBinding(
request(),
() => [target],
async () => 'refs/custom/origin/main'
)
expect([...target.gitStatusRefPaths]).toEqual(['C:/repo/.git/refs/custom/origin/main'])
})
it('does not spend the dynamic ref stat on a slash-named local upstream', async () => {
const target = makeTarget()
await updateActiveGitStatusRefBinding(
request(),
() => [target],
async () => 'refs/heads/feature/base'
)
expect(target.gitStatusRefPaths).toEqual(new Set())
})
it('keeps a slash-containing remote name exact', async () => {
const target = makeTarget()
await updateActiveGitStatusRefBinding(
request(),
() => [target],
async () => 'refs/remotes/team/fork/feature/main'
)
expect([...target.gitStatusRefPaths]).toEqual([
'C:/repo/.git/refs/remotes/team/fork/feature/main'
])
})
it('does not resolve the exact ref again on unchanged status ticks', async () => {
const target = makeTarget()
const resolve = vi.fn(async () => 'refs/remotes/origin/feature/main')
await updateActiveGitStatusRefBinding(request(), () => [target], resolve)
await updateActiveGitStatusRefBinding(request(), () => [target], resolve)
expect(resolve).toHaveBeenCalledOnce()
})
it('applies an in-flight resolution to a replacement watch', async () => {
const first = makeTarget()
const replacement = makeTarget()
let current = [first]
let finish: (ref: string) => void = () => {}
const pending = new Promise<string>((resolve) => {
finish = resolve
})
const update = updateActiveGitStatusRefBinding(
request(),
() => current,
() => pending
)
current = [replacement]
finish('refs/remotes/origin/feature/main')
await update
expect(first.gitStatusRefPaths).toEqual(new Set())
expect([...replacement.gitStatusRefPaths]).toEqual([
'C:/repo/.git/refs/remotes/origin/feature/main'
])
const latest = makeTarget()
current = [latest]
const resolve = vi.fn(async () => 'refs/remotes/origin/feature/main')
await updateActiveGitStatusRefBinding(request(), () => current, resolve)
expect(resolve).not.toHaveBeenCalled()
expect([...latest.gitStatusRefPaths]).toEqual(['C:/repo/.git/refs/remotes/origin/feature/main'])
})
})