All checks were successful
continuous-integration/drone/push Build is passing
144 lines
5.2 KiB
TypeScript
144 lines
5.2 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { createErrorResolver } from '../../src/errors/createErrorResolver';
|
|
|
|
const CATALOG = {
|
|
AUTH_UNAUTHORIZED: 'Unauthorized access. Please sign in again.',
|
|
FORBIDDEN: 'You do not have permission to perform this action.',
|
|
USER_NOT_FOUND: 'User not found.',
|
|
INTERNAL_ERROR: 'Unexpected request error.',
|
|
REQUEST_FAILED: 'Request failed. Please try again.',
|
|
};
|
|
|
|
const resolver = createErrorResolver({
|
|
catalog: CATALOG,
|
|
fallbackCode: 'REQUEST_FAILED',
|
|
defaultContext: 'default',
|
|
contextOverrides: {
|
|
session: {
|
|
AUTH_UNAUTHORIZED: 'Session expired. Please sign in again.',
|
|
},
|
|
},
|
|
inferCodeFromStatus: (status?: number | null) => {
|
|
switch (status) {
|
|
case 401:
|
|
return 'AUTH_UNAUTHORIZED';
|
|
case 403:
|
|
return 'FORBIDDEN';
|
|
case 404:
|
|
return 'USER_NOT_FOUND';
|
|
default:
|
|
if (status != null && status >= 500) {
|
|
return 'INTERNAL_ERROR';
|
|
}
|
|
return undefined;
|
|
}
|
|
},
|
|
inferCodeFromLegacyMessage: (message?: string | null) => {
|
|
if (message?.toLowerCase() === 'unauthorized') {
|
|
return 'AUTH_UNAUTHORIZED';
|
|
}
|
|
return undefined;
|
|
},
|
|
});
|
|
|
|
describe('createErrorResolver', () => {
|
|
it('recognizes known error codes', () => {
|
|
expect(resolver.isKnownErrorCode('AUTH_UNAUTHORIZED')).toBe(true);
|
|
expect(resolver.isKnownErrorCode('NOPE')).toBe(false);
|
|
});
|
|
|
|
it('returns context override for known code', () => {
|
|
expect(
|
|
resolver.resolveErrorMessage({
|
|
code: 'AUTH_UNAUTHORIZED',
|
|
context: 'session',
|
|
}),
|
|
).toBe('Session expired. Please sign in again.');
|
|
});
|
|
|
|
it('falls back from unknown code to legacy message inference', () => {
|
|
expect(
|
|
resolver.resolveErrorMessage({
|
|
code: 'UNKNOWN_CODE',
|
|
fallbackMessage: 'unauthorized',
|
|
}),
|
|
).toBe('Unauthorized access. Please sign in again.');
|
|
});
|
|
|
|
it('falls back to status mapping when code is missing', () => {
|
|
expect(resolver.resolveErrorMessage({ status: 404 })).toBe('User not found.');
|
|
});
|
|
|
|
it('falls back to status mapping when legacy inference returns an unmapped code', () => {
|
|
const resolverWithUnmappedLegacyCode = createErrorResolver({
|
|
catalog: CATALOG,
|
|
inferCodeFromStatus: resolver.inferErrorCodeFromStatus,
|
|
inferCodeFromLegacyMessage: () => 'LEGACY_ONLY_CODE',
|
|
});
|
|
|
|
expect(
|
|
resolverWithUnmappedLegacyCode.resolveErrorMessage({
|
|
status: 403,
|
|
fallbackMessage: 'legacy message',
|
|
}),
|
|
).toBe('You do not have permission to perform this action.');
|
|
});
|
|
|
|
it('uses fallbackCode when no code/status resolve and fallback message is missing', () => {
|
|
expect(resolver.resolveErrorMessage({ status: 418 })).toBe('Request failed. Please try again.');
|
|
});
|
|
|
|
it('uses fallback message when no mapping exists and fallback code is unavailable', () => {
|
|
const noFallbackCodeResolver = createErrorResolver({
|
|
catalog: CATALOG,
|
|
inferCodeFromStatus: () => undefined,
|
|
});
|
|
|
|
expect(
|
|
noFallbackCodeResolver.resolveErrorMessage({
|
|
code: 'UNKNOWN',
|
|
fallbackMessage: 'raw backend message',
|
|
}),
|
|
).toBe('raw backend message');
|
|
});
|
|
|
|
it('returns default request failure message when no signal is available', () => {
|
|
const emptyResolver = createErrorResolver({
|
|
catalog: {},
|
|
});
|
|
|
|
expect(emptyResolver.resolveErrorMessage({})).toBe('Request failed. Please try again.');
|
|
});
|
|
|
|
it('resolveOptionalErrorMessage returns undefined for empty inputs', () => {
|
|
expect(resolver.resolveOptionalErrorMessage(undefined)).toBeUndefined();
|
|
expect(resolver.resolveOptionalErrorMessage(null)).toBeUndefined();
|
|
});
|
|
|
|
it('resolveOptionalErrorMessage resolves known codes', () => {
|
|
expect(resolver.resolveOptionalErrorMessage('FORBIDDEN')).toBe(
|
|
'You do not have permission to perform this action.',
|
|
);
|
|
});
|
|
|
|
it('toErrorMessage prefers rawMessage over message and handles unknown values', () => {
|
|
expect(
|
|
resolver.toErrorMessage({
|
|
status: 403,
|
|
rawMessage: 'unauthorized',
|
|
message: 'ignored message',
|
|
}),
|
|
).toBe('Unauthorized access. Please sign in again.');
|
|
|
|
expect(resolver.toErrorMessage('boom')).toBe('Request failed. Please try again.');
|
|
expect(resolver.toErrorMessage(null)).toBe('Request failed. Please try again.');
|
|
});
|
|
|
|
it('exposes inferErrorCodeFromStatus', () => {
|
|
expect(resolver.inferErrorCodeFromStatus(401)).toBe('AUTH_UNAUTHORIZED');
|
|
expect(resolver.inferErrorCodeFromStatus(500)).toBe('INTERNAL_ERROR');
|
|
expect(resolver.inferErrorCodeFromStatus(418)).toBeUndefined();
|
|
expect(resolver.inferErrorCodeFromStatus(null)).toBeUndefined();
|
|
});
|
|
});
|