import 'package:flutter_test/flutter_test.dart'; import 'package:kouyu_english/core/ai_config.dart'; import 'package:kouyu_english/core/ai_service.dart'; import 'package:kouyu_english/core/models.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('AiService resolveEndpointUri', () { test('resolves OpenAI endpoint ending in /v1 to /v1/chat/completions', () { final uri = AiService.resolveEndpointUri( provider: AiProviderType.openAi, endpoint: 'https://api.openai.com/v1', model: 'gpt-4o-mini', ); expect(uri.toString(), equals('https://api.openai.com/v1/chat/completions')); }); test('resolves OpenAI endpoint without /v1 to /v1/chat/completions', () { final uri = AiService.resolveEndpointUri( provider: AiProviderType.openAi, endpoint: 'https://api.openai.com', model: 'gpt-4o-mini', ); expect(uri.toString(), equals('https://api.openai.com/v1/chat/completions')); }); test('resolves custom compatible endpoint to /v1/chat/completions', () { final uri = AiService.resolveEndpointUri( provider: AiProviderType.compatible, endpoint: 'https://codex.slcydia.fun/v1', model: 'gpt-4o-mini', ); expect(uri.toString(), equals('https://codex.slcydia.fun/v1/chat/completions')); }); test('preserves endpoint already ending in /v1/chat/completions', () { final uri = AiService.resolveEndpointUri( provider: AiProviderType.compatible, endpoint: 'https://codex.slcydia.fun/v1/chat/completions', model: 'gpt-4o-mini', ); expect(uri.toString(), equals('https://codex.slcydia.fun/v1/chat/completions')); }); test('preserves /responses endpoint for OpenAI Responses API support', () { final uri = AiService.resolveEndpointUri( provider: AiProviderType.compatible, endpoint: 'https://codex.slcydia.fun/v1/responses', model: 'gpt-4o-mini', ); expect(uri.toString(), equals('https://codex.slcydia.fun/v1/responses')); }); test('resolves Gemini endpoint to :generateContent', () { final uri = AiService.resolveEndpointUri( provider: AiProviderType.gemini, endpoint: 'https://generativelanguage.googleapis.com/v1beta', model: 'gemini-2.5-flash', ); expect( uri.toString(), equals('https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent'), ); }); }); group('AiConfigFile', () { test('parses standard JSON configuration correctly', () { const rawJson = '''{ "provider": "compatible", "endpoint": "https://api.openai.com/v1", "model": "gpt-4o-mini", "apiKey": "sk-test-key-12345", "description": "Default AI configuration" }'''; final config = AiConfigFile.parse(rawJson); expect(config.provider, equals(AiProviderType.compatible)); expect(config.endpoint, equals('https://api.openai.com/v1')); expect(config.model, equals('gpt-4o-mini')); expect(config.apiKey, equals('sk-test-key-12345')); expect(config.description, equals('Default AI configuration')); }); test('handles case-insensitive provider mapping and fallbacks', () { const openAiJson = '{"provider": "openAi", "endpoint": "https://api.openai.com/v1", "model": "gpt-4o"}'; expect(AiConfigFile.parse(openAiJson).provider, equals(AiProviderType.openAi)); const geminiJson = '{"provider": "GEMINI", "endpoint": "https://generativelanguage.googleapis.com/v1beta", "model": "gemini-2.5-flash"}'; expect(AiConfigFile.parse(geminiJson).provider, equals(AiProviderType.gemini)); const mockJson = '{"provider": "mock", "endpoint": "", "model": ""}'; expect(AiConfigFile.parse(mockJson).provider, equals(AiProviderType.mock)); const unknownJson = '{"provider": "unknown_provider", "endpoint": "", "model": ""}'; expect(AiConfigFile.parse(unknownJson).provider, equals(AiProviderType.compatible)); }); test('serializes to JSON correctly', () { const config = AiConfigFile( provider: AiProviderType.compatible, endpoint: 'http://localhost:8000/v1', model: 'llama3', apiKey: 'sk-local', description: 'Local proxy', ); final json = config.toJson(); expect(json['provider'], equals('compatible')); expect(json['endpoint'], equals('http://localhost:8000/v1')); expect(json['model'], equals('llama3')); expect(json['apiKey'], equals('sk-local')); expect(json['description'], equals('Local proxy')); }); }); group('AiService API Key Resolution', () { test('uses explicit key over fallback key', () async { AiService.instance.setFallbackApiKey('fallback-key'); final key = await AiService.instance.resolveApiKey('explicit-override'); expect(key, equals('explicit-override')); }); test('falls back to fallbackApiKey when no explicit or secure key is set', () async { AiService.instance.setFallbackApiKey('config-file-key'); final key = await AiService.instance.resolveApiKey(); expect(key, equals('config-file-key')); }); }); }