provider = new MistralProvider('test-api-key'); } public function test_builds_request_correctly(): void { $messages = [ ['role' => 'user', 'content' => 'Hello'] ]; $options = [ 'model' => 'mistral-small-latest', '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('mistral-small-latest', $result['model']); $this->assertEquals(0.8, $result['temperature']); $this->assertEquals(1000, $result['max_tokens']); $this->assertEquals($messages, $result['messages']); $this->assertArrayNotHasKey('stream', $result); // stream=false is filtered out } public function test_normalizes_response_correctly(): void { $rawResponse = [ 'id' => 'cmpl-123', 'model' => 'mistral-small-latest', 'choices' => [ [ 'message' => [ 'role' => 'assistant', 'content' => 'Bonjour! Comment puis-je vous aider?' ], 'finish_reason' => 'stop' ] ], 'usage' => [ 'prompt_tokens' => 12, 'completion_tokens' => 18, 'total_tokens' => 30 ] ]; $normalized = $this->provider->normalizeResponse($rawResponse); $this->assertEquals('cmpl-123', $normalized['id']); $this->assertEquals('mistral-small-latest', $normalized['model']); $this->assertEquals('Bonjour! Comment puis-je vous aider?', $normalized['content']); $this->assertEquals('assistant', $normalized['role']); $this->assertEquals('stop', $normalized['finish_reason']); $this->assertEquals(12, $normalized['usage']['prompt_tokens']); $this->assertEquals(18, $normalized['usage']['completion_tokens']); $this->assertEquals(30, $normalized['usage']['total_tokens']); } public function test_calculates_cost_correctly(): void { ModelPricing::updateOrCreate( [ 'provider' => 'mistral', 'model' => 'mistral-small-latest', 'effective_from' => now()->toDateString(), ], [ 'input_price_per_million' => 0.20, 'output_price_per_million' => 0.60, 'is_active' => true, ] ); Cache::flush(); $cost = $this->provider->calculateCost(1000, 500, 'mistral-small-latest'); // Expected: (1000/1M * 0.20) + (500/1M * 0.60) = 0.0002 + 0.0003 = 0.0005 $this->assertEquals(0.0005, $cost); } public function test_handles_api_errors(): void { Http::fake([ 'https://api.mistral.ai/*' => 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('mistral-large-latest', $models); $this->assertContains('mistral-small-latest', $models); $this->assertContains('open-mixtral-8x7b', $models); } }