2026-05-17 22:43:47 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace App\Tests\Unit\Infrastructure\AI\Agent;
|
|
|
|
|
|
2026-05-18 16:57:01 +00:00
|
|
|
use App\Domain\AI\PromptTemplate;
|
|
|
|
|
use App\Domain\AI\Repository\PromptTemplateRepositoryInterface;
|
2026-05-17 22:43:47 +00:00
|
|
|
use App\Infrastructure\AI\Agent\OllamaVisionAgent;
|
|
|
|
|
use App\Infrastructure\AI\OllamaClientInterface;
|
2026-05-18 16:57:01 +00:00
|
|
|
use App\Infrastructure\AI\PromptTemplateService;
|
2026-05-17 22:43:47 +00:00
|
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
|
|
|
|
|
|
final class OllamaVisionAgentTest extends TestCase
|
|
|
|
|
{
|
|
|
|
|
private OllamaClientInterface&MockObject $ollama;
|
|
|
|
|
private OllamaVisionAgent $agent;
|
|
|
|
|
|
|
|
|
|
protected function setUp(): void
|
|
|
|
|
{
|
|
|
|
|
$this->ollama = $this->createMock(OllamaClientInterface::class);
|
2026-05-18 16:57:01 +00:00
|
|
|
|
|
|
|
|
$repo = $this->createMock(PromptTemplateRepositoryInterface::class);
|
|
|
|
|
$repo->method('findByKey')->willReturn(new PromptTemplate('vision_analyze', 'prompt'));
|
|
|
|
|
$prompts = new PromptTemplateService($repo);
|
|
|
|
|
|
|
|
|
|
$this->agent = new OllamaVisionAgent($this->ollama, $prompts, 'llava');
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 10:56:37 +00:00
|
|
|
public function testParsesAllFields(): void
|
2026-05-18 16:57:01 +00:00
|
|
|
{
|
|
|
|
|
$this->ollama->method('generateWithImage')->willReturn(
|
|
|
|
|
"MANUFACTURER: Lenovo\nMODEL_NAME: ThinkBook 14 G6 IRL\nMODEL_NUMBER: 21KG00NQGE\nSERIAL: PNV09SJZ"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$result = $this->agent->analyze('/tmp/photo.jpg');
|
|
|
|
|
|
|
|
|
|
$this->assertSame('Lenovo', $result['manufacturer']);
|
|
|
|
|
$this->assertSame('ThinkBook 14 G6 IRL', $result['modelName']);
|
|
|
|
|
$this->assertSame('21KG00NQGE', $result['modelNumber']);
|
|
|
|
|
$this->assertSame('PNV09SJZ', $result['serial']);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 10:56:37 +00:00
|
|
|
public function testStripsSerialBleedIntoModelNumberLeading(): void
|
2026-05-18 16:57:01 +00:00
|
|
|
{
|
|
|
|
|
// LLM outputs "SERIAL: xyz" as the entire value for MODEL_NUMBER
|
|
|
|
|
$this->ollama->method('generateWithImage')->willReturn(
|
|
|
|
|
"MANUFACTURER: ELPIDA\nMODEL_NAME: 2GB 2Rx8 PC3-10600S-9-10-F1\nMODEL_NUMBER: SERIAL: 1005NK677594\nSERIAL: 1005NK677594"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$result = $this->agent->analyze('/tmp/photo.jpg');
|
|
|
|
|
|
|
|
|
|
$this->assertSame('', $result['modelNumber']);
|
|
|
|
|
$this->assertSame('1005NK677594', $result['serial']);
|
2026-05-17 22:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-19 10:56:37 +00:00
|
|
|
public function testStripsSerialBleedIntoModelNameMidValue(): void
|
2026-05-17 22:43:47 +00:00
|
|
|
{
|
2026-05-18 16:57:01 +00:00
|
|
|
// LLM appends serial after the model name
|
|
|
|
|
$this->ollama->method('generateWithImage')->willReturn(
|
|
|
|
|
"MANUFACTURER: Lenovo\nMODEL_NAME: ThinkBook 14 G6 IRL SERIAL: PNV09SJZ\nMODEL_NUMBER: 21KG00NQGE\nSERIAL: PNV09SJZ"
|
|
|
|
|
);
|
2026-05-17 22:43:47 +00:00
|
|
|
|
|
|
|
|
$result = $this->agent->analyze('/tmp/photo.jpg');
|
|
|
|
|
|
2026-05-18 16:57:01 +00:00
|
|
|
$this->assertSame('ThinkBook 14 G6 IRL', $result['modelName']);
|
|
|
|
|
$this->assertSame('PNV09SJZ', $result['serial']);
|
2026-05-17 22:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-19 10:56:37 +00:00
|
|
|
public function testReturnsEmptyStringsWhenFieldsMissing(): void
|
2026-05-17 22:43:47 +00:00
|
|
|
{
|
2026-05-18 16:57:01 +00:00
|
|
|
$this->ollama->method('generateWithImage')->willReturn('I cannot read the nameplate.');
|
2026-05-17 22:43:47 +00:00
|
|
|
|
|
|
|
|
$result = $this->agent->analyze('/tmp/photo.jpg');
|
|
|
|
|
|
2026-05-18 16:57:01 +00:00
|
|
|
$this->assertSame('', $result['manufacturer']);
|
|
|
|
|
$this->assertSame('', $result['modelName']);
|
|
|
|
|
$this->assertSame('', $result['modelNumber']);
|
|
|
|
|
$this->assertSame('', $result['serial']);
|
2026-05-17 22:43:47 +00:00
|
|
|
}
|
|
|
|
|
}
|