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; }); });