mirror of
https://github.com/stablyai/orca.git
synced 2026-11-05 11:37:25 +00:00
215 lines
6.5 KiB
TypeScript
215 lines
6.5 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
resolveCreatePrIntentRemoteStep,
|
|
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('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 })
|
|
}
|
|
)
|
|
})
|
|
|
|
describe('resolveCreatePrIntentRemoteStep', () => {
|
|
it('resolves safe remote steps for publish, push, and patch-equivalent force-push', () => {
|
|
expect(
|
|
resolveCreatePrIntentRemoteStep({
|
|
upstreamStatus: { hasUpstream: false, ahead: 0, behind: 0 },
|
|
branchCommitsAhead: 2,
|
|
hasCurrentBranch: true,
|
|
hostedReviewCreation: { canCreate: false, blockedReason: 'no_upstream' }
|
|
})
|
|
).toBe('publish')
|
|
|
|
expect(
|
|
resolveCreatePrIntentRemoteStep({
|
|
upstreamStatus: { hasUpstream: true, ahead: 2, behind: 0 },
|
|
hasCurrentBranch: true,
|
|
hostedReviewCreation: { canCreate: false, blockedReason: 'needs_push' }
|
|
})
|
|
).toBe('push')
|
|
|
|
expect(
|
|
resolveCreatePrIntentRemoteStep({
|
|
upstreamStatus: {
|
|
hasUpstream: true,
|
|
ahead: 3,
|
|
behind: 2,
|
|
behindCommitsArePatchEquivalent: true
|
|
},
|
|
branchCommitsAhead: 3,
|
|
hasCurrentBranch: true,
|
|
hostedReviewCreation: { canCreate: false, blockedReason: 'needs_sync' }
|
|
})
|
|
).toBe('force_push')
|
|
})
|
|
|
|
it('fast-forwards behind-only branches, blocks diverged and unpublished-without-commits branches', () => {
|
|
// Diverged branches require an explicit stop; automatic merge would reconcile without consent.
|
|
expect(
|
|
resolveCreatePrIntentRemoteStep({
|
|
upstreamStatus: { hasUpstream: true, ahead: 1, behind: 1 },
|
|
hasCurrentBranch: true,
|
|
hostedReviewCreation: { canCreate: false, blockedReason: 'needs_sync' }
|
|
})
|
|
).toBe('blocked')
|
|
|
|
// Behind with no local commits: pure --ff-only (never plain merge sync).
|
|
expect(
|
|
resolveCreatePrIntentRemoteStep({
|
|
upstreamStatus: { hasUpstream: true, ahead: 0, behind: 3 },
|
|
hasCurrentBranch: true,
|
|
hostedReviewCreation: { canCreate: false, blockedReason: 'needs_sync' }
|
|
})
|
|
).toBe('fast_forward')
|
|
|
|
expect(
|
|
resolveCreatePrIntentRemoteStep({
|
|
upstreamStatus: { hasUpstream: false, ahead: 0, behind: 0 },
|
|
branchCommitsAhead: 0,
|
|
hasCurrentBranch: true,
|
|
hostedReviewCreation: { canCreate: false, blockedReason: 'no_upstream' }
|
|
})
|
|
).toBe('blocked')
|
|
})
|
|
})
|