fix(build): retry transient sitemap endpoint fetches

The TypeScript helper refactor exposed a separate production risk: builds can fail when the sitemap-endpoints API returns a transient 5xx during Pagefind index generation. Move that fetch into a small helper with retry logic and add a focused prebuild test so this failure mode is caught without waiting on a full deploy.

Constraint: Must keep Pagefind index generation behavior the same when the endpoint is healthy
Rejected: Ignore the failure as external-only | transient 5xx responses can still block deploys and CI repeatedly
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When external build inputs can fail transiently, add a small retryable helper plus a direct test before relying on full deploy verification
Tested: pnpm exec vitest run test/prebuild/load-sitemap-endpoints.test.ts; pnpm run typecheck; pnpm run test-prebuild
Not-tested: Full production redeploy completion before push
This commit is contained in:
ThatGuySam 2026-04-04 18:18:23 -05:00
parent cd41143f0d
commit f1cb66c477
3 changed files with 159 additions and 21 deletions

View file

@ -0,0 +1,94 @@
import {
beforeEach,
describe,
expect,
it,
vi
} from 'vitest'
import axios from 'axios'
import {
fetchJsonWithRetries,
shouldRetryError
} from '~/helpers/pagefind/load-sitemap-endpoints'
vi.mock( 'axios', () => {
return {
default: {
get: vi.fn()
}
}
} )
describe( 'load sitemap endpoints helper', () => {
beforeEach( () => {
vi.mocked( axios.get ).mockReset()
} )
it( 'retries transient 5xx errors and eventually resolves', async () => {
const axiosGet = vi.mocked( axios.get )
axiosGet
.mockRejectedValueOnce({
response: {
status: 502
}
})
.mockResolvedValueOnce({
data: {
ok: true
}
} )
const data = await fetchJsonWithRetries( 'https://api.doesitarm.com/sitemap-endpoints.json', {
attempts: 2,
delayMs: 0
} )
expect( data ).toEqual({
ok: true
} )
expect( axiosGet ).toHaveBeenCalledTimes( 2 )
} )
it( 'does not retry non-5xx errors', async () => {
const axiosGet = vi.mocked( axios.get )
axiosGet.mockRejectedValueOnce({
response: {
status: 404
}
})
await expect( fetchJsonWithRetries( 'https://api.doesitarm.com/sitemap-endpoints.json', {
attempts: 3,
delayMs: 0
} ) ).rejects.toEqual({
response: {
status: 404
}
})
expect( axiosGet ).toHaveBeenCalledTimes( 1 )
} )
it( 'classifies retryable server errors', () => {
expect( shouldRetryError( {
response: {
status: 502
}
} ) ).toBe( true )
expect( shouldRetryError( {
response: {
status: 503
}
} ) ).toBe( true )
expect( shouldRetryError( {
response: {
status: 404
}
} ) ).toBe( false )
expect( shouldRetryError( new Error( 'network' ) ) ).toBe( false )
} )
} )