mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 00:02:56 +00:00
* 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.
166 lines
4.8 KiB
TypeScript
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 })
|
|
}
|
|
)
|
|
})
|