provider = new GeminiProvider('test-api-key'); } public function test_builds_request_correctly(): void { $messages = [ ['role' => 'user', 'content' => 'Hello, Gemini!'] ]; $options = [ 'model' => 'gemini-pro', 'temperature' => 0.9, 'max_tokens' => 2000 ]; $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('buildRequest'); $method->setAccessible(true); $result = $method->invoke($this->provider, $messages, $options); $this->assertArrayHasKey('contents', $result); $this->assertCount(1, $result['contents']); $this->assertEquals('user', $result['contents'][0]['role']); $this->assertEquals('Hello, Gemini!', $result['contents'][0]['parts'][0]['text']); $this->assertArrayHasKey('generationConfig', $result); $this->assertEquals(0.9, $result['generationConfig']['temperature']); $this->assertEquals(2000, $result['generationConfig']['maxOutputTokens']); } public function test_converts_system_messages_to_user(): void { $messages = [ ['role' => 'system', 'content' => 'You are helpful'], ['role' => 'user', 'content' => 'Hello'] ]; $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('buildRequest'); $method->setAccessible(true); $result = $method->invoke($this->provider, $messages, []); $this->assertEquals('user', $result['contents'][0]['role']); $this->assertEquals('user', $result['contents'][1]['role']); } public function test_normalizes_response_correctly(): void { $rawResponse = [ 'candidates' => [ [ 'content' => [ 'parts' => [ ['text' => 'Hello! How can I help you today?'] ] ], 'finishReason' => 'STOP' ] ], 'usageMetadata' => [ 'promptTokenCount' => 8, 'candidatesTokenCount' => 15, 'totalTokenCount' => 23 ], 'modelVersion' => 'gemini-pro' ]; $normalized = $this->provider->normalizeResponse($rawResponse); $this->assertEquals('gemini-pro', $normalized['model']); $this->assertEquals('Hello! How can I help you today?', $normalized['content']); $this->assertEquals('assistant', $normalized['role']); $this->assertEquals('STOP', $normalized['finish_reason']); $this->assertEquals(8, $normalized['usage']['prompt_tokens']); $this->assertEquals(15, $normalized['usage']['completion_tokens']); $this->assertEquals(23, $normalized['usage']['total_tokens']); } public function test_calculates_cost_correctly(): void { ModelPricing::updateOrCreate( [ 'provider' => 'gemini', 'model' => 'gemini-pro', 'effective_from' => now()->toDateString(), ], [ 'input_price_per_million' => 0.50, 'output_price_per_million' => 1.50, 'is_active' => true, ] ); Cache::flush(); $cost = $this->provider->calculateCost(1000, 500, 'gemini-pro'); // Expected: (1000/1M * 0.50) + (500/1M * 1.50) = 0.0005 + 0.00075 = 0.00125 $this->assertEquals(0.00125, $cost); } public function test_handles_api_errors(): void { Http::fake([ 'https://generativelanguage.googleapis.com/*' => Http::response(['error' => 'Invalid API key'], 401) ]); $this->expectException(\App\Exceptions\ProviderException::class); $this->expectExceptionMessage('Invalid API key'); $this->provider->chatCompletion([ ['role' => 'user', 'content' => 'test'] ]); } public function test_get_supported_models(): void { $models = $this->provider->getSupportedModels(); $this->assertIsArray($models); $this->assertContains('gemini-pro', $models); $this->assertContains('gemini-1.5-pro', $models); $this->assertContains('gemini-1.5-flash', $models); } }