@clduab11/gemini-flow
Version:
Revolutionary AI agent swarm coordination platform with Google Services integration, multimedia processing, and production-ready monitoring. Features 8 Google AI services, quantum computing capabilities, and enterprise-grade security.
369 lines (281 loc) • 11.1 kB
text/typescript
/**
* Google AI Authentication Tests
*
* Test suite for Google AI Studio API key authentication
* providing simple authentication for Gemini CLI parity
*/
import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
import { GoogleAIAuth } from '../../../src/core/google-ai-auth';
import * as fs from 'fs';
import * as path from 'path';
// Mock filesystem operations
jest.mock('fs', () => ({
existsSync: jest.fn(),
readFileSync: jest.fn(),
writeFileSync: jest.fn(),
mkdirSync: jest.fn()
}));
jest.mock('path', () => ({
join: jest.fn(),
resolve: jest.fn(),
dirname: jest.fn()
}));
const mockFs = fs as jest.Mocked<typeof fs>;
const mockPath = path as jest.Mocked<typeof path>;
describe('GoogleAIAuth', () => {
let auth: GoogleAIAuth;
let originalEnv: typeof process.env;
beforeEach(() => {
// Store original environment
originalEnv = { ...process.env };
// Clear environment variables
delete process.env.GEMINI_API_KEY;
delete process.env.GOOGLE_AI_API_KEY;
delete process.env.GOOGLE_API_KEY;
// Reset mocks
jest.clearAllMocks();
// Setup default mock behavior
mockPath.join.mockImplementation((...args) => args.join('/'));
mockPath.resolve.mockImplementation((...args) => args.join('/'));
mockFs.existsSync.mockReturnValue(false);
mockFs.readFileSync.mockReturnValue('');
});
afterEach(() => {
// Restore original environment
process.env = originalEnv;
jest.restoreAllMocks();
});
describe('initialization', () => {
it('should initialize with default options', () => {
auth = new GoogleAIAuth();
expect(auth).toBeDefined();
});
it('should initialize with custom options', () => {
const options = {
apiKey: 'test-key',
configPath: '/custom/config'
};
auth = new GoogleAIAuth(options);
expect(auth).toBeDefined();
});
});
describe('API key detection', () => {
it('should detect API key from GEMINI_API_KEY environment variable', () => {
process.env.GEMINI_API_KEY = 'env-key-gemini';
auth = new GoogleAIAuth();
expect(auth.isAuthenticated()).toBe(true);
expect(auth.getApiKey()).toBe('env-key-gemini');
});
it('should detect API key from GOOGLE_AI_API_KEY environment variable', () => {
process.env.GOOGLE_AI_API_KEY = 'env-key-google-ai';
auth = new GoogleAIAuth();
expect(auth.isAuthenticated()).toBe(true);
expect(auth.getApiKey()).toBe('env-key-google-ai');
});
it('should detect API key from GOOGLE_API_KEY environment variable', () => {
process.env.GOOGLE_API_KEY = 'env-key-google';
auth = new GoogleAIAuth();
expect(auth.isAuthenticated()).toBe(true);
expect(auth.getApiKey()).toBe('env-key-google');
});
it('should prioritize GEMINI_API_KEY over other environment variables', () => {
process.env.GEMINI_API_KEY = 'gemini-key';
process.env.GOOGLE_AI_API_KEY = 'google-ai-key';
process.env.GOOGLE_API_KEY = 'google-key';
auth = new GoogleAIAuth();
expect(auth.getApiKey()).toBe('gemini-key');
});
it('should load API key from constructor option', () => {
auth = new GoogleAIAuth({ apiKey: 'constructor-key' });
expect(auth.isAuthenticated()).toBe(true);
expect(auth.getApiKey()).toBe('constructor-key');
});
it('should prioritize constructor option over environment variables', () => {
process.env.GEMINI_API_KEY = 'env-key';
auth = new GoogleAIAuth({ apiKey: 'constructor-key' });
expect(auth.getApiKey()).toBe('constructor-key');
});
});
describe('config file handling', () => {
it('should load API key from config file', () => {
const configContent = JSON.stringify({
apiKey: 'config-file-key',
model: 'gemini-1.5-flash'
});
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(configContent);
auth = new GoogleAIAuth({ configPath: '/test/config.json' });
expect(auth.isAuthenticated()).toBe(true);
expect(auth.getApiKey()).toBe('config-file-key');
});
it('should handle missing config file gracefully', () => {
mockFs.existsSync.mockReturnValue(false);
auth = new GoogleAIAuth({ configPath: '/nonexistent/config.json' });
expect(auth.isAuthenticated()).toBe(false);
expect(auth.getApiKey()).toBeNull();
});
it('should handle malformed config file', () => {
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue('invalid json');
auth = new GoogleAIAuth({ configPath: '/test/config.json' });
expect(auth.isAuthenticated()).toBe(false);
expect(auth.getApiKey()).toBeNull();
});
it('should use default config path when not specified', () => {
const homeDir = '/home/user';
process.env.HOME = homeDir;
mockPath.join.mockReturnValue(`${homeDir}/.gemini-flow/config.json`);
mockFs.existsSync.mockReturnValue(false);
auth = new GoogleAIAuth();
expect(mockPath.join).toHaveBeenCalledWith(homeDir, '.gemini-flow', 'config.json');
});
});
describe('API key validation', () => {
it('should validate API key format', () => {
// Valid API key format (starts with AIza)
auth = new GoogleAIAuth({ apiKey: 'AIzaSyDummy1234567890abcdefghijklmnop' });
expect(auth.isAuthenticated()).toBe(true);
expect(auth.isValidApiKey()).toBe(true);
});
it('should reject invalid API key format', () => {
// Invalid API key format
auth = new GoogleAIAuth({ apiKey: 'invalid-key' });
expect(auth.isAuthenticated()).toBe(true); // Has key but invalid format
expect(auth.isValidApiKey()).toBe(false);
});
it('should reject empty API key', () => {
auth = new GoogleAIAuth({ apiKey: '' });
expect(auth.isAuthenticated()).toBe(false);
expect(auth.isValidApiKey()).toBe(false);
});
it('should reject null API key', () => {
auth = new GoogleAIAuth();
expect(auth.isAuthenticated()).toBe(false);
expect(auth.isValidApiKey()).toBe(false);
});
});
describe('setApiKey()', () => {
beforeEach(() => {
auth = new GoogleAIAuth();
});
it('should set valid API key', () => {
const validKey = 'AIzaSyDummy1234567890abcdefghijklmnop';
const result = auth.setApiKey(validKey);
expect(result).toBe(true);
expect(auth.getApiKey()).toBe(validKey);
expect(auth.isAuthenticated()).toBe(true);
});
it('should reject invalid API key format', () => {
const invalidKey = 'invalid-key';
const result = auth.setApiKey(invalidKey);
expect(result).toBe(false);
expect(auth.getApiKey()).toBeNull();
expect(auth.isAuthenticated()).toBe(false);
});
it('should clear API key with null', () => {
auth.setApiKey('AIzaSyDummy1234567890abcdefghijklmnop');
expect(auth.isAuthenticated()).toBe(true);
auth.setApiKey(null);
expect(auth.getApiKey()).toBeNull();
expect(auth.isAuthenticated()).toBe(false);
});
});
describe('saveConfig()', () => {
beforeEach(() => {
auth = new GoogleAIAuth();
mockFs.mkdirSync.mockImplementation();
mockFs.writeFileSync.mockImplementation();
});
it('should save config to file', async () => {
const configPath = '/test/.gemini-flow/config.json';
auth.setApiKey('AIzaSyDummy1234567890abcdefghijklmnop');
await auth.saveConfig(configPath);
expect(mockFs.mkdirSync).toHaveBeenCalled();
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
configPath,
expect.stringContaining('AIzaSyDummy1234567890abcdefghijklmnop')
);
});
it('should handle directory creation errors', async () => {
mockFs.mkdirSync.mockImplementation(() => {
throw new Error('Permission denied');
});
auth.setApiKey('AIzaSyDummy1234567890abcdefghijklmnop');
await expect(auth.saveConfig('/test/config.json')).rejects.toThrow('Permission denied');
});
});
describe('loadConfig()', () => {
beforeEach(() => {
auth = new GoogleAIAuth();
});
it('should load config from file', () => {
const configContent = JSON.stringify({
apiKey: 'AIzaSyConfigKey1234567890abcdefghijk',
model: 'gemini-1.5-pro'
});
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(configContent);
const result = auth.loadConfig('/test/config.json');
expect(result).toBe(true);
expect(auth.getApiKey()).toBe('AIzaSyConfigKey1234567890abcdefghijk');
});
it('should return false for missing config file', () => {
mockFs.existsSync.mockReturnValue(false);
const result = auth.loadConfig('/nonexistent/config.json');
expect(result).toBe(false);
expect(auth.isAuthenticated()).toBe(false);
});
});
describe('clearAuth()', () => {
it('should clear authentication', () => {
auth = new GoogleAIAuth({ apiKey: 'AIzaSyDummy1234567890abcdefghijklmnop' });
expect(auth.isAuthenticated()).toBe(true);
auth.clearAuth();
expect(auth.isAuthenticated()).toBe(false);
expect(auth.getApiKey()).toBeNull();
});
});
describe('getAuthStatus()', () => {
it('should return complete auth status', () => {
process.env.GEMINI_API_KEY = 'AIzaSyEnvKey1234567890abcdefghijklmn';
auth = new GoogleAIAuth();
const status = auth.getAuthStatus();
expect(status).toEqual({
isAuthenticated: true,
source: 'environment',
keyFormat: 'valid',
keyPrefix: 'AIzaSy...'
});
});
it('should return unauthenticated status', () => {
auth = new GoogleAIAuth();
const status = auth.getAuthStatus();
expect(status).toEqual({
isAuthenticated: false,
source: 'none',
keyFormat: 'none',
keyPrefix: null
});
});
});
describe('error handling', () => {
it('should handle filesystem errors gracefully', () => {
mockFs.readFileSync.mockImplementation(() => {
throw new Error('File read error');
});
mockFs.existsSync.mockReturnValue(true);
expect(() => {
auth = new GoogleAIAuth({ configPath: '/error/config.json' });
}).not.toThrow();
expect(auth.isAuthenticated()).toBe(false);
});
it('should handle JSON parse errors', () => {
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue('{invalid json}');
expect(() => {
auth = new GoogleAIAuth({ configPath: '/test/config.json' });
}).not.toThrow();
expect(auth.isAuthenticated()).toBe(false);
});
});
});