@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
241 lines (199 loc) • 9.03 kB
text/typescript
import { describe, expect, it } from 'vitest';
import { AgentRuntimeErrorType } from '../types/error';
import {
cleanErrorMessage,
extractStatusCodeFromError,
parseGoogleErrorMessage,
} from './googleErrorParser';
describe('googleErrorParser', () => {
describe('cleanErrorMessage', () => {
it('should remove leading asterisk and spaces', () => {
const input = '* API key not valid. Please check your credentials.';
const expected = 'API key not valid. Please check your credentials.';
expect(cleanErrorMessage(input)).toBe(expected);
});
it('should convert escaped newlines to actual newlines', () => {
const input = 'Error occurred\\nPlease try again';
const expected = 'Error occurred Please try again';
expect(cleanErrorMessage(input)).toBe(expected);
});
it('should replace multiple newlines with single space', () => {
const input = 'Line 1\n\n\nLine 2\nLine 3';
const expected = 'Line 1 Line 2 Line 3';
expect(cleanErrorMessage(input)).toBe(expected);
});
it('should trim whitespace', () => {
const input = ' \t Error message \t ';
const expected = 'Error message';
expect(cleanErrorMessage(input)).toBe(expected);
});
it('should handle combined formatting issues', () => {
const input =
'* API key not valid.\\nPlease check your credentials.\\n\\nContact support if needed. ';
const expected =
'API key not valid. Please check your credentials. Contact support if needed.';
expect(cleanErrorMessage(input)).toBe(expected);
});
});
describe('extractStatusCodeFromError', () => {
it('should extract status code and message correctly', () => {
const input = 'Connection failed [503 Service Unavailable] Please try again later';
const result = extractStatusCodeFromError(input);
expect(result.errorDetails).toEqual({
message: 'Please try again later',
statusCode: 503,
statusCodeText: '[503 Service Unavailable]',
});
expect(result.prefix).toBe('Connection failed');
});
it('should handle different status codes', () => {
const input = 'Request failed [401 Unauthorized] Invalid credentials';
const result = extractStatusCodeFromError(input);
expect(result.errorDetails).toEqual({
message: 'Invalid credentials',
statusCode: 401,
statusCodeText: '[401 Unauthorized]',
});
expect(result.prefix).toBe('Request failed');
});
it('should return null for messages without status codes', () => {
const input = 'Simple error message without status code';
const result = extractStatusCodeFromError(input);
expect(result.errorDetails).toBeNull();
expect(result.prefix).toBe('Simple error message without status code');
});
it('should handle empty message after status code', () => {
const input = 'Error [404 Not Found]';
const result = extractStatusCodeFromError(input);
expect(result.errorDetails).toEqual({
message: '',
statusCode: 404,
statusCodeText: '[404 Not Found]',
});
expect(result.prefix).toBe('Error');
});
});
describe('parseGoogleErrorMessage', () => {
it('should handle location not supported error', () => {
const input = 'This location is not supported for Google AI services';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.LocationNotSupportError);
expect(result.error.message).toBe(input);
});
it('should handle status JSON format', () => {
const input =
'got status: UNAVAILABLE. {"error":{"code":503,"message":"Service temporarily unavailable","status":"UNAVAILABLE"}}';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.ProviderBizError);
expect(result.error).toEqual({
code: 503,
message: 'Service temporarily unavailable',
status: 'UNAVAILABLE',
});
});
it('should handle direct JSON parsing', () => {
const input =
'{"error":{"code":400,"message":"* API key not valid. Please pass a valid API key.","status":"INVALID_ARGUMENT"}}';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.InvalidProviderAPIKey);
expect(result.error).toEqual({
code: 400,
message: 'API key not valid. Please pass a valid API key.',
status: 'INVALID_ARGUMENT',
});
});
it('should handle quota limit error', () => {
const input =
'{"error":{"code":429,"message":"Quota limit reached","status":"RESOURCE_EXHAUSTED"}}';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.QuotaLimitReached);
expect(result.error).toEqual({
code: 429,
message: 'Quota limit reached',
status: 'RESOURCE_EXHAUSTED',
});
});
it('should handle nested JSON format', () => {
const input =
'{"error":{"message":"{\\"error\\":{\\"code\\":400,\\"message\\":\\"Invalid request\\"}}"}}';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.ProviderBizError);
expect(result.error).toEqual({
code: 400,
message: 'Invalid request',
status: '',
});
});
it('should handle array format with API_KEY_INVALID', () => {
const input =
'Request failed [{"@type": "type.googleapis.com/google.rpc.ErrorInfo", "reason": "API_KEY_INVALID", "domain": "googleapis.com"}]';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.InvalidProviderAPIKey);
});
it('should handle array format with other errors', () => {
const input =
'Request failed [{"@type": "type.googleapis.com/google.rpc.ErrorInfo", "reason": "QUOTA_EXCEEDED", "domain": "googleapis.com"}]';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.ProviderBizError);
expect(result.error).toEqual([
{
'@type': 'type.googleapis.com/google.rpc.ErrorInfo',
'reason': 'QUOTA_EXCEEDED',
'domain': 'googleapis.com',
},
]);
});
it('should handle status code extraction fallback', () => {
const input = 'Connection failed [503 Service Unavailable] Please try again later';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.ProviderBizError);
expect(result.error).toEqual({
message: 'Please try again later',
statusCode: 503,
statusCodeText: '[503 Service Unavailable]',
});
});
it('should handle complex nested JSON with message cleaning', () => {
const input =
'{"error":{"code":400,"message":"* Request contains invalid parameters\\nPlease check the documentation\\n\\nContact support for help"}}';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.ProviderBizError);
expect(result.error.message).toBe(
'Request contains invalid parameters Please check the documentation Contact support for help',
);
});
it('should return default error for unparseable messages', () => {
const input = 'Some random error message that cannot be parsed';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.ProviderBizError);
expect(result.error.message).toBe(input);
});
it('should handle malformed JSON gracefully', () => {
const input = '{"error":{"code":400,"message":"Invalid JSON{incomplete';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.ProviderBizError);
expect(result.error.message).toBe(input);
});
it('should handle empty error object in JSON', () => {
const input = '{"error":{}}';
const result = parseGoogleErrorMessage(input);
expect(result.errorType).toBe(AgentRuntimeErrorType.ProviderBizError);
expect(result.error).toEqual({
code: null,
message: input,
status: '',
});
});
it('should handle recursion depth limit', () => {
// Create a deeply nested JSON that exceeds the recursion limit
let deeplyNested = '{"error":{"code":400,"message":"Deep error"}}';
for (let i = 0; i < 6; i++) {
deeplyNested = `{"error":{"message":"${deeplyNested.replaceAll('"', '\\"')}"}}`;
}
const result = parseGoogleErrorMessage(deeplyNested);
// Should still return a valid result, but might not reach the deepest level
expect(result.errorType).toBe(AgentRuntimeErrorType.ProviderBizError);
expect(result.error).toBeDefined();
});
});
});