Skip to main content
Glama
gemini-cli.test.js7.55 kB
import { jest } from '@jest/globals'; // Mock the gemini-cli SDK module jest.unstable_mockModule('ai-sdk-provider-gemini-cli', () => ({ createGeminiProvider: jest.fn((options) => { const provider = (modelId, settings) => ({ // Mock language model id: modelId, settings, authOptions: options }); provider.languageModel = jest.fn((id, settings) => ({ id, settings })); provider.chat = provider.languageModel; return provider; }) })); // Mock the base provider jest.unstable_mockModule('../../../src/ai-providers/base-provider.js', () => ({ BaseAIProvider: class { constructor() { this.name = 'Base Provider'; this.needsExplicitJsonSchema = false; this.supportsTemperature = true; } handleError(context, error) { throw error; } validateParams(params) { if (!params.modelId) { throw new Error('Model ID is required'); } } validateMessages(messages) { if (!messages || !Array.isArray(messages)) { throw new Error('Invalid messages array'); } } } })); // Import after mocking const { GeminiCliProvider } = await import( '../../../src/ai-providers/gemini-cli.js' ); const { createGeminiProvider } = await import('ai-sdk-provider-gemini-cli'); describe('GeminiCliProvider', () => { let provider; beforeEach(() => { provider = new GeminiCliProvider(); jest.clearAllMocks(); }); describe('constructor', () => { it('should set the provider name to Gemini CLI', () => { expect(provider.name).toBe('Gemini CLI'); }); it('should set supportsTemperature to false', () => { expect(provider.supportsTemperature).toBe(false); }); it('should not set needsExplicitJsonSchema (rely on SDK defaults)', () => { // The SDK has defaultObjectGenerationMode = 'json' and supportsStructuredOutputs = true // so we don't need to override this in the provider expect(provider.needsExplicitJsonSchema).toBe(false); }); }); describe('validateAuth', () => { it('should not throw an error when API key is provided', () => { expect(() => provider.validateAuth({ apiKey: 'test-key' })).not.toThrow(); }); it('should not throw an error when no API key is provided', () => { expect(() => provider.validateAuth({})).not.toThrow(); }); it('should not throw an error when called with no parameters', () => { expect(() => provider.validateAuth()).not.toThrow(); }); it('should not throw an error when called with undefined', () => { expect(() => provider.validateAuth(undefined)).not.toThrow(); }); }); describe('getClient', () => { it('should return a gemini client with API key auth when apiKey is provided', async () => { const client = await provider.getClient({ apiKey: 'test-api-key' }); expect(client).toBeDefined(); expect(typeof client).toBe('function'); expect(createGeminiProvider).toHaveBeenCalledWith({ authType: 'api-key', apiKey: 'test-api-key' }); }); it('should return a gemini client with OAuth auth when no apiKey is provided', async () => { const client = await provider.getClient({}); expect(client).toBeDefined(); expect(typeof client).toBe('function'); expect(createGeminiProvider).toHaveBeenCalledWith({ authType: 'oauth-personal' }); }); it('should use OAuth when apiKey is the special no-key-required value', async () => { const client = await provider.getClient({ apiKey: 'gemini-cli-no-key-required' }); expect(client).toBeDefined(); expect(createGeminiProvider).toHaveBeenCalledWith({ authType: 'oauth-personal' }); }); it('should include baseURL when provided with API key', async () => { const client = await provider.getClient({ apiKey: 'test-key', baseURL: 'https://custom-endpoint.com' }); expect(client).toBeDefined(); expect(createGeminiProvider).toHaveBeenCalledWith({ authType: 'api-key', apiKey: 'test-key', baseURL: 'https://custom-endpoint.com' }); }); it('should include baseURL when provided with OAuth', async () => { const client = await provider.getClient({ baseURL: 'https://custom-endpoint.com' }); expect(client).toBeDefined(); expect(createGeminiProvider).toHaveBeenCalledWith({ authType: 'oauth-personal', baseURL: 'https://custom-endpoint.com' }); }); it('should have languageModel and chat methods', async () => { const client = await provider.getClient({ apiKey: 'test-key' }); expect(client.languageModel).toBeDefined(); expect(client.chat).toBeDefined(); expect(client.chat).toBe(client.languageModel); }); it('should use OAuth with empty string API key', async () => { await provider.getClient({ apiKey: '' }); expect(createGeminiProvider).toHaveBeenCalledWith({ authType: 'oauth-personal' }); }); it('should throw error when createGeminiProvider fails', async () => { createGeminiProvider.mockImplementationOnce(() => { throw new Error('Auth initialization failed'); }); await expect(provider.getClient({})).rejects.toThrow( 'Auth initialization failed' ); }); }); describe('getRequiredApiKeyName', () => { it('should return GEMINI_API_KEY', () => { expect(provider.getRequiredApiKeyName()).toBe('GEMINI_API_KEY'); }); }); describe('isRequiredApiKey', () => { it('should return false (API key is optional for gemini-cli)', () => { expect(provider.isRequiredApiKey()).toBe(false); }); }); describe('base class delegation', () => { it('should not override generateText (uses base class)', () => { // Verify that generateText is not defined on the provider prototype // (it inherits from base class) expect( Object.prototype.hasOwnProperty.call( GeminiCliProvider.prototype, 'generateText' ) ).toBe(false); }); it('should not override streamText (uses base class)', () => { expect( Object.prototype.hasOwnProperty.call( GeminiCliProvider.prototype, 'streamText' ) ).toBe(false); }); it('should not override generateObject (uses base class)', () => { expect( Object.prototype.hasOwnProperty.call( GeminiCliProvider.prototype, 'generateObject' ) ).toBe(false); }); it('should not override streamObject (uses base class)', () => { expect( Object.prototype.hasOwnProperty.call( GeminiCliProvider.prototype, 'streamObject' ) ).toBe(false); }); it('should not have JSON extraction methods (removed)', () => { expect(provider._extractSystemMessage).toBeUndefined(); expect(provider._detectJsonRequest).toBeUndefined(); expect(provider._getJsonEnforcementPrompt).toBeUndefined(); expect(provider._isValidJson).toBeUndefined(); expect(provider.extractJson).toBeUndefined(); expect(provider._simplifyJsonPrompts).toBeUndefined(); }); }); describe('authentication scenarios', () => { it('should use api-key auth type with valid API key', async () => { await provider.getClient({ apiKey: 'gemini-test-key' }); expect(createGeminiProvider).toHaveBeenCalledWith({ authType: 'api-key', apiKey: 'gemini-test-key' }); }); it('should use oauth-personal auth type without API key', async () => { await provider.getClient({}); expect(createGeminiProvider).toHaveBeenCalledWith({ authType: 'oauth-personal' }); }); it('should prioritize OAuth over special marker API key', async () => { await provider.getClient({ apiKey: 'gemini-cli-no-key-required' }); expect(createGeminiProvider).toHaveBeenCalledWith({ authType: 'oauth-personal' }); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/eyaltoledano/claude-task-master'

If you have feedback or need assistance with the MCP directory API, please join our Discord server