Files
go-mxl-player/cmd/mxl-player/playlist_file_test.go
T
2026-09-01 21:58:20 +03:00

175 lines
5.1 KiB
Go

package main
import (
"errors"
"os"
"path/filepath"
"strings"
"testing"
"time"
"mxl-player/internal/playback"
)
func TestDecodePlaylistFile(t *testing.T) {
input := `{
"loop": true,
"entries": [
{
"name": "sync",
"video": {"domain": "/video", "uuid": "video-1"},
"audio": {"domain": "/audio", "uuid": "audio-1"},
"sync": true,
"duration": "10s"
},
{
"name": "video",
"video": {"domain": "/other-video", "uuid": "video-2"},
"duration": "250ms"
},
{
"name": "audio",
"audio": {"domain": "/other-audio", "uuid": "audio-3"},
"duration": "1m"
},
{
"name": "manual",
"video": {"domain": "/video", "uuid": "video-4"}
}
]
}`
got, err := decodePlaylistFile(strings.NewReader(input))
if err != nil {
t.Fatalf("decodePlaylistFile() error = %v", err)
}
want := playback.Playlist{
Loop: true,
Entries: []playback.PlaylistEntry{
{
Name: "sync",
Video: playback.PlaylistFeed{Domain: "/video", UUID: "video-1"},
Audio: playback.PlaylistFeed{Domain: "/audio", UUID: "audio-1"},
SyncRequested: true,
Duration: 10 * time.Second,
},
{
Name: "video",
Video: playback.PlaylistFeed{Domain: "/other-video", UUID: "video-2"},
Duration: 250 * time.Millisecond,
},
{
Name: "audio",
Audio: playback.PlaylistFeed{Domain: "/other-audio", UUID: "audio-3"},
Duration: time.Minute,
},
{
Name: "manual",
Video: playback.PlaylistFeed{Domain: "/video", UUID: "video-4"},
},
},
}
if len(got.Entries) != len(want.Entries) || got.Loop != want.Loop {
t.Fatalf("decodePlaylistFile() = %#v, want %#v", got, want)
}
for index := range want.Entries {
if got.Entries[index] != want.Entries[index] {
t.Fatalf("entry %d = %#v, want %#v", index, got.Entries[index], want.Entries[index])
}
}
}
func TestDecodePlaylistFileAllowsEmptyPlaylist(t *testing.T) {
got, err := decodePlaylistFile(strings.NewReader(`{"entries": []}`))
if err != nil {
t.Fatalf("decodePlaylistFile() error = %v", err)
}
if len(got.Entries) != 0 || got.Loop {
t.Fatalf("decodePlaylistFile() = %#v, want empty non-looping playlist", got)
}
}
func TestDecodePlaylistFileRejectsInvalidInput(t *testing.T) {
tests := []struct {
name string
input string
wantErr error
wantText string
}{
{name: "empty input", input: ``, wantText: "decode JSON"},
{name: "malformed JSON", input: `{"entries": [`, wantText: "decode JSON"},
{name: "unknown field", input: `{"unknown": true}`, wantText: "unknown field"},
{name: "multiple roots", input: `{"entries": []} {"entries": []}`, wantText: "multiple root values"},
{
name: "invalid duration",
input: `{"entries":[{"video":{"domain":"/video","uuid":"video"},"duration":"later"}]}`,
wantText: `playlist entry 0 duration "later"`,
},
{
name: "negative duration",
input: `{"entries":[{"video":{"domain":"/video","uuid":"video"},"duration":"-1s"}]}`,
wantErr: playback.ErrPlaylistDurationNegative,
},
{
name: "UUID without domain",
input: `{"entries":[{"video":{"uuid":"video"}}]}`,
wantErr: playback.ErrFeedDomainRequired,
},
{
name: "domain without UUID",
input: `{"entries":[{"audio":{"domain":"/audio"}}]}`,
wantErr: playback.ErrPlaylistFeedUUIDRequired,
},
{
name: "empty entry",
input: `{"entries":[{}]}`,
wantErr: playback.ErrPlaylistEntryEmpty,
},
{
name: "sync with one feed",
input: `{"entries":[{"video":{"domain":"/video","uuid":"video"},"sync":true}]}`,
wantErr: playback.ErrPlaylistSyncFeedsRequired,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
_, err := decodePlaylistFile(strings.NewReader(test.input))
if err == nil {
t.Fatal("decodePlaylistFile() error = nil")
}
if test.wantErr != nil && !errors.Is(err, test.wantErr) {
t.Fatalf("decodePlaylistFile() error = %v, want %v", err, test.wantErr)
}
if test.wantText != "" && !strings.Contains(err.Error(), test.wantText) {
t.Fatalf("decodePlaylistFile() error = %q, want text %q", err, test.wantText)
}
})
}
}
func TestLoadPlaylistFile(t *testing.T) {
directory := t.TempDir()
path := filepath.Join(directory, "playlist.json")
input := []byte(`{"loop":true,"entries":[{"audio":{"domain":"/audio","uuid":"audio"}}]}`)
if err := os.WriteFile(path, input, 0o600); err != nil {
t.Fatalf("WriteFile() error = %v", err)
}
got, err := loadPlaylistFile(path)
if err != nil {
t.Fatalf("loadPlaylistFile() error = %v", err)
}
if !got.Loop || len(got.Entries) != 1 || got.Entries[0].Audio.UUID != "audio" {
t.Fatalf("loadPlaylistFile() = %#v", got)
}
}
func TestLoadPlaylistFileIncludesPathInErrors(t *testing.T) {
path := filepath.Join(t.TempDir(), "missing.json")
_, err := loadPlaylistFile(path)
if err == nil || !strings.Contains(err.Error(), path) {
t.Fatalf("loadPlaylistFile() error = %v, want path %q", err, path)
}
}