Files
gemini-cli/packages/core/src/utils/errors.test.ts

253 lines
7.0 KiB
TypeScript
Raw Normal View History

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import {
isAuthenticationError,
UnauthorizedError,
toFriendlyError,
BadRequestError,
ForbiddenError,
getErrorMessage,
getErrorType,
FatalAuthenticationError,
FatalCancellationError,
FatalInputError,
FatalSandboxError,
FatalConfigError,
FatalTurnLimitedError,
FatalToolExecutionError,
} from './errors.js';
describe('getErrorMessage', () => {
it('should return plain error message', () => {
expect(getErrorMessage(new Error('plain error'))).toBe('plain error');
});
it('should handle non-Error inputs', () => {
expect(getErrorMessage('string error')).toBe('string error');
expect(getErrorMessage(123)).toBe('123');
});
it('should handle structured HTTP errors via toFriendlyError', () => {
const error = {
response: {
data: {
error: {
code: 400,
message: 'Bad Request Message',
},
},
},
};
expect(getErrorMessage(error)).toBe('Bad Request Message');
});
});
describe('isAuthenticationError', () => {
it('should detect error with code: 401 property (MCP SDK style)', () => {
const error = { code: 401, message: 'Unauthorized' };
expect(isAuthenticationError(error)).toBe(true);
});
it('should detect UnauthorizedError instance', () => {
const error = new UnauthorizedError('Authentication required');
expect(isAuthenticationError(error)).toBe(true);
});
it('should return false for 404 errors', () => {
const error = { code: 404, message: 'Not Found' };
expect(isAuthenticationError(error)).toBe(false);
});
it('should handle null and undefined gracefully', () => {
expect(isAuthenticationError(null)).toBe(false);
expect(isAuthenticationError(undefined)).toBe(false);
});
it('should handle non-error objects', () => {
expect(isAuthenticationError('string error')).toBe(false);
expect(isAuthenticationError(123)).toBe(false);
expect(isAuthenticationError({})).toBe(false);
});
it('should detect 401 in various message formats', () => {
expect(isAuthenticationError(new Error('401 Unauthorized'))).toBe(true);
expect(isAuthenticationError(new Error('HTTP 401'))).toBe(true);
expect(isAuthenticationError(new Error('Status code: 401'))).toBe(true);
});
});
describe('toFriendlyError', () => {
it('should return BadRequestError for 400', () => {
const error = {
response: {
data: {
error: {
code: 400,
message: 'Bad Request',
},
},
},
};
const result = toFriendlyError(error);
expect(result).toBeInstanceOf(BadRequestError);
expect((result as BadRequestError).message).toBe('Bad Request');
});
it('should return UnauthorizedError for 401', () => {
const error = {
response: {
data: {
error: {
code: 401,
message: 'Unauthorized',
},
},
},
};
const result = toFriendlyError(error);
expect(result).toBeInstanceOf(UnauthorizedError);
expect((result as UnauthorizedError).message).toBe('Unauthorized');
});
it('should return ForbiddenError for 403', () => {
const error = {
response: {
data: {
error: {
code: 403,
message: 'Forbidden',
},
},
},
};
const result = toFriendlyError(error);
expect(result).toBeInstanceOf(ForbiddenError);
expect((result as ForbiddenError).message).toBe('Forbidden');
});
it('should parse stringified JSON data', () => {
const error = {
response: {
data: JSON.stringify({
error: {
code: 400,
message: 'Parsed Message',
},
}),
},
};
const result = toFriendlyError(error);
expect(result).toBeInstanceOf(BadRequestError);
expect((result as BadRequestError).message).toBe('Parsed Message');
});
it('should return original error if response data is undefined', () => {
const error = {
response: {
data: undefined,
},
};
expect(toFriendlyError(error)).toBe(error);
});
it('should return original error if error object is missing in data', () => {
const error = {
response: {
data: {
somethingElse: 'value',
},
},
};
expect(toFriendlyError(error)).toBe(error);
});
it('should return original error if error code or message is missing', () => {
const errorNoCode = {
response: {
data: {
error: {
message: 'No Code',
},
},
},
};
expect(toFriendlyError(errorNoCode)).toBe(errorNoCode);
const errorNoMessage = {
response: {
data: {
error: {
code: 400,
},
},
},
};
expect(toFriendlyError(errorNoMessage)).toBe(errorNoMessage);
});
it('should return original error for unknown codes', () => {
const error = {
response: {
data: {
error: {
code: 500,
message: 'Internal Server Error',
},
},
},
};
expect(toFriendlyError(error)).toBe(error);
});
it('should return original error if not a Gaxios error object', () => {
const error = new Error('Regular Error');
expect(toFriendlyError(error)).toBe(error);
});
});
describe('getErrorType', () => {
it('should return error name for standard errors', () => {
expect(getErrorType(new Error('test'))).toBe('Error');
expect(getErrorType(new TypeError('test'))).toBe('TypeError');
expect(getErrorType(new SyntaxError('test'))).toBe('SyntaxError');
});
it('should return constructor name for custom errors', () => {
expect(getErrorType(new FatalAuthenticationError('test'))).toBe(
'FatalAuthenticationError',
);
expect(getErrorType(new FatalInputError('test'))).toBe('FatalInputError');
expect(getErrorType(new FatalSandboxError('test'))).toBe(
'FatalSandboxError',
);
expect(getErrorType(new FatalConfigError('test'))).toBe('FatalConfigError');
expect(getErrorType(new FatalTurnLimitedError('test'))).toBe(
'FatalTurnLimitedError',
);
expect(getErrorType(new FatalToolExecutionError('test'))).toBe(
'FatalToolExecutionError',
);
expect(getErrorType(new FatalCancellationError('test'))).toBe(
'FatalCancellationError',
);
expect(getErrorType(new ForbiddenError('test'))).toBe('ForbiddenError');
expect(getErrorType(new UnauthorizedError('test'))).toBe(
'UnauthorizedError',
);
expect(getErrorType(new BadRequestError('test'))).toBe('BadRequestError');
});
it('should return "unknown" for non-Error objects', () => {
expect(getErrorType('string error')).toBe('unknown');
expect(getErrorType(123)).toBe('unknown');
expect(getErrorType({})).toBe('unknown');
expect(getErrorType(null)).toBe('unknown');
expect(getErrorType(undefined)).toBe('unknown');
});
});