mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-18 10:01:29 -07:00
206 lines
6.4 KiB
TypeScript
206 lines
6.4 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
import {
|
|
classifyGoogleError,
|
|
RetryableQuotaError,
|
|
TerminalQuotaError,
|
|
} from './googleQuotaErrors.js';
|
|
import * as errorParser from './googleErrors.js';
|
|
import type { GoogleApiError } from './googleErrors.js';
|
|
|
|
describe('classifyGoogleError', () => {
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('should return original error if not a Google API error', () => {
|
|
const regularError = new Error('Something went wrong');
|
|
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(null);
|
|
const result = classifyGoogleError(regularError);
|
|
expect(result).toBe(regularError);
|
|
});
|
|
|
|
it('should return original error if code is not 429', () => {
|
|
const apiError: GoogleApiError = {
|
|
code: 500,
|
|
message: 'Server error',
|
|
details: [],
|
|
};
|
|
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
const originalError = new Error();
|
|
const result = classifyGoogleError(originalError);
|
|
expect(result).toBe(originalError);
|
|
expect(result).not.toBeInstanceOf(TerminalQuotaError);
|
|
expect(result).not.toBeInstanceOf(RetryableQuotaError);
|
|
});
|
|
|
|
it('should return TerminalQuotaError for daily quota violations in QuotaFailure', () => {
|
|
const apiError: GoogleApiError = {
|
|
code: 429,
|
|
message: 'Quota exceeded',
|
|
details: [
|
|
{
|
|
'@type': 'type.googleapis.com/google.rpc.QuotaFailure',
|
|
violations: [
|
|
{
|
|
subject: 'user',
|
|
description: 'daily limit',
|
|
quotaId: 'RequestsPerDay-limit',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
const result = classifyGoogleError(new Error());
|
|
expect(result).toBeInstanceOf(TerminalQuotaError);
|
|
expect((result as TerminalQuotaError).cause).toBe(apiError);
|
|
});
|
|
|
|
it('should return TerminalQuotaError for daily quota violations in ErrorInfo', () => {
|
|
const apiError: GoogleApiError = {
|
|
code: 429,
|
|
message: 'Quota exceeded',
|
|
details: [
|
|
{
|
|
'@type': 'type.googleapis.com/google.rpc.ErrorInfo',
|
|
reason: 'QUOTA_EXCEEDED',
|
|
domain: 'googleapis.com',
|
|
metadata: {
|
|
quota_limit: 'RequestsPerDay_PerProject_PerUser',
|
|
},
|
|
},
|
|
],
|
|
};
|
|
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
const result = classifyGoogleError(new Error());
|
|
expect(result).toBeInstanceOf(TerminalQuotaError);
|
|
});
|
|
|
|
it('should return TerminalQuotaError for long retry delays', () => {
|
|
const apiError: GoogleApiError = {
|
|
code: 429,
|
|
message: 'Too many requests',
|
|
details: [
|
|
{
|
|
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
retryDelay: '301s', // > 5 minutes
|
|
},
|
|
],
|
|
};
|
|
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
const result = classifyGoogleError(new Error());
|
|
expect(result).toBeInstanceOf(TerminalQuotaError);
|
|
});
|
|
|
|
it('should return RetryableQuotaError for short retry delays', () => {
|
|
const apiError: GoogleApiError = {
|
|
code: 429,
|
|
message: 'Too many requests',
|
|
details: [
|
|
{
|
|
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
retryDelay: '45.123s',
|
|
},
|
|
],
|
|
};
|
|
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
const result = classifyGoogleError(new Error());
|
|
expect(result).toBeInstanceOf(RetryableQuotaError);
|
|
expect((result as RetryableQuotaError).retryDelayMs).toBe(45123);
|
|
});
|
|
|
|
it('should return RetryableQuotaError for per-minute quota violations in QuotaFailure', () => {
|
|
const apiError: GoogleApiError = {
|
|
code: 429,
|
|
message: 'Quota exceeded',
|
|
details: [
|
|
{
|
|
'@type': 'type.googleapis.com/google.rpc.QuotaFailure',
|
|
violations: [
|
|
{
|
|
subject: 'user',
|
|
description: 'per minute limit',
|
|
quotaId: 'RequestsPerMinute-limit',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
const result = classifyGoogleError(new Error());
|
|
expect(result).toBeInstanceOf(RetryableQuotaError);
|
|
expect((result as RetryableQuotaError).retryDelayMs).toBe(60000);
|
|
});
|
|
|
|
it('should return RetryableQuotaError for per-minute quota violations in ErrorInfo', () => {
|
|
const apiError: GoogleApiError = {
|
|
code: 429,
|
|
message: 'Quota exceeded',
|
|
details: [
|
|
{
|
|
'@type': 'type.googleapis.com/google.rpc.ErrorInfo',
|
|
reason: 'QUOTA_EXCEEDED',
|
|
domain: 'googleapis.com',
|
|
metadata: {
|
|
quota_limit: 'RequestsPerMinute_PerProject_PerUser',
|
|
},
|
|
},
|
|
],
|
|
};
|
|
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
const result = classifyGoogleError(new Error());
|
|
expect(result).toBeInstanceOf(RetryableQuotaError);
|
|
expect((result as RetryableQuotaError).retryDelayMs).toBe(60000);
|
|
});
|
|
|
|
it('should prioritize daily limit over retry info', () => {
|
|
const apiError: GoogleApiError = {
|
|
code: 429,
|
|
message: 'Quota exceeded',
|
|
details: [
|
|
{
|
|
'@type': 'type.googleapis.com/google.rpc.QuotaFailure',
|
|
violations: [
|
|
{
|
|
subject: 'user',
|
|
description: 'daily limit',
|
|
quotaId: 'RequestsPerDay-limit',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
retryDelay: '10s',
|
|
},
|
|
],
|
|
};
|
|
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
const result = classifyGoogleError(new Error());
|
|
expect(result).toBeInstanceOf(TerminalQuotaError);
|
|
});
|
|
|
|
it('should return original error for 429 without specific details', () => {
|
|
const apiError: GoogleApiError = {
|
|
code: 429,
|
|
message: 'Too many requests',
|
|
details: [
|
|
{
|
|
'@type': 'type.googleapis.com/google.rpc.DebugInfo',
|
|
detail: 'some debug info',
|
|
stackEntries: [],
|
|
},
|
|
],
|
|
};
|
|
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
const originalError = new Error();
|
|
const result = classifyGoogleError(originalError);
|
|
expect(result).toBe(originalError);
|
|
});
|
|
});
|