Files
orca/src/main/git/worktree-base-divergence.test.ts
T
OrcaWinandOrcaWin 4687e90a16 [comment audit] Continue main-process comment cleanup (#32946)
* [comment audit] Shorten daemon, hook, and emulator comments [skip ci]

* [comment audit] Shorten VM, conversation, SQLite, and theme-import comments [skip ci]

* [comment audit] Tighten Git execution and source-control comment contracts [skip ci]

* [comment audit] Clarify repository probes and maintenance comments [skip ci]

* [comment audit] Shorten status, diff-cache, and checkout comments [skip ci]

* [comment audit] Clarify worktree lifecycle and WSL routing comments [skip ci]

* [comment audit] Shorten forge identity and review-refresh comments [skip ci]

* [comment audit] Tighten hosted-review client comment contracts [skip ci]

* [comment audit] Clarify GitLab compatibility and review comments [skip ci]

* [comment audit] Tighten hook ownership and watchdog rationale [skip ci]

* [comment audit] Condense host and integration compatibility comments [skip ci]

* [comment audit] Clarify IPC authority and remote-routing rationale [skip ci]

* [comment audit] Finalize file-import and containment comment cleanup

* [comment audit] Fix stale Jira type imports blocking full CI

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 16:27:37 -07:00

210 lines
7.1 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({ gitExecFileAsync: vi.fn() }))
vi.mock('./runner', () => ({ gitExecFileAsync: mocks.gitExecFileAsync }))
import { GIT_READ_TIMEOUT_MS } from './command-runner/git-command-timeout'
import { GitAdmissionScheduler } from './command-runner/git-subprocess-admission'
import type { GitAdmissionTier } from './command-runner/git-exec-options'
import { WSL_GIT_READ_ENVIRONMENT_WAIT_MS } from './wsl-git-read-environment'
import {
measureRetargetDivergence,
RETARGET_DIVERGENCE_BUDGET_MS
} from './worktree-base-divergence'
type ExecOptions = {
cwd: string
timeout?: number
wslDistro?: string
signal?: AbortSignal
admissionTier?: GitAdmissionTier
}
function callOptions(): ExecOptions[] {
return mocks.gitExecFileAsync.mock.calls.map((call) => call[1] as ExecOptions)
}
function subcommands(): string[] {
return mocks.gitExecFileAsync.mock.calls.map((call) => (call[0] as string[])[0]!)
}
function answerProbes(count: string, mergeBase = 'abc123\n') {
mocks.gitExecFileAsync.mockImplementation(async (args: string[]) =>
args[0] === 'merge-base' ? { stdout: mergeBase } : { stdout: count }
)
}
function exitCodeError(code: number): Error & { code: number } {
return Object.assign(new Error('git exited'), { code })
}
beforeEach(() => {
mocks.gitExecFileAsync.mockReset()
})
describe('measureRetargetDivergence deadlines', () => {
it('finishes through interactive headroom while general capacity is occupied', async () => {
const scheduler = new GitAdmissionScheduler({ generalCap: 1, generalHeadroom: 1 })
const blocker = await scheduler.acquire({ args: ['status'], cwd: '/repo' })
mocks.gitExecFileAsync.mockImplementation(async (args: string[], options: ExecOptions) => {
const grant = await scheduler.acquire({
args,
cwd: options.cwd,
signal: options.signal,
tier: options.admissionTier
})
try {
return { stdout: args[0] === 'merge-base' ? 'abc123\n' : '1\n' }
} finally {
grant.release()
}
})
try {
await expect(
measureRetargetDivergence('/repo', 'refs/heads/main', 'refs/remotes/origin/main', {
admissionTier: 'interactive',
budgetMsForTest: 200
})
).resolves.toBe('within')
expect(subcommands()).toEqual(['rev-list', 'rev-list', 'merge-base'])
} finally {
blocker.release()
}
})
it('puts every probe under one shared budget, not a budget each', async () => {
answerProbes('3\n')
await expect(
measureRetargetDivergence('/repo', 'refs/heads/main', 'refs/remotes/origin/main')
).resolves.toBe('within')
expect(subcommands()).toEqual(['rev-list', 'rev-list', 'merge-base'])
const signals = callOptions().map((options) => options.signal)
// One budget spans all staged probes; per-probe budgets would add together.
expect(new Set(signals).size).toBe(1)
expect(signals[0]).toBeInstanceOf(AbortSignal)
})
it('also gives each probe a command timeout well below git default read deadline', async () => {
answerProbes('3\n')
await measureRetargetDivergence('/repo', 'refs/heads/main', 'refs/remotes/origin/main')
// Signals cover pre-spawn waits; command timeouts still bound a hung spawn.
for (const options of callOptions()) {
expect(options.timeout).toBe(RETARGET_DIVERGENCE_BUDGET_MS)
}
expect(RETARGET_DIVERGENCE_BUDGET_MS).toBeLessThan(GIT_READ_TIMEOUT_MS)
})
it('really aborts the in-flight probes when the shared budget expires', async () => {
mocks.gitExecFileAsync.mockImplementation(
(_args: string[], options: ExecOptions) =>
new Promise((_resolve, reject) => {
options.signal?.addEventListener(
'abort',
() =>
reject(
Object.assign(new Error('The operation was aborted.'), { name: 'AbortError' })
),
{ once: true }
)
})
)
await expect(
measureRetargetDivergence('/repo', 'refs/heads/main', 'refs/remotes/origin/main', {
budgetMsForTest: 25
})
).resolves.toBe('unknown')
})
it('clears the WSL read-environment wait, which starts before any command timeout', () => {
// Leave walk headroom after the first WSL environment probe.
expect(RETARGET_DIVERGENCE_BUDGET_MS).toBeGreaterThan(WSL_GIT_READ_ENVIRONMENT_WAIT_MS)
expect(RETARGET_DIVERGENCE_BUDGET_MS).toBeLessThan(GIT_READ_TIMEOUT_MS)
})
it('stops the probes when the create itself is cancelled, without waiting for the budget', async () => {
mocks.gitExecFileAsync.mockImplementation(
(_args: string[], options: ExecOptions) =>
new Promise((_resolve, reject) => {
options.signal?.addEventListener(
'abort',
() =>
reject(
Object.assign(new Error('The operation was aborted.'), { name: 'AbortError' })
),
{ once: true }
)
})
)
const controller = new AbortController()
const pending = measureRetargetDivergence(
'/repo',
'refs/heads/main',
'refs/remotes/origin/main',
// A budget long enough that only the caller's cancellation can end this in time.
{ signal: controller.signal, budgetMsForTest: 60_000 }
)
controller.abort()
await expect(pending).resolves.toBe('unknown')
})
it('reports a blown deadline as unverifiable rather than as excess drift', async () => {
mocks.gitExecFileAsync.mockRejectedValue(new Error('git timed out.'))
await expect(
measureRetargetDivergence('/repo', 'refs/heads/main', 'refs/remotes/origin/main')
).resolves.toBe('unknown')
expect(subcommands()).not.toContain('merge-base')
})
it('separates merge-base saying no from merge-base failing', async () => {
mocks.gitExecFileAsync.mockImplementation(async (args: string[]) => {
if (args[0] === 'merge-base') {
throw exitCodeError(1)
}
return { stdout: '2\n' }
})
await expect(
measureRetargetDivergence('/repo', 'refs/heads/main', 'refs/remotes/origin/main')
).resolves.toBe('exceeded')
mocks.gitExecFileAsync.mockReset()
mocks.gitExecFileAsync.mockImplementation(async (args: string[]) => {
if (args[0] === 'merge-base') {
throw new Error('git timed out.')
}
return { stdout: '2\n' }
})
await expect(
measureRetargetDivergence('/repo', 'refs/heads/main', 'refs/remotes/origin/main')
).resolves.toBe('unknown')
})
it('reports drift past the cap without spending a merge-base walk', async () => {
answerProbes('101\n')
await expect(
measureRetargetDivergence('/repo', 'refs/heads/main', 'refs/remotes/origin/main')
).resolves.toBe('exceeded')
expect(subcommands()).not.toContain('merge-base')
})
it('routes every probe to the caller-named WSL distro', async () => {
answerProbes('1\n')
await measureRetargetDivergence('/repo', 'refs/heads/main', 'refs/remotes/origin/main', {
wslDistro: 'Ubuntu'
})
for (const options of callOptions()) {
expect(options).toMatchObject({ cwd: '/repo', wslDistro: 'Ubuntu' })
}
})
})