Skip to content

src/github-errors.node.test.ts

71 lines · 2.2 KB · TypeScript
Raw
import assert from 'node:assert/strict'
import test from 'node:test'
import {
	githubFailureStatus,
	isTransientGithubFailure,
	withTransientGithubRetry,
} from './github-errors.ts'

function requestError(status: number, message = `GitHub request failed with ${status} `) {
	const error = new Error(message) as Error & { response: { status: number } }
	error.name = 'GitHubRequestError'
	error.response = { status }
	return error
}

test('reads status from GitHubRequestError.response', () => {
	assert.equal(githubFailureStatus(requestError(522)), 522)
	assert.equal(githubFailureStatus(requestError(404)), 404)
})

test('falls back to the github helper message format', () => {
	assert.equal(githubFailureStatus(new Error('GitHub request failed with 503 ')), 503)
	assert.equal(githubFailureStatus(new Error('unrelated failure')), null)
	assert.equal(githubFailureStatus('not-an-error'), null)
})

test('treats Cloudflare and GitHub blips as transient', () => {
	assert.equal(isTransientGithubFailure(requestError(522)), true)
	assert.equal(isTransientGithubFailure(requestError(502)), true)
	assert.equal(isTransientGithubFailure(requestError(429)), true)
	const timeout = new Error('GitHub request timed out: https://api.github.com/repos/o/r/commits')
	timeout.name = 'GitHubRequestTimeoutError'
	assert.equal(isTransientGithubFailure(timeout), true)
})

test('does not treat caller or not-found failures as transient', () => {
	assert.equal(isTransientGithubFailure(requestError(404)), false)
	assert.equal(isTransientGithubFailure(requestError(401)), false)
	assert.equal(isTransientGithubFailure(requestError(403)), false)
	assert.equal(isTransientGithubFailure(new Error('boom')), false)
})

test('retries a transient failure once, then succeeds', async () => {
	let calls = 0
	const result = await withTransientGithubRetry(
		async () => {
			calls += 1
			if (calls === 1) throw requestError(522)
			return 'ok'
		},
		{ delayMs: 0 },
	)
	assert.equal(result, 'ok')
	assert.equal(calls, 2)
})

test('does not retry a non-transient failure', async () => {
	let calls = 0
	await assert.rejects(
		() =>
			withTransientGithubRetry(
				async () => {
					calls += 1
					throw requestError(404)
				},
				{ delayMs: 0 },
			),
		/GitHub request failed with 404/,
	)
	assert.equal(calls, 1)
})