mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 16:03:17 +00:00
When GitHub's API is unreachable (5xx outage, network, or rate limit), Orca showed no PR data with no explanation, so it read as an Orca bug rather than a GitHub-side problem. - Add a shared classifier (classifyGitHubUnavailable) reused by the main process and the renderer so every surface attributes an outage identically. A live outage returns HTTP 5xx, which the PR-refresh classifier previously had no branch for (fell through to the un-attributed "refresh failed"). - Right-sidebar Checks panel: show GitHub-attributed copy in the error empty-state, plus an inline banner over stale cached PR data so an outage doesn't look like a normal (silently out-of-date) panel. - Tasks/PR-list page: replace the vague "N of M projects failed to load" with a GitHub-attributed banner when the failure is a reachability problem. Copy names GitHub as the source and reassures it isn't an Orca problem, with no status-page link. Stays GitHub-scoped so GitLab/other providers aren't mislabeled.
59 lines
2.6 KiB
TypeScript
59 lines
2.6 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { classifyGitHubUnavailable, isGitHubUnavailableError } from './github-api-availability'
|
|
|
|
describe('classifyGitHubUnavailable', () => {
|
|
it('classifies HTTP 5xx outages as server_error', () => {
|
|
expect(classifyGitHubUnavailable('HTTP 503: Service Unavailable')).toBe('server_error')
|
|
expect(classifyGitHubUnavailable('gh: Command failed: HTTP 502 Bad Gateway')).toBe(
|
|
'server_error'
|
|
)
|
|
expect(classifyGitHubUnavailable('GitHub API error: 500 Internal Server Error')).toBe(
|
|
'server_error'
|
|
)
|
|
expect(classifyGitHubUnavailable('The service is temporarily unavailable')).toBe('server_error')
|
|
})
|
|
|
|
it('classifies transport failures as network', () => {
|
|
for (const message of [
|
|
'request to https://api.github.com failed, reason: getaddrinfo ENOTFOUND api.github.com',
|
|
'dial tcp: lookup api.github.com: no such host',
|
|
'connect ETIMEDOUT 140.82.112.5:443',
|
|
'fetch failed',
|
|
'socket hang up',
|
|
'could not resolve host: api.github.com',
|
|
'connection refused'
|
|
]) {
|
|
expect(classifyGitHubUnavailable(message)).toBe('network')
|
|
}
|
|
})
|
|
|
|
it('classifies rate limiting as rate_limited (even when it carries HTTP 403)', () => {
|
|
expect(classifyGitHubUnavailable('HTTP 403: API rate limit exceeded')).toBe('rate_limited')
|
|
expect(classifyGitHubUnavailable('You have exceeded a secondary rate limit')).toBe(
|
|
'rate_limited'
|
|
)
|
|
expect(classifyGitHubUnavailable('HTTP 429 Too Many Requests')).toBe('rate_limited')
|
|
})
|
|
|
|
it('returns null for non-reachability failures', () => {
|
|
expect(classifyGitHubUnavailable('HTTP 403: Resource not accessible by integration')).toBeNull()
|
|
expect(classifyGitHubUnavailable('HTTP 404: Not Found')).toBeNull()
|
|
expect(classifyGitHubUnavailable('could not resolve to a Repository with the name')).toBeNull()
|
|
expect(classifyGitHubUnavailable('gh auth login required')).toBeNull()
|
|
expect(classifyGitHubUnavailable('')).toBeNull()
|
|
})
|
|
|
|
it('does not misread unrelated 3-digit numbers as a server outage', () => {
|
|
expect(classifyGitHubUnavailable('found 512 pull requests')).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('isGitHubUnavailableError', () => {
|
|
it('detects reachability failures from Error objects and strings', () => {
|
|
expect(isGitHubUnavailableError(new Error('HTTP 503: Service Unavailable'))).toBe(true)
|
|
expect(isGitHubUnavailableError('fetch failed')).toBe(true)
|
|
expect(isGitHubUnavailableError(new Error('HTTP 404: Not Found'))).toBe(false)
|
|
expect(isGitHubUnavailableError(null)).toBe(false)
|
|
})
|
|
})
|