Files
orca/src/shared/source-control-create-review-intent.test.ts
T
Jinjing 239c027693 Keep Create PR intent running when review lookup is unavailable (#11678)
* Fix Create PR preparation with unavailable lookup

* test(source-control): align dirty+unavailable intent expectation

Create PR preparation is allowed when review lookup is unavailable; only final create stays fail-closed. Update the local-blocker snapshot test to match.

* Keep Create PR intent running when hosted review lookup fails

A failed or timed-out hosted-review eligibility lookup no longer aborts
Create PR intent mid-run. Local prep (stage/commit/push) continues, the
branch-ahead refresh is deferred until after eligibility resolves, and the
final create preflight still fails closed to prevent duplicate reviews.

Also gate generated PR title/body on eligibility and thread the provider
through the intent run token so an unavailable lookup falls back to the
inferred remote host.

* fix(source-control): align dirty+unavailable intent expectation

Local preparation (stage/commit changes) is safe without review-lookup authority; remote actions stay blocked. Prevents dirty trees from dead-ending at sync-first when lookup is unavailable.

* fix(source-control): distinguish loading state from unavailable lookup

Require head branch presence in shouldAttemptCreateHostedReviewForIntent to
separate real unavailable-lookup results from loading placeholders, which
share the same outcome/reason pair but lack a branch name.

* test(activity): drive portal readiness latch release with explicit rAF

Wall-clock setTimeout waits for requestAnimationFrame were flaky under
CI load (shard 15/16), leaving status stuck at loading instead of ready.

* Distinguish expected absence from git errors in remote removal

Why: swallowing all errors silently masks genuine git failures.
Check presence explicitly instead, so setup/teardown can still
skip when origin is absent while letting real errors surface.
2026-07-30 21:45:44 -07:00

166 lines
4.8 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
resolveCreateReviewIntentEligibility,
type CreateReviewIntentKind
} from './source-control-create-review-intent'
import type { HostedReviewCreationBlockedReason } from './hosted-review'
import type { GitUpstreamStatus } from './git-status-types'
function unavailableEligibility(blockedReason: HostedReviewCreationBlockedReason) {
return {
provider: 'github' as const,
review: null,
canCreate: false,
blockedReason,
nextAction: null,
defaultBaseRef: 'main',
reviewLookupOutcome: 'unavailable' as const
}
}
describe('resolveCreateReviewIntentEligibility', () => {
it('rejects unavailable eligibility when the default branch is unknown', () => {
expect(
resolveCreateReviewIntentEligibility({
stagedCount: 1,
hasStageableChanges: true,
hasMessage: true,
hasUnresolvedConflicts: false,
upstreamStatus: { hasUpstream: true, ahead: 0, behind: 0 },
hostedReviewCreation: {
...unavailableEligibility('dirty'),
defaultBaseRef: null
}
})
).toEqual({ eligible: false, kind: null })
})
it('rejects unavailable eligibility when the default branch is blank', () => {
expect(
resolveCreateReviewIntentEligibility({
stagedCount: 1,
hasStageableChanges: true,
hasMessage: true,
hasUnresolvedConflicts: false,
upstreamStatus: { hasUpstream: true, ahead: 0, behind: 0 },
hostedReviewCreation: {
...unavailableEligibility('dirty'),
defaultBaseRef: ' '
}
})
).toEqual({ eligible: false, kind: null })
})
it('stays ineligible when no local blocker remains under unavailable lookup', () => {
expect(
resolveCreateReviewIntentEligibility({
stagedCount: 0,
hasStageableChanges: false,
hasMessage: true,
hasUnresolvedConflicts: false,
upstreamStatus: { hasUpstream: true, ahead: 0, behind: 0 },
hostedReviewCreation: {
...unavailableEligibility('dirty'),
blockedReason: null
}
})
).toEqual({ eligible: false, kind: null })
})
it('keeps dirty local preparation eligible when review lookup is unavailable', () => {
expect(
resolveCreateReviewIntentEligibility({
stagedCount: 0,
hasStageableChanges: true,
hasMessage: true,
hasUnresolvedConflicts: false,
upstreamStatus: { hasUpstream: true, ahead: 0, behind: 0 },
hostedReviewCreation: unavailableEligibility('dirty')
})
).toEqual({ eligible: true, kind: 'dirty' })
})
it('still requires a message before committing staged changes without lookup authority', () => {
expect(
resolveCreateReviewIntentEligibility({
stagedCount: 1,
hasStageableChanges: false,
hasMessage: false,
hasUnresolvedConflicts: false,
upstreamStatus: { hasUpstream: true, ahead: 0, behind: 0 },
hostedReviewCreation: unavailableEligibility('dirty')
})
).toEqual({ eligible: true, kind: 'message_required' })
})
it.each<{
blockedReason: HostedReviewCreationBlockedReason
blockedKind: CreateReviewIntentKind
stagedCount?: number
hasStageableChanges?: boolean
branchCommitsAhead?: number
upstreamStatus?: GitUpstreamStatus
}>([
{
blockedReason: 'no_upstream',
blockedKind: 'no_upstream',
branchCommitsAhead: 1,
upstreamStatus: { hasUpstream: false, ahead: 0, behind: 0 }
},
{
blockedReason: 'needs_push',
blockedKind: 'needs_push',
upstreamStatus: {
hasUpstream: true,
upstreamName: 'origin/feature',
ahead: 1,
behind: 0
}
},
{
blockedReason: 'needs_sync',
blockedKind: 'needs_sync',
upstreamStatus: {
hasUpstream: true,
upstreamName: 'origin/feature',
ahead: 0,
behind: 1
}
},
{
blockedReason: 'needs_sync',
blockedKind: 'force_push',
branchCommitsAhead: 1,
upstreamStatus: {
hasUpstream: true,
upstreamName: 'origin/feature',
ahead: 2,
behind: 1,
behindCommitsArePatchEquivalent: true
}
}
])(
'keeps recoverable $blockedKind preparation eligible when review lookup is unavailable',
({
blockedReason,
blockedKind,
stagedCount = 0,
hasStageableChanges = false,
branchCommitsAhead,
upstreamStatus
}) => {
expect(
resolveCreateReviewIntentEligibility({
stagedCount,
hasStageableChanges,
hasMessage: true,
hasUnresolvedConflicts: false,
upstreamStatus,
hostedReviewCreation: unavailableEligibility(blockedReason),
branchCommitsAhead
})
).toEqual({ eligible: true, kind: blockedKind })
}
)
})