Something went wrong. Try again.
Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations. pdsmoover.com
pds atproto migrations moo cow
Something went wrong. Try again.
3.1 kB · 79 lines
TypeScript
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980import { XrpcResponseError } from '@atproto/lex'import { com } from '@pds-moover/lexicons'import { BlobRateLimitError } from '@pds-moover/moover'import { beforeEach, describe, expect, it, vi } from 'vitest'
const captureException = vi.fn()vi.mock('@sentry/sveltekit', () => ({ captureException: (...args: unknown[]) => captureException(...args),}))
const { isExpectedError, reportError } = await import('./sentryReport')
/** A real XrpcResponseError, because the filter narrows with `instanceof`. */function xrpcError(status: number, error: string) { return new XrpcResponseError( com.atproto.server.describeServer.main, new Response(null, { status }), { encoding: 'application/json', body: { error, message: error }, } as never, )}
describe('isExpectedError', () => { it('treats user-facing conditions as expected', () => { expect(isExpectedError(xrpcError(401, 'AuthFactorTokenRequired'))).toBe(true) expect(isExpectedError(xrpcError(401, 'AuthenticationRequired'))).toBe(true) expect(isExpectedError(xrpcError(400, 'InvalidToken'))).toBe(true) expect(isExpectedError(xrpcError(400, 'ExpiredToken'))).toBe(true) expect(isExpectedError(xrpcError(400, 'InvalidInviteCode'))).toBe(true) expect(isExpectedError(xrpcError(400, 'HandleNotAvailable'))).toBe(true) expect(isExpectedError(xrpcError(429, 'RateLimitExceeded'))).toBe(true) expect(isExpectedError(new BlobRateLimitError({ limit: 1, remaining: 0 } as never, 3, 7))).toBe( true, ) const aborted = new Error('The operation was aborted') aborted.name = 'AbortError' expect(isExpectedError(aborted)).toBe(true) })
it('treats everything else as unexpected', () => { expect(isExpectedError(new Error('boom'))).toBe(false) expect(isExpectedError(new TypeError('Failed to fetch'))).toBe(false) expect(isExpectedError(xrpcError(500, 'InternalServerError'))).toBe(false) expect(isExpectedError(xrpcError(400, 'RepoNotFound'))).toBe(false) expect(isExpectedError('a string')).toBe(false) expect(isExpectedError(undefined)).toBe(false) })})
describe('reportError', () => { beforeEach(() => captureException.mockReset())
it('skips expected errors', () => { expect(reportError(xrpcError(401, 'AuthFactorTokenRequired'), 'login')).toBe(false) expect(captureException).not.toHaveBeenCalled() })
it('captures unexpected errors tagged with the op and the xrpc code', () => { const error = xrpcError(500, 'InternalServerError') expect(reportError(error, 'migration.run', { extra: { step: 'blobs' } })).toBe(true) expect(captureException).toHaveBeenCalledWith(error, { level: 'error', tags: { op: 'migration.run' }, extra: { step: 'blobs', xrpc_error: 'InternalServerError', xrpc_status: 500 }, }) })
it('passes the level through for best-effort work', () => { const error = new Error('resolver down') reportError(error, 'handle.check', { level: 'warning' }) expect(captureException).toHaveBeenCalledWith(error, { level: 'warning', tags: { op: 'handle.check' }, extra: {}, }) })})