UNPKG

@eleva-io/erp-sdk

Version:

SDK oficial para el ERP de Eleva

203 lines 10.5 kB
import { ElevaError, ERROR_CODES, ErrorKind } from '.'; import { RemoteElevaError } from './errors_remote'; const request = { operation: 'GET', metadata: { authorization: 'Bearer token' }, resource: '/api/users/1', payload: undefined, }; const response = { statusCode: 404, metadata: { 'content-type': 'application/json' }, payload: { code: 'NOT_FOUND', message: 'Not found', kind: 'domain', status: 404 }, }; const validBody = { code: ERROR_CODES.NOT_FOUND, kind: ErrorKind.DOMAIN, status: 404, message: 'User not found', details: { entity: 'User' }, }; describe('RemoteElevaError', () => { describe('constructor', () => { it('is an instance of Error, ElevaError and RemoteElevaError', () => { const error = RemoteElevaError.from({ code: ERROR_CODES.INTERNAL_SERVER_ERROR }, request, response); expect(error).toBeInstanceOf(Error); expect(error).toBeInstanceOf(ElevaError); expect(error).toBeInstanceOf(RemoteElevaError); }); it('sets name to "RemoteElevaError"', () => { const error = RemoteElevaError.from({ code: ERROR_CODES.INTERNAL_SERVER_ERROR }, request, response); expect(error.name).toBe('RemoteElevaError'); }); it('exposes request and response as readonly properties', () => { const error = RemoteElevaError.from({ code: ERROR_CODES.INTERNAL_SERVER_ERROR }, request, response); expect(error.request).toBe(request); expect(error.response).toBe(response); }); it('inherits code, kind, status and message from the catalog', () => { const error = RemoteElevaError.from({ code: ERROR_CODES.NOT_FOUND, }, request, response); expect(error.code).toBe(ERROR_CODES.NOT_FOUND); expect(error.kind).toBe(ErrorKind.DOMAIN); expect(error.status).toBe(404); }); it('forwards cause to the underlying Error', () => { const cause = new Error('network timeout'); const error = RemoteElevaError.from({ code: ERROR_CODES.INTERNAL_SERVER_ERROR }, request, response, cause); expect(error.cause).toBe(cause); }); }); describe('is()', () => { it('returns true when the code matches', () => { const error = RemoteElevaError.from({ code: ERROR_CODES.NOT_FOUND, }, request, response); expect(error.is(ERROR_CODES.NOT_FOUND)).toBe(true); }); it('returns false when the code does not match', () => { const error = RemoteElevaError.from({ code: ERROR_CODES.NOT_FOUND, }, request, response); expect(error.is(ERROR_CODES.CONFLICT)).toBe(false); }); it('narrows details to the matching code', () => { const err = RemoteElevaError.from({ code: ERROR_CODES.NOT_FOUND, details: { entity: 'User' }, }, request, response); const entity = err.is(ERROR_CODES.NOT_FOUND) ? err.details.entity : null; expect(entity).toBe('User'); }); it('preserves request and response on the narrowed type', () => { const err = RemoteElevaError.from({ code: ERROR_CODES.NOT_FOUND, details: { entity: 'User' }, }, request, response); expect(err.is(ERROR_CODES.NOT_FOUND)).toBe(true); expect(err.request).toBe(request); expect(err.response).toBe(response); expect(err.details).toEqual({ entity: 'User' }); }); it('narrows correctly on a union-typed error', () => { const err = RemoteElevaError.from(validBody, request, response); const entity = err.is(ERROR_CODES.NOT_FOUND) ? err.details.entity : null; expect(entity).toBe('User'); }); it('returns true when a plain string matches the code', () => { const error = RemoteElevaError.from({ code: ERROR_CODES.NOT_FOUND }, request, response); expect(error.is('NOT_FOUND')).toBe(true); }); it('returns false when a plain string does not match the code', () => { const error = RemoteElevaError.from({ code: ERROR_CODES.NOT_FOUND }, request, response); expect(error.is('CONFLICT')).toBe(false); }); }); describe('toJSON()', () => { it('includes all ElevaError fields plus request and response', () => { const error = RemoteElevaError.from({ code: ERROR_CODES.NOT_FOUND, details: { entity: 'User' }, }, request, response); const json = error.toJSON(); expect(json.code).toBe(ERROR_CODES.NOT_FOUND); expect(json.kind).toBe(ErrorKind.DOMAIN); expect(json.status).toBe(404); expect(json.details).toEqual({ entity: 'User' }); expect(json.request).toBe(request); expect(json.response).toBe(response); }); it('toString serializes request and response', () => { const error = RemoteElevaError.from({ code: ERROR_CODES.INTERNAL_SERVER_ERROR }, request, response); const parsed = JSON.parse(error.toString()); expect(parsed.request.resource).toBe('/api/users/1'); expect(parsed.response.statusCode).toBe(404); }); }); describe('from()', () => { it('constructs a RemoteElevaError instance (not just ElevaError)', () => { const error = RemoteElevaError.from(validBody, request, response); expect(error).toBeInstanceOf(RemoteElevaError); expect(error).toBeInstanceOf(ElevaError); expect(error.name).toBe('RemoteElevaError'); }); it('parses the body and sets code, message and details correctly', () => { const error = RemoteElevaError.from(validBody, request, response); expect(error.code).toBe(ERROR_CODES.NOT_FOUND); expect(error.message).toBe('User not found'); }); it('attaches request and response context', () => { const error = RemoteElevaError.from(validBody, request, response); expect(error.request).toBe(request); expect(error.response).toBe(response); }); it('uses the provided cause', () => { const cause = new Error('axios error'); const error = RemoteElevaError.from(validBody, request, response, cause); expect(error.cause).toBe(cause); }); it('falls back to INTERNAL_SERVER_ERROR when body is malformed', () => { const error = RemoteElevaError.from({ unexpected: true }, request, response); expect(error.code).toBe(ERROR_CODES.INTERNAL_SERVER_ERROR); expect(error.message).toMatch(/^Failed to parse error payload:/); }); it('uses the raw body as cause when body is malformed and no cause is provided', () => { const body = 'not a valid error'; const error = RemoteElevaError.from(body, request, response); expect(error.cause).toBe(body); }); it('prefers the provided cause over the parsed cause on malformed body', () => { const axiosCause = new Error('axios'); const error = RemoteElevaError.from('bad body', request, response, axiosCause); expect(error.cause).toBe(axiosCause); }); it('toJSON() includes request and response after from()', () => { const error = RemoteElevaError.from(validBody, request, response); const json = error.toJSON(); expect(json.request).toBe(request); expect(json.response).toBe(response); }); }); describe('custom / vendor-specific error codes', () => { it('preserves an unknown code string verbatim', () => { const error = RemoteElevaError.from({ code: 'RATE_LIMIT', status: 429, message: 'Too many requests', kind: 'domain' }, request, response); expect(error.code).toBe('RATE_LIMIT'); }); it('is() returns true for the custom code string', () => { const error = RemoteElevaError.from({ code: 'RATE_LIMIT', status: 429, message: 'Too many requests', kind: 'domain' }, request, response); expect(error.is('RATE_LIMIT')).toBe(true); }); it('is() returns false for a different custom code string', () => { const error = RemoteElevaError.from({ code: 'RATE_LIMIT', status: 429, message: 'Too many requests', kind: 'domain' }, request, response); expect(error.is('QUOTA_EXCEEDED')).toBe(false); }); it('uses status and kind from the body when both are valid', () => { const error = RemoteElevaError.from({ code: 'RATE_LIMIT', status: 429, message: 'Too many requests', kind: 'domain' }, request, response); expect(error.status).toBe(429); expect(error.kind).toBe(ErrorKind.DOMAIN); }); it('falls back to INTERNAL_SERVER_ERROR status and kind when they are absent', () => { const error = RemoteElevaError.from({ code: 'UNKNOWN_VENDOR_CODE', message: 'Something broke' }, request, response); expect(error.code).toBe('UNKNOWN_VENDOR_CODE'); expect(error.status).toBe(500); expect(error.kind).toBe(ErrorKind.SYSTEM); }); it('preserves the message from the body', () => { const error = RemoteElevaError.from({ code: 'RATE_LIMIT', status: 429, message: 'Retry after 60 s', kind: 'domain' }, request, response); expect(error.message).toBe('Retry after 60 s'); }); it('preserves details from the body', () => { const error = RemoteElevaError.from({ code: 'RATE_LIMIT', status: 429, kind: 'domain', details: { retryAfter: 60 } }, request, response); expect(error.details.retryAfter).toBe(60); }); it('toJSON() includes the custom code, request and response', () => { const error = RemoteElevaError.from({ code: 'RATE_LIMIT', status: 429, message: 'Too many requests', kind: 'domain' }, request, response); const json = error.toJSON(); expect(json.code).toBe('RATE_LIMIT'); expect(json.request).toBe(request); expect(json.response).toBe(response); }); }); }); //# sourceMappingURL=errors_remote.spec.js.map