/** * @fileoverview Mock provider for testing BaseProvider functionality */ import type { AIModel, AIOptions, AIResponse, ProviderInfo, ProviderUsageStats } from '../../src/interfaces/ai-provider.interface'; import { BaseProvider, type BaseProviderConfig, type CompletionResult } from '../../src/providers/ai/base-provider'; /** * Configuration for MockProvider behavior */ export interface MockProviderOptions extends BaseProviderConfig { shouldFail?: boolean; failAfterAttempts?: number; simulateRateLimit?: boolean; simulateTimeout?: boolean; responseDelay?: number; tokenMultiplier?: number; } /** * Mock provider for testing BaseProvider functionality */ export class MockProvider extends BaseProvider { private attemptCount = 0; private readonly options: MockProviderOptions; constructor(options: MockProviderOptions) { super(options); this.options = options; } /** * Simulate completion generation with configurable behavior */ protected async generateCompletionInternal( prompt: string, _options?: AIOptions ): Promise { this.attemptCount++; // Simulate delay if configured if (this.options.responseDelay) { await this.sleep(this.options.responseDelay); } // Simulate failures based on configuration if (this.options.shouldFail) { throw new Error('Mock provider error'); } if ( this.options.failAfterAttempts && this.attemptCount <= this.options.failAfterAttempts ) { if (this.options.simulateRateLimit) { throw new Error('Rate limit exceeded - too many requests (429)'); } if (this.options.simulateTimeout) { throw new Error('Request timeout - ECONNRESET'); } throw new Error('Temporary failure'); } // Return successful mock response return { content: `Mock response to: ${prompt}`, inputTokens: this.calculateTokens(prompt), outputTokens: this.calculateTokens(`Mock response to: ${prompt}`), finishReason: 'complete', model: this.model }; } /** * Simple token calculation for testing */ calculateTokens(text: string, _model?: string): number { const multiplier = this.options.tokenMultiplier || 1; // Rough approximation: 1 token per 4 characters return Math.ceil((text.length / 4) * multiplier); } getName(): string { return 'mock'; } getDefaultModel(): string { return 'mock-model-v1'; } /** * Get the number of attempts made */ getAttemptCount(): number { return this.attemptCount; } /** * Reset attempt counter */ resetAttempts(): void { this.attemptCount = 0; } // Implement remaining abstract methods async generateStreamingCompletion( prompt: string, _options?: AIOptions ): AsyncIterator> { // Simple mock implementation const response: Partial = { content: `Mock streaming response to: ${prompt}`, provider: this.getName(), model: this.model }; return { async next() { return { value: response, done: true }; } }; } async isAvailable(): Promise { return !this.options.shouldFail; } getProviderInfo(): ProviderInfo { return { name: 'mock', displayName: 'Mock Provider', description: 'Mock provider for testing', models: this.getAvailableModels(), defaultModel: this.getDefaultModel(), requiresApiKey: true, features: { streaming: true, functions: false, vision: false, embeddings: false } }; } getAvailableModels(): AIModel[] { return [ { id: 'mock-model-v1', name: 'Mock Model v1', description: 'First mock model', contextLength: 4096, inputCostPer1K: 0.001, outputCostPer1K: 0.002, supportsStreaming: true }, { id: 'mock-model-v2', name: 'Mock Model v2', description: 'Second mock model', contextLength: 8192, inputCostPer1K: 0.002, outputCostPer1K: 0.004, supportsStreaming: true } ]; } async validateCredentials(): Promise { return this.apiKey === 'valid-key'; } async getUsageStats(): Promise { return { totalRequests: this.attemptCount, totalTokens: 1000, totalCost: 0.01, requestsToday: this.attemptCount, tokensToday: 1000, costToday: 0.01, averageResponseTime: 100, successRate: 0.9, lastRequestAt: new Date().toISOString() }; } async initialize(): Promise { // No-op for mock } async close(): Promise { // No-op for mock } // Override retry configuration for testing protected getMaxRetries(): number { return this.options.failAfterAttempts ? this.options.failAfterAttempts + 1 : 3; } }