mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(gitea): treat HTTP 404 as no-PR in the hosted-review branch lookup (#10411)
* fix(gitea): treat HTTP 404 as no-PR in throwing hosted-review lookup hostedReview:forBranch always uses the throwing Gitea lookup variant, and requestJsonAtBase threw on any non-OK status. A 404 (PR deleted, repo absent on the forge, or Gitea masking a private repo) rejected the IPC handler and spammed the main-process log on every refresh for every unaffiliated branch. 404 means the resource is absent — an accepted "no PR", not a transport or auth failure. Map it to null even with throwOnFailure; 401/403/5xx/network errors still throw so creation eligibility keeps reporting 'unavailable'. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gitea): scope the 404 no-PR exemption to the linked-PR fetch A blanket status !== 404 exemption in the shared request helper also covered the repo-level /pulls scan, where a 404 means a wrong API base URL, a repo absent from the forge, or a token without scope. Collapsing that to "no PR" would make a misconfigured Gitea host look permanently PR-less instead of surfacing reviewLookupOutcome: 'unavailable'. Gate the exemption on an explicit allowNotFound request option, set only on the single-resource /pulls/{n} fetch. Co-authored-by: Orca <help@stably.ai> * test(gitea): pin the 200-with-empty-list no-PR shape alongside the 404 case Co-authored-by: Orca <help@stably.ai> * fix(gitea): preserve deleted linked pull request absence handling Refresh the original Donach PR and its prior maintainer correction on current main, keeping the HTTP404 exemption limited to the linked pull resource. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
co-authored by
Orca
Claude Fable 5
Neil
parent
39fc4f7688
commit
e4f5e1ddf1
@@ -261,6 +261,44 @@ describe('Gitea client', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('treats a 404 on the linked PR as "no PR" rather than a lookup failure', async () => {
|
||||
const requested: string[] = []
|
||||
const fetchMock = vi.fn(async (url: string | URL) => {
|
||||
requested.push(new URL(String(url)).pathname)
|
||||
// The branch scan succeeds and is simply empty; only PR #42 is gone.
|
||||
return String(url).includes('/pulls/42')
|
||||
? Response.json({ message: 'not found' }, { status: 404 })
|
||||
: Response.json([])
|
||||
})
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
|
||||
await expect(
|
||||
getGiteaPullRequestForBranchOrThrow('/repo', 'feature/gitea', 42)
|
||||
).resolves.toBeNull()
|
||||
expect(requested).toEqual(['/api/v1/repos/team/repo/pulls', '/api/v1/repos/team/repo/pulls/42'])
|
||||
})
|
||||
|
||||
it('resolves null without throwing when the scan returns an empty list', async () => {
|
||||
// Gitea has no head-branch filter, so "branch has no PR" arrives as a 200
|
||||
// with [] from the repo listing — never a 404. The throwing variant must
|
||||
// still report that as a plain "no PR".
|
||||
const fetchMock = vi.fn(async () => Response.json([]))
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
|
||||
await expect(getGiteaPullRequestForBranchOrThrow('/repo', 'feature/gitea')).resolves.toBeNull()
|
||||
})
|
||||
|
||||
it('still throws on a 404 from the /pulls scan so a misconfigured host is not read as "no PR"', async () => {
|
||||
const fetchMock = vi.fn(async () => Response.json({ message: 'not found' }, { status: 404 }))
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
|
||||
// A repo-level 404 means wrong base URL / repo absent / token lacks scope —
|
||||
// collapsing it to "no PR" would hide the misconfiguration indefinitely.
|
||||
await expect(getGiteaPullRequestForBranchOrThrow('/repo', 'feature/gitea')).rejects.toThrow(
|
||||
/Gitea request failed: HTTP 404/
|
||||
)
|
||||
})
|
||||
|
||||
it('expires successful scans and bounds retained repository listings', async () => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
|
||||
@@ -37,6 +37,8 @@ export type GiteaAuthStatus = {
|
||||
type RequestOptions = {
|
||||
searchParams?: Record<string, string | number>
|
||||
timeoutMs?: number
|
||||
// Why: only a single-resource fetch may read 404 as "absent". See requestJsonAtBase.
|
||||
allowNotFound?: boolean
|
||||
}
|
||||
|
||||
function envValue(name: string): string | null {
|
||||
@@ -93,7 +95,8 @@ async function requestJsonAtBase<T>(
|
||||
})
|
||||
if (!response.ok) {
|
||||
await cancelUnreadResponseBody(response)
|
||||
if (throwOnFailure) {
|
||||
// Why: listing 404s still signal repository or access failures.
|
||||
if (throwOnFailure && !(options.allowNotFound && response.status === 404)) {
|
||||
throw new Error(`Gitea request failed: HTTP ${response.status}`)
|
||||
}
|
||||
return null
|
||||
@@ -299,7 +302,7 @@ export async function getGiteaPullRequestForBranch(
|
||||
const raw = await requestJson<RawGiteaPullRequest>(
|
||||
repo,
|
||||
`/repos/${encodedRepoPath(repo)}/pulls/${encodeURIComponent(String(linkedPRNumber))}`,
|
||||
{},
|
||||
{ allowNotFound: true },
|
||||
throwOnFailure
|
||||
)
|
||||
return raw ? normalizePullRequest(repo, raw) : null
|
||||
|
||||
Reference in New Issue
Block a user