110 lines
3.5 KiB
PHP
110 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\Artwork;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use function Pest\Laravel\actingAs;
|
|
use function Pest\Laravel\getJson;
|
|
use function Pest\Laravel\postJson;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function (): void {
|
|
config()->set('vision.vector_gateway.enabled', true);
|
|
config()->set('vision.vector_gateway.base_url', 'https://vision.klevze.net');
|
|
config()->set('vision.vector_gateway.api_key', 'test-key');
|
|
config()->set('vision.vector_gateway.search_endpoint', '/vectors/search');
|
|
config()->set('cdn.files_url', 'https://files.skinbase.org');
|
|
config()->set('app.url', 'https://skinbase.test');
|
|
Storage::fake('public');
|
|
});
|
|
|
|
it('returns AI similar artworks for a public artwork', function (): void {
|
|
$source = Artwork::factory()->create([
|
|
'title' => 'Source artwork',
|
|
'hash' => 'aabbcc112233',
|
|
'thumb_ext' => 'webp',
|
|
'is_public' => true,
|
|
'is_approved' => true,
|
|
'published_at' => now()->subHour(),
|
|
]);
|
|
|
|
$match = Artwork::factory()->create([
|
|
'title' => 'AI match',
|
|
'hash' => 'ddeeff445566',
|
|
'thumb_ext' => 'webp',
|
|
'is_public' => true,
|
|
'is_approved' => true,
|
|
'published_at' => now()->subHour(),
|
|
]);
|
|
|
|
Http::fake([
|
|
'https://vision.klevze.net/vectors/search' => Http::response([
|
|
'results' => [
|
|
['id' => $source->id, 'score' => 1.0],
|
|
['id' => $match->id, 'score' => 0.91234],
|
|
],
|
|
], 200),
|
|
]);
|
|
|
|
$response = getJson('/api/art/' . $source->id . '/similar-ai');
|
|
|
|
$response->assertOk()
|
|
->assertJsonPath('data.0.id', $match->id)
|
|
->assertJsonPath('data.0.source', 'vector_gateway')
|
|
->assertJsonPath('meta.artwork_id', $source->id)
|
|
->assertJsonCount(1, 'data');
|
|
});
|
|
|
|
it('returns 404 for missing similar-ai source artwork', function (): void {
|
|
getJson('/api/art/999999/similar-ai')
|
|
->assertStatus(404)
|
|
->assertJsonPath('error', 'Artwork not found');
|
|
});
|
|
|
|
it('searches by uploaded image through the vector gateway', function (): void {
|
|
$user = User::factory()->create();
|
|
$match = Artwork::factory()->create([
|
|
'title' => 'Reverse image match',
|
|
'hash' => '112233445566',
|
|
'thumb_ext' => 'webp',
|
|
'is_public' => true,
|
|
'is_approved' => true,
|
|
'published_at' => now()->subHour(),
|
|
]);
|
|
|
|
Http::fake([
|
|
'https://vision.klevze.net/vectors/search' => Http::response([
|
|
'results' => [
|
|
['id' => $match->id, 'score' => 0.88765],
|
|
],
|
|
], 200),
|
|
]);
|
|
|
|
actingAs($user);
|
|
|
|
$response = postJson('/api/search/image', [
|
|
'image' => UploadedFile::fake()->image('query.png', 640, 480),
|
|
'limit' => 12,
|
|
]);
|
|
|
|
$response->assertOk()
|
|
->assertJsonPath('data.0.id', $match->id)
|
|
->assertJsonPath('data.0.source', 'vector_gateway')
|
|
->assertJsonPath('meta.limit', 12);
|
|
|
|
Http::assertSent(function ($request): bool {
|
|
$payload = json_decode($request->body(), true);
|
|
|
|
return $request->url() === 'https://vision.klevze.net/vectors/search'
|
|
&& $request->hasHeader('X-API-Key', 'test-key')
|
|
&& is_array($payload)
|
|
&& str_contains((string) ($payload['url'] ?? ''), '/storage/ai-search/tmp/')
|
|
&& ($payload['limit'] ?? null) === 12;
|
|
});
|
|
}); |