provider = new OpenAIProvider('test-api-key'); } public function test_builds_request_correctly(): void { $messages = [ ['role' => 'user', 'content' => 'Hello'] ]; $options = [ 'model' => 'gpt-4o-mini', 'temperature' => 0.8, 'max_tokens' => 1000 ]; $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('buildRequest'); $method->setAccessible(true); $result = $method->invoke($this->provider, $messages, $options); $this->assertEquals('gpt-4o-mini', $result['model']); $this->assertEquals(0.8, $result['temperature']); $this->assertEquals(1000, $result['max_tokens']); $this->assertEquals($messages, $result['messages']); $this->assertFalse($result['stream']); } public function test_normalizes_response_correctly(): void { $rawResponse = [ 'id' => 'chatcmpl-123', 'model' => 'gpt-4o-mini', 'choices' => [ [ 'message' => [ 'role' => 'assistant', 'content' => 'Hello! How can I help you?' ], 'finish_reason' => 'stop' ] ], 'usage' => [ 'prompt_tokens' => 10, 'completion_tokens' => 20, 'total_tokens' => 30 ] ]; $normalized = $this->provider->normalizeResponse($rawResponse); $this->assertEquals('chatcmpl-123', $normalized['id']); $this->assertEquals('gpt-4o-mini', $normalized['model']); $this->assertEquals('Hello! How can I help you?', $normalized['content']); $this->assertEquals('assistant', $normalized['role']); $this->assertEquals('stop', $normalized['finish_reason']); $this->assertEquals(10, $normalized['usage']['prompt_tokens']); $this->assertEquals(20, $normalized['usage']['completion_tokens']); $this->assertEquals(30, $normalized['usage']['total_tokens']); } public function test_calculates_cost_correctly(): void { // Create pricing in database ModelPricing::create([ 'provider' => 'openai', 'model' => 'gpt-4o-mini', 'input_price_per_million' => 0.15, 'output_price_per_million' => 0.60, 'is_active' => true, 'effective_from' => now() ]); Cache::flush(); $cost = $this->provider->calculateCost(1000, 500, 'gpt-4o-mini'); // Expected: (1000/1M * 0.15) + (500/1M * 0.60) = 0.00015 + 0.0003 = 0.00045 $this->assertEquals(0.00045, $cost); } public function test_handles_api_errors(): void { Http::fake([ 'https://api.openai.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_retries_on_server_error(): void { Http::fake([ 'https://api.openai.com/*' => Http::sequence() ->push(['error' => 'Server error'], 500) ->push(['error' => 'Server error'], 500) ->push([ 'id' => 'test-123', 'model' => 'gpt-4o-mini', 'choices' => [[ 'message' => ['content' => 'Success', 'role' => 'assistant'], 'finish_reason' => 'stop' ]], 'usage' => ['prompt_tokens' => 10, 'completion_tokens' => 5, 'total_tokens' => 15] ], 200) ]); $result = $this->provider->chatCompletion([ ['role' => 'user', 'content' => 'test'] ]); $this->assertArrayHasKey('id', $result); $this->assertEquals('test-123', $result['id']); } public function test_get_supported_models(): void { $models = $this->provider->getSupportedModels(); $this->assertIsArray($models); $this->assertContains('gpt-4o', $models); $this->assertContains('gpt-4o-mini', $models); $this->assertContains('gpt-3.5-turbo', $models); } }