create(['username' => 'lazycollections']); $collections = Collection::factory()->for($user)->count(3)->sequence( ['title' => 'First collection', 'profile_order' => 0], ['title' => 'Featured collection', 'is_featured' => true, 'profile_order' => 1], ['title' => 'Third collection', 'profile_order' => 2], )->create(); return [$user, $collections]; } it('omits collection production from the default Posts SSR request', function (): void { [$user] = profileCollectionsLazyFixture(); $collectionQueries = []; DB::listen(function ($query) use (&$collectionQueries): void { if (preg_match('/(?:from|join) [`"]?(collections|collection_artwork)[`"]?/i', $query->sql)) { $collectionQueries[] = $query->sql; } }); $this->get('/@' . $user->username) ->assertOk() ->assertInertia(fn (AssertableInertia $page) => $page ->where('initialTab', 'posts') ->where('collections', null)); expect($collectionQueries)->toBe([]); }); it('keeps collections in direct Collections SSR with the existing order and limit', function (): void { [$user, $collections] = profileCollectionsLazyFixture(); $this->get('/@' . $user->username . '/collections') ->assertOk() ->assertInertia(fn (AssertableInertia $page) => $page ->where('initialTab', 'collections') ->has('collections', 3) ->where('collections.0.id', $collections[1]->id) ->where('collections.1.id', $collections[0]->id) ->where('collections.2.id', $collections[2]->id)); }); it('returns the same profile card payload from the lazy endpoint without tracking a view', function (): void { [$user] = profileCollectionsLazyFixture(); $before = (int) ($user->profile_views_count ?? 0); $expected = app(\App\Services\CollectionService::class) ->mapCollectionCardPayloads( app(\App\Services\CollectionService::class)->getProfileCollections($user, null), false, null, ); $this->getJson('/api/profile/' . $user->username . '/collections') ->assertOk() ->assertJson(['data' => $expected]); expect((int) $user->fresh()->profile_views_count)->toBe($before); }); it('keeps private collections owner-only in the lazy endpoint', function (): void { [$user] = profileCollectionsLazyFixture(); Collection::factory()->for($user)->create([ 'title' => 'Private collection', 'visibility' => Collection::VISIBILITY_PRIVATE, ]); $this->getJson('/api/profile/' . $user->username . '/collections') ->assertOk() ->assertJsonMissing(['title' => 'Private collection']); $this->actingAs($user) ->getJson('/api/profile/' . $user->username . '/collections') ->assertOk() ->assertJsonFragment(['title' => 'Private collection']); }); it('preserves the authenticated non-owner payload and viewer state', function (): void { [$user] = profileCollectionsLazyFixture(); $viewer = User::factory()->create(['username' => 'collectionviewer']); $service = app(\App\Services\CollectionService::class); $expected = $service->mapCollectionCardPayloads( $service->getProfileCollections($user, $viewer), false, $viewer, ); $this->actingAs($viewer) ->getJson('/api/profile/' . $user->username . '/collections') ->assertOk() ->assertJson(['data' => $expected]); }); it('keeps the existing first twelve collection order', function (): void { $user = User::factory()->create(['username' => 'collectionlimit']); Collection::factory()->for($user)->count(13)->sequence( fn ($sequence) => ['profile_order' => $sequence->index], )->create(); $service = app(\App\Services\CollectionService::class); $expectedIds = $service->getProfileCollections($user, null)->pluck('id')->all(); expect($expectedIds)->toHaveCount(12); $this->getJson('/api/profile/' . $user->username . '/collections') ->assertOk() ->assertJsonPath('data.*.id', $expectedIds); }); it('bulk-loads manual collection covers without mapper fan-out', function (): void { $user = User::factory()->create(['username' => 'collectionmapper']); $collections = Collection::factory()->for($user)->count(3)->create(); foreach ($collections as $index => $collection) { $artwork = Artwork::factory()->for($user)->create([ 'title' => 'Collection artwork ' . $index, ]); $collection->artworks()->attach($artwork->id, ['order_num' => 1]); } $service = app(\App\Services\CollectionService::class); $loaded = $service->getProfileCollections($user, null); DB::flushQueryLog(); DB::enableQueryLog(); $payload = $service->mapCollectionCardPayloads($loaded, false, null); $queries = DB::getQueryLog(); expect($payload)->toHaveCount(3) ->and($queries)->toHaveCount(2) ->and(collect($payload)->pluck('cover_artwork_id')->filter()->count())->toBe(3); }); it('keeps manual mapper query counts bounded from one through twelve collections', function (): void { $user = User::factory()->create(['username' => 'collectionmatrix']); $collections = Collection::factory()->for($user)->count(12)->create(); foreach ($collections as $index => $collection) { $artwork = Artwork::factory()->for($user)->create(['title' => 'Matrix artwork ' . $index]); $collection->artworks()->attach($artwork->id, ['order_num' => 1]); } $service = app(\App\Services\CollectionService::class); $matrix = []; foreach ([1, 3, 6, 12] as $size) { DB::flushQueryLog(); DB::enableQueryLog(); $loaded = $service->getProfileCollections($user, null, $size); $producer = count(DB::getQueryLog()); DB::flushQueryLog(); $service->mapCollectionCardPayloads($loaded, false, null); $mapper = count(DB::getQueryLog()); $matrix[$size] = [$producer, $mapper, $producer + $mapper]; } expect($matrix[1][1])->toBe($matrix[3][1]) ->and($matrix[3][1])->toBe($matrix[6][1]) ->and($matrix[6][1])->toBe($matrix[12][1]); }); it('does not alter the smart collection cover and rule-output path', function (): void { $user = User::factory()->create(['username' => 'smartguard']); $artwork = Artwork::factory()->for($user)->create(['title' => 'Smart result']); $collection = Collection::factory()->for($user)->create([ 'mode' => Collection::MODE_SMART, 'smart_rules_json' => ['match' => 'all', 'rules' => [['field' => 'is_featured', 'value' => true]]], ]); $smart = Mockery::mock(SmartCollectionService::class); $smart->shouldReceive('firstArtwork')->once()->withArgs(fn (Collection $actual, bool $ownerView): bool => $actual->is($collection) && $ownerView === false)->andReturn($artwork); $smart->shouldReceive('smartSummary')->once()->andReturn('smart rule summary'); app()->instance(SmartCollectionService::class, $smart); $payload = app(\App\Services\CollectionService::class)->mapCollectionCardPayloads( new \Illuminate\Database\Eloquent\Collection([$collection->load('user')]), false, null, ); expect($payload[0]['cover_artwork_id'])->toBe($artwork->id) ->and($payload[0]['smart_summary'])->toBe('smart rule summary'); });