mirror of
https://github.com/stablyai/orca.git
synced 2026-10-17 08:03:14 +00:00
Tighten PR refresh perf accounting
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -3,14 +3,19 @@ request timestamps, and follow-up scheduling against shared module state. */
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { GitHubPRRefreshCandidate, PRInfo } from '../../shared/types'
|
||||
|
||||
const { sendMock, getAllWebContentsMock, getPRForBranchOutcomeMock, getRateLimitMock } = vi.hoisted(
|
||||
() => ({
|
||||
sendMock: vi.fn(),
|
||||
getAllWebContentsMock: vi.fn(),
|
||||
getPRForBranchOutcomeMock: vi.fn(),
|
||||
getRateLimitMock: vi.fn()
|
||||
})
|
||||
)
|
||||
const {
|
||||
sendMock,
|
||||
getAllWebContentsMock,
|
||||
getPRForBranchOutcomeMock,
|
||||
getRateLimitMock,
|
||||
noteRateLimitSpendMock
|
||||
} = vi.hoisted(() => ({
|
||||
sendMock: vi.fn(),
|
||||
getAllWebContentsMock: vi.fn(),
|
||||
getPRForBranchOutcomeMock: vi.fn(),
|
||||
getRateLimitMock: vi.fn(),
|
||||
noteRateLimitSpendMock: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('electron', () => ({
|
||||
webContents: {
|
||||
@@ -24,7 +29,7 @@ vi.mock('./client', () => ({
|
||||
|
||||
vi.mock('./rate-limit', () => ({
|
||||
getRateLimit: getRateLimitMock,
|
||||
noteRateLimitSpend: vi.fn(),
|
||||
noteRateLimitSpend: noteRateLimitSpendMock,
|
||||
rateLimitGuard: vi.fn(() => ({ blocked: false }))
|
||||
}))
|
||||
|
||||
@@ -80,6 +85,7 @@ describe('pr-refresh-coordinator', () => {
|
||||
getAllWebContentsMock.mockReset()
|
||||
getPRForBranchOutcomeMock.mockReset()
|
||||
getRateLimitMock.mockReset()
|
||||
noteRateLimitSpendMock.mockReset()
|
||||
getAllWebContentsMock.mockReturnValue([
|
||||
{
|
||||
id: 1,
|
||||
@@ -770,6 +776,38 @@ describe('pr-refresh-coordinator', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('refreshes empty-branch candidates when a worktree HEAD hint exists', async () => {
|
||||
const { refreshPRNow } = await import('./pr-refresh-coordinator')
|
||||
getPRForBranchOutcomeMock.mockResolvedValueOnce({
|
||||
kind: 'no-pr',
|
||||
fetchedAt: Date.now()
|
||||
})
|
||||
|
||||
await refreshPRNow(makeCandidate({ branch: '', worktreeHead: 'abcdef1' }))
|
||||
|
||||
expect(getPRForBranchOutcomeMock).toHaveBeenCalledWith('/repo', '', null, null, null, {
|
||||
currentHeadOid: 'abcdef1'
|
||||
})
|
||||
})
|
||||
|
||||
it('accounts for the extra core probe on background PR refreshes', async () => {
|
||||
const { reportVisiblePRRefreshCandidates } = await import('./pr-refresh-coordinator')
|
||||
getPRForBranchOutcomeMock.mockResolvedValueOnce({
|
||||
kind: 'no-pr',
|
||||
fetchedAt: Date.now()
|
||||
})
|
||||
|
||||
reportVisiblePRRefreshCandidates([makeCandidate({ worktreeHead: 'abcdef1' })], 1, 1)
|
||||
await vi.runOnlyPendingTimersAsync()
|
||||
await vi.advanceTimersByTimeAsync(10_000)
|
||||
|
||||
expect(noteRateLimitSpendMock.mock.calls.map(([bucket]) => bucket)).toEqual([
|
||||
'core',
|
||||
'core',
|
||||
'graphql'
|
||||
])
|
||||
})
|
||||
|
||||
it('preserves coalesced aliases across visible follow-up refreshes', async () => {
|
||||
const { reportVisiblePRRefreshCandidates } = await import('./pr-refresh-coordinator')
|
||||
getPRForBranchOutcomeMock
|
||||
|
||||
@@ -236,7 +236,12 @@ function validateCandidate(
|
||||
if (candidate.connectionId && candidate.connectionState === 'disconnected') {
|
||||
return 'disconnected'
|
||||
}
|
||||
if (!candidate.branch && typeof candidate.linkedPRNumber !== 'number') {
|
||||
if (
|
||||
!candidate.branch &&
|
||||
typeof candidate.linkedPRNumber !== 'number' &&
|
||||
typeof candidate.fallbackPRNumber !== 'number' &&
|
||||
!candidate.worktreeHead
|
||||
) {
|
||||
return 'fresh'
|
||||
}
|
||||
return null
|
||||
@@ -463,10 +468,9 @@ function isMergeabilityPendingOutcome(outcome: PRRefreshOutcome): boolean {
|
||||
}
|
||||
|
||||
function backgroundRefreshBuckets(): ('core' | 'graphql')[] {
|
||||
// Why: branch refreshes prefer REST but can still fall back to `gh pr list`
|
||||
// when local head-owner metadata is unavailable. Guard both buckets until the
|
||||
// client exposes an exact per-lookup cost plan.
|
||||
return ['core', 'graphql']
|
||||
// Why: branch misses can now spend core on both branch and merge-commit REST
|
||||
// probes; keep the coarse queue budget conservative until lookup cost is exact.
|
||||
return ['core', 'core', 'graphql']
|
||||
}
|
||||
|
||||
function noteBackgroundStart(): void {
|
||||
|
||||
@@ -1712,6 +1712,17 @@ describe('createGitHubSlice.fetchPRForBranch', () => {
|
||||
connectionId: 'ssh-1'
|
||||
}
|
||||
],
|
||||
worktreesByRepo: {
|
||||
'repo-1': [
|
||||
makePRRefreshWorktree({
|
||||
id: 'wt-direct-refresh-head',
|
||||
repoId: 'repo-1',
|
||||
path: `${repoPath}/worktrees/direct-refresh`,
|
||||
branch,
|
||||
head: 'direct-head-oid'
|
||||
})
|
||||
]
|
||||
},
|
||||
prCache: {
|
||||
[`repo-1::${branch}`]: {
|
||||
data: pr,
|
||||
@@ -1726,7 +1737,10 @@ describe('createGitHubSlice.fetchPRForBranch', () => {
|
||||
})
|
||||
|
||||
await expect(
|
||||
store.getState().fetchPRForBranch(repoPath, branch, { force: true })
|
||||
store.getState().fetchPRForBranch(repoPath, branch, {
|
||||
force: true,
|
||||
worktreeId: 'wt-direct-refresh-head'
|
||||
})
|
||||
).resolves.toMatchObject({ number: 44 })
|
||||
expect(mockApi.gh.prForBranch).not.toHaveBeenCalled()
|
||||
expect(mockApi.gh.refreshPRNow).toHaveBeenCalledWith({
|
||||
@@ -1735,6 +1749,7 @@ describe('createGitHubSlice.fetchPRForBranch', () => {
|
||||
repoPath,
|
||||
branch,
|
||||
cacheKey: `ssh:ssh-1::repo-1::${branch}`,
|
||||
worktreeHead: 'direct-head-oid',
|
||||
connectionId: 'ssh-1'
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2957,6 +2957,9 @@ export const createGitHubSlice: StateCreator<AppState, [], [], GitHubSlice> = (s
|
||||
: ({ kind: 'no-pr', fetchedAt: Date.now() } as const)
|
||||
)
|
||||
: await (async () => {
|
||||
const requestWorktree = options?.worktreeId
|
||||
? findWorktreeById(get(), options.worktreeId)
|
||||
: null
|
||||
const candidate: GitHubPRRefreshCandidate = {
|
||||
repoId: repoId ?? '',
|
||||
repoPath,
|
||||
@@ -2964,6 +2967,7 @@ export const createGitHubSlice: StateCreator<AppState, [], [], GitHubSlice> = (s
|
||||
branch,
|
||||
cacheKey,
|
||||
worktreeId: options?.worktreeId,
|
||||
worktreeHead: requestWorktree?.head ?? null,
|
||||
linkedPRNumber,
|
||||
fallbackPRNumber,
|
||||
fallbackPRSource,
|
||||
|
||||
Reference in New Issue
Block a user