getJson("/api/profile/{$user->username}/artworks"); $firstPage->assertOk(); $firstPage->assertJsonCount(24, 'data'); $nextCursor = $firstPage->json('next_cursor'); expect($nextCursor)->not->toBeNull(); $secondPage = $this->getJson("/api/profile/{$user->username}/artworks?cursor={$nextCursor}"); $secondPage->assertOk(); $secondPage->assertJsonCount(2, 'data'); }); it('supports cursor pagination for stats-sorted profile artworks', function () { [$user] = seedProfileArtworksApiFixtures(26); $firstPage = $this->getJson("/api/profile/{$user->username}/artworks?sort=views"); $firstPage->assertOk(); $firstPage->assertJsonCount(24, 'data'); $nextCursor = $firstPage->json('next_cursor'); expect($nextCursor)->not->toBeNull(); $secondPage = $this->getJson("/api/profile/{$user->username}/artworks?sort=views&cursor={$nextCursor}"); $secondPage->assertOk(); $secondPage->assertJsonCount(2, 'data'); }); it('falls back to the first page when a legacy profile artworks cursor is missing id', function () { [$user] = seedProfileArtworksApiFixtures(26); $legacyCursor = new Cursor([ 'published_at' => Artwork::query()->orderByDesc('published_at')->value('published_at')?->format(DATE_ATOM), ]); $response = $this->getJson("/api/profile/{$user->username}/artworks?cursor={$legacyCursor->encode()}"); $response->assertOk(); $response->assertJsonCount(24, 'data'); expect($response->json('next_cursor'))->not->toBeNull(); }); function seedProfileArtworksApiFixtures(int $count): array { $user = User::factory()->create([ 'name' => 'Profile Artist', 'username' => 'profileartist', 'email' => 'profileartist@example.test', ]); $contentType = ContentType::create([ 'name' => 'Skins', 'slug' => 'skins', 'description' => 'Skins content type', ]); $category = Category::create([ 'content_type_id' => $contentType->id, 'name' => 'Winamp', 'slug' => 'winamp', 'description' => 'Winamp skins', 'is_active' => true, 'sort_order' => 1, ]); for ($i = 1; $i <= $count; $i++) { $artwork = Artwork::factory() ->for($user) ->create([ 'title' => 'Profile Artwork ' . $i, 'slug' => 'profile-artwork-' . $i, 'published_at' => now()->subMinutes($i), ]); $artwork->categories()->attach($category->id); ArtworkStats::create([ 'artwork_id' => $artwork->id, 'views' => $count - $i, 'downloads' => $i, 'favorites' => $i, 'rating_avg' => 0, 'rating_count' => 0, 'comments_count' => 0, 'shares_count' => 0, 'ranking_score' => $count - $i, 'engagement_velocity' => 0, 'shares_24h' => 0, 'comments_24h' => 0, 'favourites_24h' => 0, 'heat_score' => $count - $i, 'heat_score_updated_at' => null, 'views_1h' => 0, 'favourites_1h' => 0, 'comments_1h' => 0, 'shares_1h' => 0, 'downloads_1h' => 0, ]); } return [$user, $contentType, $category]; }