UNPKG

@ledgerhq/coin-stellar

Version:
185 lines (163 loc) 6.01 kB
// SPDX-FileCopyrightText: © 2026 LEDGER SAS // SPDX-License-Identifier: Apache-2.0 import { LedgerAPI4xx, LedgerAPI5xx, NetworkDown } from '@ledgerhq/errors' import { NetworkError, NotFoundError } from '@stellar/stellar-sdk' import { messageFromHorizonUnknown, throwHorizonLedgerOrOperationsError, } from './horizonLedgerErrors' describe('messageFromHorizonUnknown', () => { it('returns Horizon documentationSummary when response body is present', () => { const error = Object.assign(new Error('Request failed with status code 400'), { response: { data: { title: 'Transaction Malformed', status: 400, detail: 'Horizon could not decode the transaction envelope in this request.', }, }, }) expect(messageFromHorizonUnknown(error)).toBe( 'Horizon could not decode the transaction envelope in this request.' ) }) it('returns message for Error', () => { expect(messageFromHorizonUnknown(new Error('hello'))).toBe('hello') }) it('returns string as-is', () => { expect(messageFromHorizonUnknown('plain')).toBe('plain') }) it('returns empty string for non-Error non-string', () => { expect(messageFromHorizonUnknown({ code: 1 })).toBe('') expect(messageFromHorizonUnknown(null)).toBe('') expect(messageFromHorizonUnknown(undefined)).toBe('') }) }) describe('throwHorizonLedgerOrOperationsError', () => { const notFound = 'ledger not found' it('throws notFoundMessage for NotFoundError', () => { expect(() => throwHorizonLedgerOrOperationsError(new NotFoundError('gone', {}), notFound) ).toThrow(notFound) }) it('throws notFoundMessage when message contains status code 404', () => { expect(() => throwHorizonLedgerOrOperationsError(new Error('Request failed: status code 404'), notFound) ).toThrow(notFound) }) it('throws notFoundMessage when Horizon problem body has status 404', () => { const error = Object.assign(new Error('Request failed with status code 404'), { response: { data: { title: 'Resource Missing', status: 404, detail: 'The resource at the url requested was not found.', }, }, }) expect(() => throwHorizonLedgerOrOperationsError(error, notFound)).toThrow(notFound) }) it('throws LedgerAPI4xx 429 for too many requests in message', () => { expect(() => throwHorizonLedgerOrOperationsError(new Error('Too Many Requests'), notFound) ).toThrow(LedgerAPI4xx) }) it('throws LedgerAPI4xx 429 when Horizon problem body has status 429', () => { const error = Object.assign(new Error('Request failed with status code 429'), { response: { data: { title: 'Rate Limited', status: 429, detail: 'The request was rate limited.', }, }, }) expect(() => throwHorizonLedgerOrOperationsError(error, notFound)).toThrow( expect.objectContaining({ name: 'LedgerAPI4xx', status: 429 }) ) }) it('throws LedgerAPI4xx for other 4xx status in message', () => { expect(() => throwHorizonLedgerOrOperationsError(new Error('status code 400'), notFound) ).toThrow(LedgerAPI4xx) }) it('throws LedgerAPI4xx when Horizon problem body has status 400', () => { const error = Object.assign(new Error('Request failed with status code 400'), { response: { data: { title: 'Bad Request', status: 400, detail: 'The request you sent was invalid in some way.', }, }, }) expect(() => throwHorizonLedgerOrOperationsError(error, notFound)).toThrow(LedgerAPI4xx) }) test.each([['401'], ['403']])( 'throws LedgerAPI4xx when message contains status code %s', (code) => { expect(() => throwHorizonLedgerOrOperationsError( new Error(`Request failed: status code ${code}`), notFound ) ).toThrow(LedgerAPI4xx) } ) it('throws LedgerAPI5xx for 5xx status in message', () => { expect(() => throwHorizonLedgerOrOperationsError(new Error('status code 503'), notFound) ).toThrow(LedgerAPI5xx) }) it('throws LedgerAPI5xx when Horizon problem body has status 503', () => { const error = Object.assign(new Error('Request failed with status code 503'), { response: { data: { title: 'Internal Server Error', status: 503, detail: 'An error occurred while processing this request.', }, }, }) expect(() => throwHorizonLedgerOrOperationsError(error, notFound)).toThrow(LedgerAPI5xx) }) test.each([['502'], ['500']])( 'throws LedgerAPI5xx when message contains status code %s', (code) => { expect(() => throwHorizonLedgerOrOperationsError(new Error(`upstream status code ${code}`), notFound) ).toThrow(LedgerAPI5xx) } ) it('throws NetworkDown for NetworkError', () => { expect(() => throwHorizonLedgerOrOperationsError(new NetworkError('net', {}), notFound) ).toThrow(NetworkDown) }) test.each([ ['ECONNRESET', 'ECONNRESET on read'], ['ECONNREFUSED', 'connect ECONNREFUSED 127.0.0.1:443'], ['ENOTFOUND', 'getaddrinfo ENOTFOUND horizon.example'], ['EPIPE', 'write EPIPE broken pipe'], ['ETIMEDOUT', 'connect ETIMEDOUT 10.0.0.1:443'], ])('throws NetworkDown when message contains %s', (_label, message) => { expect(() => throwHorizonLedgerOrOperationsError(new Error(message), notFound)).toThrow( NetworkDown ) }) it('throws NetworkDown for undefined is not an object in message', () => { expect(() => throwHorizonLedgerOrOperationsError(new Error('undefined is not an object'), notFound) ).toThrow(NetworkDown) }) it('rethrows unknown errors', () => { const o = { custom: true } try { throwHorizonLedgerOrOperationsError(o, notFound) } catch (e) { expect(e).toBe(o) return } throw new Error('expected throw') }) })