'track_1', 'position' => 1, 'title' => 'Test Track', 'duration' => 180, 'preview_url' => null, 'spotify_uri' => 'spotify:track:track_id_123', 'isrc' => null, ]); // Use reflection to test private method $service = new \ReflectionClass(App\Application\Campaign\Services\SpotifyCampaignService::class); $method = $service->getMethod('extractSpotifyId'); $method->setAccessible(true); $extracted = $method->invoke( $service->newInstanceWithoutConstructor(), 'spotify:track:track_id_123' ); expect($extracted)->toBe('track_id_123'); }); it('extracts Spotify ID from URL format', function () { $service = new \ReflectionClass(App\Application\Campaign\Services\SpotifyCampaignService::class); $method = $service->getMethod('extractSpotifyId'); $method->setAccessible(true); $extracted = $method->invoke( $service->newInstanceWithoutConstructor(), 'https://open.spotify.com/track/url_track_id' ); expect($extracted)->toBe('url_track_id'); }); it('returns ID if already just an ID', function () { $service = new \ReflectionClass(App\Application\Campaign\Services\SpotifyCampaignService::class); $method = $service->getMethod('extractSpotifyId'); $method->setAccessible(true); $extracted = $method->invoke( $service->newInstanceWithoutConstructor(), 'just_an_id_123' ); expect($extracted)->toBe('just_an_id_123'); }); it('returns null for null input', function () { $service = new \ReflectionClass(App\Application\Campaign\Services\SpotifyCampaignService::class); $method = $service->getMethod('extractSpotifyId'); $method->setAccessible(true); $extracted = $method->invoke( $service->newInstanceWithoutConstructor(), null ); expect($extracted)->toBeNull(); }); }); describe('Campaign Value Objects', function () { it('creates campaign with tracks', function () { $campaign = Campaign::fromArray([ 'id' => '123', 'slug' => 'test-album', 'artist_name' => 'Test Artist', 'album_title' => 'Test Album', 'description' => 'Test description', 'artwork_url' => 'https://example.com/art.jpg', 'release_date' => '2024-12-01', 'total_saves' => 100, 'track_count' => 2, 'spotify_enabled' => true, 'apple_music_enabled' => true, 'spotify_uri' => 'spotify:album:abc123', 'apple_music_id' => 'apple123', 'tracks' => [ [ 'id' => 'track_1', 'position' => 1, 'title' => 'Track 1', 'duration' => 180, 'preview_url' => null, 'spotify_uri' => 'spotify:track:track1', 'isrc' => 'US1234567890', ], [ 'id' => 'track_2', 'position' => 2, 'title' => 'Track 2', 'duration' => 200, 'preview_url' => null, 'spotify_uri' => 'spotify:track:track2', 'isrc' => 'US0987654321', ], ], 'status' => 'active', ]); expect($campaign->id)->toBe('123'); expect($campaign->slug)->toBe('test-album'); expect($campaign->spotify_enabled)->toBeTrue(); expect($campaign->tracks)->toHaveCount(2); expect($campaign->tracks[0]->title)->toBe('Track 1'); }); it('validates campaign status methods', function () { $activeCampaign = Campaign::fromArray([ 'id' => '1', 'slug' => 'active', 'artist_name' => 'Artist', 'album_title' => 'Album', 'description' => null, 'artwork_url' => null, 'release_date' => '2025-12-01', 'total_saves' => 0, 'track_count' => 0, 'spotify_enabled' => false, 'apple_music_enabled' => false, 'spotify_uri' => null, 'apple_music_id' => null, 'status' => 'active', ]); expect($activeCampaign->isActive())->toBeTrue(); expect($activeCampaign->hasReleased())->toBeFalse(); }); it('creates campaign track value object', function () { $track = CampaignTrack::fromArray([ 'id' => 'track_123', 'position' => 1, 'title' => 'Amazing Song', 'duration' => 240, 'preview_url' => 'https://example.com/preview.mp3', 'spotify_id' => 'abc123', 'apple_music_id' => 'apple456', ]); expect($track->id)->toBe('track_123'); expect($track->position)->toBe(1); expect($track->title)->toBe('Amazing Song'); expect($track->duration)->toBe(240); expect($track->spotify_id)->toBe('abc123'); expect($track->apple_music_id)->toBe('apple456'); }); });