mirror of
https://github.com/stablyai/orca.git
synced 2026-09-21 16:02:20 +00:00
test: synchronize large repository recovery with Retry request (#18999)
This commit is contained in:
@@ -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(() =>
|
||||
|
||||
Reference in New Issue
Block a user