Files
go-mxl-player/internal/playback/session_plan_test.go
T
Dmitry Sergeev a4cbed188a session plan
2026-08-31 23:53:14 +03:00

131 lines
3.8 KiB
Go

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")
}
}