124 lines
4.1 KiB
PHP
124 lines
4.1 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace App\Tests\Unit\Infrastructure\AI;
|
||
|
|
|
||
|
|
use App\Infrastructure\AI\MistralClient;
|
||
|
|
use PHPUnit\Framework\TestCase;
|
||
|
|
use Symfony\Component\HttpClient\MockHttpClient;
|
||
|
|
use Symfony\Component\HttpClient\Response\MockResponse;
|
||
|
|
|
||
|
|
final class MistralClientTest extends TestCase
|
||
|
|
{
|
||
|
|
private function makeClient(MockResponse $response): MistralClient
|
||
|
|
{
|
||
|
|
return new MistralClient(
|
||
|
|
new MockHttpClient($response),
|
||
|
|
'test-api-key',
|
||
|
|
'https://api.mistral.ai',
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testGenerateReturnsParsedContent(): void
|
||
|
|
{
|
||
|
|
$body = json_encode([
|
||
|
|
'choices' => [['message' => ['content' => 'Dell Latitude 5520']]],
|
||
|
|
]);
|
||
|
|
|
||
|
|
$client = $this->makeClient(new MockResponse((string) $body));
|
||
|
|
|
||
|
|
self::assertSame('Dell Latitude 5520', $client->generate('mistral-large-latest', 'What is this?'));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testGenerateSendsAuthHeader(): void
|
||
|
|
{
|
||
|
|
$body = json_encode([
|
||
|
|
'choices' => [['message' => ['content' => 'ok']]],
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = new MockResponse((string) $body);
|
||
|
|
$client = $this->makeClient($response);
|
||
|
|
$client->generate('mistral-large-latest', 'test');
|
||
|
|
|
||
|
|
self::assertStringContainsString(
|
||
|
|
'Bearer test-api-key',
|
||
|
|
implode(', ', $response->getRequestOptions()['headers'] ?? []),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testGenerateWithImageReturnsParsedContent(): void
|
||
|
|
{
|
||
|
|
$tmpFile = tempnam(sys_get_temp_dir(), 'test_') . '.jpg';
|
||
|
|
file_put_contents($tmpFile, 'fake-image-data');
|
||
|
|
|
||
|
|
$body = json_encode([
|
||
|
|
'choices' => [['message' => ['content' => 'MODEL: ThinkPad X1']]],
|
||
|
|
]);
|
||
|
|
|
||
|
|
$client = $this->makeClient(new MockResponse((string) $body));
|
||
|
|
$result = $client->generateWithImage('pixtral-12b-2409', 'Read the nameplate', $tmpFile);
|
||
|
|
|
||
|
|
unlink($tmpFile);
|
||
|
|
|
||
|
|
self::assertSame('MODEL: ThinkPad X1', $result);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testGenerateWithImageEncodesImageAsBase64(): void
|
||
|
|
{
|
||
|
|
$tmpFile = tempnam(sys_get_temp_dir(), 'test_') . '.png';
|
||
|
|
$imageContent = 'fake-png-bytes';
|
||
|
|
file_put_contents($tmpFile, $imageContent);
|
||
|
|
|
||
|
|
$body = json_encode([
|
||
|
|
'choices' => [['message' => ['content' => 'ok']]],
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = new MockResponse((string) $body);
|
||
|
|
$client = $this->makeClient($response);
|
||
|
|
$client->generateWithImage('pixtral-12b-2409', 'describe', $tmpFile);
|
||
|
|
|
||
|
|
unlink($tmpFile);
|
||
|
|
|
||
|
|
$requestBody = json_decode($response->getRequestOptions()['body'], true);
|
||
|
|
$imageUrl = $requestBody['messages'][0]['content'][1]['image_url']['url'];
|
||
|
|
|
||
|
|
self::assertStringStartsWith('data:image/png;base64,', $imageUrl);
|
||
|
|
self::assertStringContainsString(base64_encode($imageContent), $imageUrl);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @dataProvider mimeTypeProvider
|
||
|
|
*/
|
||
|
|
public function testGuessMimeTypeFromExtension(string $filename, string $expectedMime): void
|
||
|
|
{
|
||
|
|
$tmpFile = tempnam(sys_get_temp_dir(), 'test_') . '.' . pathinfo($filename, PATHINFO_EXTENSION);
|
||
|
|
file_put_contents($tmpFile, 'x');
|
||
|
|
|
||
|
|
$body = json_encode(['choices' => [['message' => ['content' => 'ok']]]]);
|
||
|
|
$response = new MockResponse((string) $body);
|
||
|
|
$client = $this->makeClient($response);
|
||
|
|
$client->generateWithImage('pixtral-12b-2409', 'x', $tmpFile);
|
||
|
|
|
||
|
|
unlink($tmpFile);
|
||
|
|
|
||
|
|
$requestBody = json_decode($response->getRequestOptions()['body'], true);
|
||
|
|
$imageUrl = $requestBody['messages'][0]['content'][1]['image_url']['url'];
|
||
|
|
|
||
|
|
self::assertStringStartsWith('data:' . $expectedMime . ';base64,', $imageUrl);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @return array<string, array{string, string}> */
|
||
|
|
public static function mimeTypeProvider(): array
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
'jpeg' => ['photo.jpg', 'image/jpeg'],
|
||
|
|
'jpeg uppercase' => ['photo.JPG', 'image/jpeg'],
|
||
|
|
'jpeg ext' => ['photo.jpeg', 'image/jpeg'],
|
||
|
|
'png' => ['photo.png', 'image/png'],
|
||
|
|
'webp' => ['photo.webp', 'image/webp'],
|
||
|
|
'unknown falls back to jpeg' => ['photo.bmp', 'image/jpeg'],
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}
|