package playback import ( "errors" "testing" ) func TestBuildSessionPlan(t *testing.T) { base := validCommandSession() stoppedVideo := stoppedFeed(base.Video) stoppedAudio := stoppedFeed(base.Audio) independent := func(video, audio FeedConfig) SessionPlan { return SessionPlan{ Topology: TopologyIndependent, Video: video, Audio: audio, Sync: SyncPairConfig{Video: stoppedFeed(video), Audio: stoppedFeed(audio)}, } } idle := func(video, audio FeedConfig) SessionPlan { return SessionPlan{ Topology: TopologyIdle, Video: stoppedFeed(video), Audio: stoppedFeed(audio), Sync: SyncPairConfig{Video: stoppedFeed(video), Audio: stoppedFeed(audio)}, } } tests := []struct { name string desired SessionConfig canSync SyncPredicate want SessionPlan }{ { name: "both active with sync disabled stay independent", desired: func() SessionConfig { c := base; c.SyncRequested = false; return c }(), canSync: func(FeedConfig, FeedConfig) bool { return true }, want: independent(base.Video, base.Audio), }, { name: "eligible requested pair is synchronized", desired: base, canSync: func(FeedConfig, FeedConfig) bool { return true }, want: SessionPlan{ Topology: TopologySynchronized, Video: stoppedVideo, Audio: stoppedAudio, Sync: SyncPairConfig{Video: base.Video, Audio: base.Audio}, }, }, { name: "ineligible requested pair remains independent", desired: base, canSync: func(FeedConfig, FeedConfig) bool { return false }, want: independent(base.Video, base.Audio), }, { name: "nil capability remains independent", desired: base, want: independent(base.Video, base.Audio), }, { name: "video only", desired: func() SessionConfig { c := base; c.Audio.Active = false; return c }(), canSync: func(FeedConfig, FeedConfig) bool { return true }, want: independent(base.Video, stoppedAudio), }, { name: "audio only", desired: func() SessionConfig { c := base; c.Video.Active = false; return c }(), canSync: func(FeedConfig, FeedConfig) bool { return true }, want: independent(stoppedVideo, base.Audio), }, { name: "stopped feeds are idle and retain configuration", desired: func() SessionConfig { c := base; c.Video.Active = false; c.Audio.Active = false; return c }(), canSync: func(FeedConfig, FeedConfig) bool { return true }, want: idle(stoppedVideo, stoppedAudio), }, { name: "empty session is idle", desired: func() SessionConfig { c := base; c.Video = FeedConfig{}; c.Audio = FeedConfig{}; return c }(), canSync: func(FeedConfig, FeedConfig) bool { return true }, want: idle(FeedConfig{}, FeedConfig{}), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := BuildSessionPlan(tt.desired, tt.canSync) if err != nil { t.Fatalf("BuildSessionPlan() error = %v", err) } if got != tt.want { t.Fatalf("BuildSessionPlan() = %#v, want %#v", got, tt.want) } }) } } func TestBuildSessionPlanRejectsInvalidSession(t *testing.T) { desired := validCommandSession() desired.Video = FeedConfig{UUID: "video", Active: true} got, err := BuildSessionPlan(desired, nil) if !errors.Is(err, ErrFeedDomainRequired) { t.Fatalf("BuildSessionPlan() error = %v, want %v", err, ErrFeedDomainRequired) } if got != (SessionPlan{}) { t.Fatalf("BuildSessionPlan() = %#v, want zero plan", got) } } func TestBuildSessionPlanPassesCompleteFeedsToPredicate(t *testing.T) { desired := validCommandSession() called := false _, err := BuildSessionPlan(desired, func(video, audio FeedConfig) bool { called = true if video != desired.Video || audio != desired.Audio { t.Fatalf("predicate feeds = %#v %#v", video, audio) } return true }) if err != nil { t.Fatal(err) } if !called { t.Fatal("sync predicate was not called") } }