test: synchronize large repository recovery with Retry request (#18999)

This commit is contained in:
Neil
2026-09-05 19:57:29 -07:00
committed by GitHub
parent bedbe5997b
commit 712cf1facb
3 changed files with 114 additions and 5 deletions
@@ -0,0 +1,61 @@
import type { ElectronApplication } from '@stablyai/playwright-test'
type StatusArgs = { worktreePath?: string; admissionTier?: string }
type StatusHandler = (event: unknown, args?: StatusArgs) => unknown
type RetryBarrier = {
captured: boolean
release: () => void
original: StatusHandler
}
type BarrierScope = typeof globalThis & { __gitStatusRetryBarrier?: RetryBarrier }
export async function installGitStatusRetryBarrier(
app: ElectronApplication,
repoPath: string
): Promise<void> {
await app.evaluate(({ ipcMain }, repoPath) => {
const scope = globalThis as BarrierScope
const handlers = (ipcMain as unknown as { _invokeHandlers: Map<string, StatusHandler> })
._invokeHandlers
const original = handlers.get('git:status')
if (!original || scope.__gitStatusRetryBarrier) {
throw new Error('Git status handler unavailable or retry barrier already installed')
}
let release!: () => void
const pending = new Promise<void>((resolve) => {
release = resolve
})
const state: RetryBarrier = { captured: false, release, original }
scope.__gitStatusRetryBarrier = state
handlers.set('git:status', async (event, args) => {
if (
!state.captured &&
args?.worktreePath === repoPath &&
args.admissionTier === 'interactive'
) {
state.captured = true
await pending
}
return original(event, args)
})
}, repoPath)
}
export async function hasCapturedGitStatusRetry(app: ElectronApplication): Promise<boolean> {
return app.evaluate(() => (globalThis as BarrierScope).__gitStatusRetryBarrier?.captured ?? false)
}
export async function restoreGitStatusRetryHandler(app: ElectronApplication): Promise<void> {
await app.evaluate(({ ipcMain }) => {
const scope = globalThis as BarrierScope
const state = scope.__gitStatusRetryBarrier
if (!state) {
return
}
const handlers = (ipcMain as unknown as { _invokeHandlers: Map<string, StatusHandler> })
._invokeHandlers
handlers.set('git:status', state.original)
state.release()
delete scope.__gitStatusRetryBarrier
})
}
@@ -0,0 +1,39 @@
import type { ElectronApplication } from '@stablyai/playwright-test'
import { describe, expect, it, vi } from 'vitest'
import {
hasCapturedGitStatusRetry,
installGitStatusRetryBarrier,
restoreGitStatusRetryHandler
} from './git-status-retry-barrier'
describe('Git status retry barrier', () => {
it('holds the target interactive request and restores the real handler on cleanup', async () => {
const original = vi.fn(async (_event: unknown, args: unknown) => args)
const handlers = new Map([['git:status', original]])
const app = {
evaluate: (callback: (electron: unknown, arg?: unknown) => unknown, arg?: unknown) =>
Promise.resolve(callback({ ipcMain: { _invokeHandlers: handlers } }, arg))
} as unknown as ElectronApplication
await installGitStatusRetryBarrier(app, 'target-repo')
try {
const handler = handlers.get('git:status')!
const background = { worktreePath: 'target-repo', admissionTier: 'background' }
const otherRepo = { worktreePath: 'another-repo', admissionTier: 'interactive' }
await expect(handler({}, background)).resolves.toEqual(background)
await expect(handler({}, otherRepo)).resolves.toEqual(otherRepo)
expect(await hasCapturedGitStatusRetry(app)).toBe(false)
const retry = { worktreePath: 'target-repo', admissionTier: 'interactive' }
const event = {}
const pending = handler(event, retry)
expect(await hasCapturedGitStatusRetry(app)).toBe(true)
expect(original).toHaveBeenCalledTimes(2)
await restoreGitStatusRetryHandler(app)
await expect(pending).resolves.toEqual(retry)
expect(original).toHaveBeenLastCalledWith(event, retry)
expect(handlers.get('git:status')).toBe(original)
} finally {
await restoreGitStatusRetryHandler(app)
}
})
})
@@ -20,6 +20,11 @@
import type { ElectronApplication, Page } from '@stablyai/playwright-test'
import { test, expect } from './helpers/orca-app'
import { waitForSessionReady } from './helpers/store'
import {
hasCapturedGitStatusRetry,
installGitStatusRetryBarrier,
restoreGitStatusRetryHandler
} from './helpers/git-status-retry-barrier'
import {
createLargeFileCountRepo,
removeLargeFileCountRepo,
@@ -434,13 +439,17 @@ test.describe('Source Control large file count (#8013)', () => {
)
expect(hugeState).not.toBeNull()
// Why: watcher refreshes stay parked while huge; the visible Retry is the
// explicit recovery path after the underlying change count drops.
removeLargeFileCountUntrackedTree(fixture.repoPath)
await expect(tooManyChangesBanner).toBeVisible()
const retryButton = tooManyChangesBanner.locator('..').getByRole('button', { name: 'Retry' })
await expect(retryButton).toBeVisible()
await retryButton.click()
// Keep automatic refreshes from removing Retry before its real request starts.
await installGitStatusRetryBarrier(electronApp, fixture.repoPath)
try {
await retryButton.click()
await expect.poll(() => hasCapturedGitStatusRetry(electronApp)).toBe(true)
removeLargeFileCountUntrackedTree(fixture.repoPath)
} finally {
await restoreGitStatusRetryHandler(electronApp)
}
await expect(tooManyChangesBanner).not.toBeVisible()
await expect
.poll(() =>