- Fix os module mock pattern for Vitest v4 in proxy-ide-config tests - Fix enterprise oauth_client_key expectations in cloudHandler-sync and token-manager-parity tests - Fix proxy-controller integration test argument expectations - Add missing proxy-parity JSON fixtures (OpenAI tools, Anthropic tool-use, Gemini SSE) - Add reasoning_content mapping to convertClaudeToOpenAIResponse - Fix google-api service test env var cache leak with resetModules + dynamic imports - Fix proxy-metrics timer tests by calling onModuleInit in beforeEach - Fix locale-sensitive number formatting in StatCard tests - Add missing test files to version control (components, hooks, ipc, mocks, server, utils) - Fix CI workflows: testing.yaml (use vitest run, add type-check job, npm cache), lint.yaml and format.yaml (add npm cache, concurrency, fix job names)
160 lines
5.4 KiB
TypeScript
160 lines
5.4 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
import { GoogleAPIService, UserInfoSchema } from '@/services/GoogleAPIService';
|
|
|
|
vi.mock('@/ipc/config/manager', () => ({
|
|
ConfigManager: {
|
|
loadConfig: vi.fn().mockReturnValue({}),
|
|
},
|
|
}));
|
|
|
|
vi.mock('@/ipc/cloud/authServer', () => ({
|
|
AuthServer: {
|
|
getRedirectUri: vi.fn().mockReturnValue('http://localhost:3000/auth/callback'),
|
|
},
|
|
}));
|
|
|
|
vi.mock('@/utils/logger', () => ({
|
|
logger: {
|
|
info: vi.fn(),
|
|
warn: vi.fn(),
|
|
error: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock('@/server/modules/proxy/request-user-agent', () => ({
|
|
buildUserAgent: vi.fn().mockReturnValue('GeminiNexus/1.0'),
|
|
FALLBACK_VERSION: '1.0.0',
|
|
resolveLocalInstalledVersion: vi.fn().mockReturnValue('1.0.0'),
|
|
}));
|
|
|
|
describe('GoogleAPIService', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
delete process.env.ANTIGRAVITY_OAUTH_CLIENTS;
|
|
delete process.env.ANTIGRAVITY_OAUTH_CLIENT_KEY;
|
|
});
|
|
|
|
afterEach(() => {
|
|
delete process.env.ANTIGRAVITY_OAUTH_CLIENTS;
|
|
delete process.env.ANTIGRAVITY_OAUTH_CLIENT_KEY;
|
|
});
|
|
|
|
describe('OAuth client registry', () => {
|
|
beforeEach(() => {
|
|
vi.resetModules();
|
|
delete process.env.ANTIGRAVITY_OAUTH_CLIENTS;
|
|
delete process.env.ANTIGRAVITY_OAUTH_CLIENT_KEY;
|
|
});
|
|
|
|
it('should list built-in OAuth clients', async () => {
|
|
const { GoogleAPIService } = await import('@/services/GoogleAPIService');
|
|
const clients = GoogleAPIService.listOAuthClients();
|
|
expect(clients.length).toBeGreaterThanOrEqual(1);
|
|
expect(clients[0]).toMatchObject({
|
|
key: expect.any(String),
|
|
label: expect.any(String),
|
|
client_id: expect.any(String),
|
|
is_active: expect.any(Boolean),
|
|
is_builtin: true,
|
|
});
|
|
});
|
|
|
|
it('should parse extra OAuth clients from environment', async () => {
|
|
process.env.ANTIGRAVITY_OAUTH_CLIENTS = 'custom1|id1|secret1|Custom One;custom2|id2|secret2';
|
|
const { GoogleAPIService } = await import('@/services/GoogleAPIService');
|
|
const clients = GoogleAPIService.listOAuthClients();
|
|
|
|
const custom1 = clients.find((c) => c.key === 'custom1');
|
|
expect(custom1).toBeDefined();
|
|
expect(custom1?.label).toBe('Custom One');
|
|
expect(custom1?.is_builtin).toBe(false);
|
|
});
|
|
|
|
it('should skip invalid OAuth client entries', async () => {
|
|
process.env.ANTIGRAVITY_OAUTH_CLIENTS = 'invalid|no_secret;valid|id|secret|Label';
|
|
const { GoogleAPIService } = await import('@/services/GoogleAPIService');
|
|
const clients = GoogleAPIService.listOAuthClients();
|
|
|
|
expect(clients.some((c) => c.key === 'valid')).toBe(true);
|
|
expect(clients.some((c) => c.key === 'invalid')).toBe(false);
|
|
});
|
|
|
|
it('should allow setting active OAuth client key', async () => {
|
|
const { GoogleAPIService } = await import('@/services/GoogleAPIService');
|
|
GoogleAPIService.setActiveOAuthClientKey('gemininexus_enterprise');
|
|
expect(GoogleAPIService.getActiveOAuthClientKey()).toBe('gemininexus_enterprise');
|
|
});
|
|
|
|
it('should throw for unknown OAuth client key', async () => {
|
|
const { GoogleAPIService } = await import('@/services/GoogleAPIService');
|
|
expect(() => GoogleAPIService.setActiveOAuthClientKey('nonexistent')).toThrow(
|
|
"Unknown OAuth client key 'nonexistent'",
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('getAuthUrl', () => {
|
|
it('should generate an OAuth authorization URL', () => {
|
|
const url = GoogleAPIService.getAuthUrl();
|
|
expect(url).toContain('accounts.google.com/o/oauth2/v2/auth');
|
|
expect(url).toContain('response_type=code');
|
|
expect(url).toContain('access_type=offline');
|
|
expect(url).toContain('prompt=consent');
|
|
});
|
|
|
|
it('should include requested scopes', () => {
|
|
const url = GoogleAPIService.getAuthUrl();
|
|
expect(url).toContain(encodeURIComponent('https://www.googleapis.com/auth/cloud-platform'));
|
|
expect(url).toContain(encodeURIComponent('https://www.googleapis.com/auth/userinfo.email'));
|
|
});
|
|
});
|
|
|
|
describe('UserInfoSchema', () => {
|
|
it('should validate valid user info', () => {
|
|
const data = {
|
|
id: '123',
|
|
email: '[email protected]',
|
|
verified_email: true,
|
|
name: 'Test User',
|
|
};
|
|
expect(UserInfoSchema.parse(data)).toEqual(data);
|
|
});
|
|
|
|
it('should default verified_email to false', () => {
|
|
const data = { id: '123', email: '[email protected]' };
|
|
const parsed = UserInfoSchema.parse(data);
|
|
expect(parsed.verified_email).toBe(false);
|
|
});
|
|
|
|
it('should reject missing id', () => {
|
|
expect(() => UserInfoSchema.parse({ email: '[email protected]' })).toThrow();
|
|
});
|
|
});
|
|
|
|
describe('normalizeRefreshedOAuthClientKey', () => {
|
|
it('should return normalized key when present', () => {
|
|
const result = GoogleAPIService.normalizeRefreshedOAuthClientKey(
|
|
{ oauth_client_key: 'MyKey' },
|
|
'OtherKey',
|
|
);
|
|
expect(result).toBe('otherkey');
|
|
});
|
|
|
|
it('should fallback to current token key', () => {
|
|
const result = GoogleAPIService.normalizeRefreshedOAuthClientKey(
|
|
{ oauth_client_key: 'MyKey' },
|
|
undefined,
|
|
);
|
|
expect(result).toBe('mykey');
|
|
});
|
|
|
|
it('should unset enterprise key for legacy accounts without project_id', () => {
|
|
const result = GoogleAPIService.normalizeRefreshedOAuthClientKey(
|
|
{},
|
|
'gemininexus_enterprise',
|
|
);
|
|
expect(result).toBe('gemininexus_enterprise');
|
|
});
|
|
});
|
|
});
|