create(); $secondUser = User::factory()->create(); $service = app(CollectionService::class); Collection::factory()->for($firstUser)->create([ 'title' => 'Portal Vault', 'slug' => 'portal-vault', ]); expect($service->makeUniqueSlugForUser($firstUser, 'Portal Vault'))->toBe('portal-vault-2'); expect($service->makeUniqueSlugForUser($secondUser, 'Portal Vault'))->toBe('portal-vault'); }); it('falls back to the first attached artwork when an explicit cover is removed', function (): void { $user = User::factory()->create(); $collection = Collection::factory()->for($user)->create(); $firstArtwork = Artwork::factory()->for($user)->create(); $secondArtwork = Artwork::factory()->for($user)->create(); $service = app(CollectionService::class); $service->attachArtworks($collection, $user, [$firstArtwork->id, $secondArtwork->id]); $collection->refresh(); $service->updateCollection($collection->loadMissing('user'), [ 'title' => $collection->title, 'slug' => $collection->slug, 'description' => $collection->description, 'visibility' => $collection->visibility, 'sort_mode' => $collection->sort_mode, 'cover_artwork_id' => $secondArtwork->id, ]); $collection->refresh(); expect($collection->resolvedCoverArtwork()?->id)->toBe($secondArtwork->id); $service->removeArtwork($collection->loadMissing('user'), $secondArtwork); $collection->refresh(); expect($collection->cover_artwork_id)->toBeNull(); expect($collection->resolvedCoverArtwork()?->id)->toBe($firstArtwork->id); }); it('keeps artworks_count in sync while attaching and removing artworks', function (): void { $user = User::factory()->create(); $collection = Collection::factory()->for($user)->create(['artworks_count' => 0]); $artworkA = Artwork::factory()->for($user)->create(); $artworkB = Artwork::factory()->for($user)->create(); $service = app(CollectionService::class); $service->attachArtworks($collection, $user, [$artworkA->id, $artworkB->id]); $collection->refresh(); expect($collection->artworks_count)->toBe(2); $service->removeArtwork($collection->loadMissing('user'), $artworkA); $collection->refresh(); expect($collection->artworks_count)->toBe(1); expect($collection->artworks()->pluck('artworks.id')->all())->toBe([$artworkB->id]); }); it('builds a human readable smart summary for medium rules', function (): void { $service = app(SmartCollectionService::class); $summary = $service->smartSummary([ 'match' => 'all', 'sort' => 'newest', 'rules' => [ [ 'field' => 'medium', 'operator' => 'equals', 'value' => 'wallpapers', ], ], ]); expect($summary)->toBe('Includes artworks in medium wallpapers.'); }); it('builds a human readable smart summary for style and color rules', function (): void { $service = app(SmartCollectionService::class); $summary = $service->smartSummary([ 'match' => 'any', 'sort' => 'newest', 'rules' => [ [ 'field' => 'style', 'operator' => 'equals', 'value' => 'digital painting', ], [ 'field' => 'color', 'operator' => 'equals', 'value' => 'blue tones', ], ], ]); expect($summary)->toBe('Includes artworks matching style digital painting or using color palette blue tones.'); }); it('builds a human readable smart summary for mature rules', function (): void { $service = app(SmartCollectionService::class); $summary = $service->smartSummary([ 'match' => 'all', 'sort' => 'newest', 'rules' => [ [ 'field' => 'is_mature', 'operator' => 'equals', 'value' => true, ], ], ]); expect($summary)->toBe('Includes artworks marked as mature artworks.'); });