package playback import ( "errors" "testing" "time" ) func TestPlaylistEntryValidate(t *testing.T) { tests := []struct { name string entry PlaylistEntry wantErr error }{ {name: "video only", entry: PlaylistEntry{Video: PlaylistFeed{Domain: "video-domain", UUID: "video"}}}, {name: "audio only", entry: PlaylistEntry{Audio: PlaylistFeed{Domain: "audio-domain", UUID: "audio"}}}, { name: "independent feeds from different domains", entry: PlaylistEntry{ Video: PlaylistFeed{Domain: "video-domain", UUID: "video"}, Audio: PlaylistFeed{Domain: "audio-domain", UUID: "audio"}, }, }, { name: "synchronized pair", entry: PlaylistEntry{ Video: PlaylistFeed{Domain: "domain", UUID: "video"}, Audio: PlaylistFeed{Domain: "domain", UUID: "audio"}, SyncRequested: true, Duration: 10 * time.Second, }, }, {name: "zero duration", entry: PlaylistEntry{Video: PlaylistFeed{Domain: "domain", UUID: "video"}}}, {name: "empty entry", entry: PlaylistEntry{}, wantErr: ErrPlaylistEntryEmpty}, { name: "video UUID without domain", entry: PlaylistEntry{Video: PlaylistFeed{UUID: "video"}}, wantErr: ErrFeedDomainRequired, }, { name: "audio domain without UUID", entry: PlaylistEntry{Audio: PlaylistFeed{Domain: "audio-domain"}}, wantErr: ErrPlaylistFeedUUIDRequired, }, { name: "sync without audio", entry: PlaylistEntry{ Video: PlaylistFeed{Domain: "domain", UUID: "video"}, SyncRequested: true, }, wantErr: ErrPlaylistSyncFeedsRequired, }, { name: "sync without video", entry: PlaylistEntry{ Audio: PlaylistFeed{Domain: "domain", UUID: "audio"}, SyncRequested: true, }, wantErr: ErrPlaylistSyncFeedsRequired, }, { name: "negative duration", entry: PlaylistEntry{ Video: PlaylistFeed{Domain: "domain", UUID: "video"}, Duration: -time.Second, }, wantErr: ErrPlaylistDurationNegative, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { err := test.entry.Validate() if !errors.Is(err, test.wantErr) { t.Fatalf("Validate() error = %v, want %v", err, test.wantErr) } }) } } func TestPlaylistEntrySessionConfig(t *testing.T) { retry := RetryPolicy{MaxAttempts: 3, InitialDelay: time.Second, MaxDelay: 5 * time.Second} entry := PlaylistEntry{ Video: PlaylistFeed{Domain: "video-domain", UUID: "video"}, Audio: PlaylistFeed{Domain: "audio-domain", UUID: "audio"}, SyncRequested: true, Duration: 10 * time.Second, } got := entry.SessionConfig(retry) want := SessionConfig{ Video: FeedConfig{Domain: "video-domain", UUID: "video", Active: true}, Audio: FeedConfig{Domain: "audio-domain", UUID: "audio", Active: true}, SyncRequested: true, Retry: retry, } if got != want { t.Fatalf("SessionConfig() = %#v, want %#v", got, want) } } func TestPlaylistEntrySessionConfigLeavesMissingFeedInactive(t *testing.T) { entry := PlaylistEntry{Video: PlaylistFeed{Domain: "domain", UUID: "video"}} got := entry.SessionConfig(RetryPolicy{}) if !got.Video.Active { t.Fatal("SessionConfig() video is inactive, want active") } if got.Audio != (FeedConfig{}) { t.Fatalf("SessionConfig() audio = %#v, want zero value", got.Audio) } } func TestPlaylistValidate(t *testing.T) { valid := PlaylistEntry{Video: PlaylistFeed{Domain: "domain", UUID: "video"}} tests := []struct { name string playlist Playlist wantErr error }{ {name: "empty playlist", playlist: Playlist{}}, {name: "valid entries", playlist: Playlist{Entries: []PlaylistEntry{valid, valid}, Loop: true}}, { name: "invalid entry", playlist: Playlist{Entries: []PlaylistEntry{ valid, {Audio: PlaylistFeed{UUID: "audio"}}, }}, wantErr: ErrFeedDomainRequired, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { err := test.playlist.Validate() if !errors.Is(err, test.wantErr) { t.Fatalf("Validate() error = %v, want %v", err, test.wantErr) } }) } } func TestPlaylistValidateReportsEntryIndex(t *testing.T) { playlist := Playlist{Entries: []PlaylistEntry{ {Video: PlaylistFeed{Domain: "domain", UUID: "video"}}, {}, }} err := playlist.Validate() if err == nil || err.Error() != "playlist entry 1: playlist entry must contain at least one feed" { t.Fatalf("Validate() error = %v, want indexed entry error", err) } }