import { describe, it, expect } from 'vitest'; import { BaseProvider } from '@/providers/base-provider.js'; import type { ProviderConfig, ExecuteOptions, ProviderMessage, InstallationStatus, ModelDefinition, } from '@automaker/types'; // Concrete implementation for testing the abstract class class TestProvider extends BaseProvider { getName(): string { return 'test-provider'; } async *executeQuery(_options: ExecuteOptions): AsyncGenerator { yield { type: 'text', text: 'test response' }; } async detectInstallation(): Promise { return { installed: true }; } getAvailableModels(): ModelDefinition[] { return [{ id: 'test-model-1', name: 'Test Model 1', description: 'A test model' }]; } } describe('base-provider.ts', () => { describe('constructor', () => { it('should initialize with empty config when none provided', () => { const provider = new TestProvider(); expect(provider.getConfig()).toEqual({}); }); it('should initialize with provided config', () => { const config: ProviderConfig = { apiKey: 'test-key', baseUrl: 'https://test.com', }; const provider = new TestProvider(config); expect(provider.getConfig()).toEqual(config); }); it('should call getName() during initialization', () => { const provider = new TestProvider(); expect(provider.getName()).toBe('test-provider'); }); }); describe('validateConfig', () => { it('should return valid when config exists', () => { const provider = new TestProvider({ apiKey: 'test' }); const result = provider.validateConfig(); expect(result.valid).toBe(true); expect(result.errors).toHaveLength(0); expect(result.warnings).toHaveLength(0); }); it('should return invalid when config is undefined', () => { // Create provider without config const provider = new TestProvider(); // Manually set config to undefined to test edge case (provider as any).config = undefined; const result = provider.validateConfig(); expect(result.valid).toBe(false); expect(result.errors).toContain('Provider config is missing'); }); it('should return valid for empty config object', () => { const provider = new TestProvider({}); const result = provider.validateConfig(); expect(result.valid).toBe(true); expect(result.errors).toHaveLength(0); }); it('should include warnings array in result', () => { const provider = new TestProvider(); const result = provider.validateConfig(); expect(result).toHaveProperty('warnings'); expect(Array.isArray(result.warnings)).toBe(true); }); }); describe('supportsFeature', () => { it("should support 'tools' feature", () => { const provider = new TestProvider(); expect(provider.supportsFeature('tools')).toBe(true); }); it("should support 'text' feature", () => { const provider = new TestProvider(); expect(provider.supportsFeature('text')).toBe(true); }); it('should not support unknown features', () => { const provider = new TestProvider(); expect(provider.supportsFeature('vision')).toBe(false); expect(provider.supportsFeature('mcp')).toBe(false); expect(provider.supportsFeature('unknown')).toBe(false); }); it('should be case-sensitive', () => { const provider = new TestProvider(); expect(provider.supportsFeature('TOOLS')).toBe(false); expect(provider.supportsFeature('Text')).toBe(false); }); }); describe('getConfig', () => { it('should return current config', () => { const config: ProviderConfig = { apiKey: 'test-key', model: 'test-model', }; const provider = new TestProvider(config); expect(provider.getConfig()).toEqual(config); }); it('should return same reference', () => { const config: ProviderConfig = { apiKey: 'test' }; const provider = new TestProvider(config); const retrieved1 = provider.getConfig(); const retrieved2 = provider.getConfig(); expect(retrieved1).toBe(retrieved2); }); }); describe('setConfig', () => { it('should merge partial config with existing config', () => { const provider = new TestProvider({ apiKey: 'original-key' }); provider.setConfig({ model: 'new-model' }); expect(provider.getConfig()).toEqual({ apiKey: 'original-key', model: 'new-model', }); }); it('should override existing fields', () => { const provider = new TestProvider({ apiKey: 'old-key', model: 'old-model' }); provider.setConfig({ apiKey: 'new-key' }); expect(provider.getConfig()).toEqual({ apiKey: 'new-key', model: 'old-model', }); }); it('should accept empty object', () => { const provider = new TestProvider({ apiKey: 'test' }); const originalConfig = provider.getConfig(); provider.setConfig({}); expect(provider.getConfig()).toEqual(originalConfig); }); it('should handle multiple updates', () => { const provider = new TestProvider(); provider.setConfig({ apiKey: 'key1' }); provider.setConfig({ model: 'model1' }); provider.setConfig({ baseUrl: 'https://test.com' }); expect(provider.getConfig()).toEqual({ apiKey: 'key1', model: 'model1', baseUrl: 'https://test.com', }); }); it('should preserve other fields when updating one field', () => { const provider = new TestProvider({ apiKey: 'key', model: 'model', baseUrl: 'https://test.com', }); provider.setConfig({ model: 'new-model' }); expect(provider.getConfig()).toEqual({ apiKey: 'key', model: 'new-model', baseUrl: 'https://test.com', }); }); }); describe('abstract methods', () => { it('should require getName implementation', () => { const provider = new TestProvider(); expect(typeof provider.getName).toBe('function'); expect(provider.getName()).toBe('test-provider'); }); it('should require executeQuery implementation', async () => { const provider = new TestProvider(); expect(typeof provider.executeQuery).toBe('function'); const generator = provider.executeQuery({ prompt: 'test', projectDirectory: '/test', }); const result = await generator.next(); expect(result.value).toEqual({ type: 'text', text: 'test response' }); }); it('should require detectInstallation implementation', async () => { const provider = new TestProvider(); expect(typeof provider.detectInstallation).toBe('function'); const status = await provider.detectInstallation(); expect(status).toHaveProperty('installed'); }); it('should require getAvailableModels implementation', () => { const provider = new TestProvider(); expect(typeof provider.getAvailableModels).toBe('function'); const models = provider.getAvailableModels(); expect(Array.isArray(models)).toBe(true); expect(models.length).toBeGreaterThan(0); }); }); });