Files
silo-server/internal/overlays/summary_test.go
T

261 lines
7.7 KiB
Go

package overlays
import (
"testing"
"github.com/Silo-Server/silo-server/internal/models"
)
func TestNormalizeVideoCodec(t *testing.T) {
cases := []struct {
name string
file *models.MediaFile
want string
}{
{"empty", &models.MediaFile{}, ""},
{"file codec hevc", &models.MediaFile{CodecVideo: "hevc"}, "H.265"},
{"file codec h264", &models.MediaFile{CodecVideo: "h264"}, "H.264"},
{"file codec av1", &models.MediaFile{CodecVideo: "av1"}, "AV1"},
{"file codec vp9", &models.MediaFile{CodecVideo: "VP9"}, "VP9"},
{"track overrides file", &models.MediaFile{
CodecVideo: "h264",
VideoTracks: []models.VideoTrack{{Codec: "hevc"}},
}, "H.265"},
{"empty track codec falls back to file codec", &models.MediaFile{
CodecVideo: "h265",
VideoTracks: []models.VideoTrack{{Codec: ""}},
}, "H.265"},
{"unknown codec passes through uppercased", &models.MediaFile{CodecVideo: "foobar"}, "FOOBAR"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := normalizeVideoCodec(tc.file); got != tc.want {
t.Errorf("normalizeVideoCodec = %q, want %q", got, tc.want)
}
})
}
}
func TestNormalizeAudioChannels(t *testing.T) {
cases := []struct {
name string
file *models.MediaFile
want string
}{
{"empty", &models.MediaFile{}, ""},
{"file channels 2", &models.MediaFile{AudioChannels: 2}, "Stereo"},
{"file channels 6", &models.MediaFile{AudioChannels: 6}, "5.1"},
{"file channels 8", &models.MediaFile{AudioChannels: 8}, "7.1"},
{"file channels 1", &models.MediaFile{AudioChannels: 1}, "Mono"},
{"unusual channel count", &models.MediaFile{AudioChannels: 10}, "10ch"},
{"default track wins over higher", &models.MediaFile{
AudioTracks: []models.AudioTrack{
{Channels: 8},
{Channels: 2, Default: true},
},
}, "Stereo"},
{"highest non-default when no default", &models.MediaFile{
AudioTracks: []models.AudioTrack{
{Channels: 2},
{Channels: 8},
},
}, "7.1"},
{"track beats file fallback", &models.MediaFile{
AudioChannels: 2,
AudioTracks: []models.AudioTrack{{Channels: 6, Default: true}},
}, "5.1"},
{"default track with 0 channels does not shadow earlier non-default", &models.MediaFile{
AudioTracks: []models.AudioTrack{
{Channels: 8},
{Channels: 0, Default: true},
},
}, "7.1"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := normalizeAudioChannels(tc.file); got != tc.want {
t.Errorf("normalizeAudioChannels = %q, want %q", got, tc.want)
}
})
}
}
func TestNormalizeContainer(t *testing.T) {
cases := []struct {
in, want string
}{
{"", ""},
{"mkv", "MKV"},
{"MP4", "MP4"},
{" mov ", "MOV"},
}
for _, tc := range cases {
if got := normalizeContainer(tc.in); got != tc.want {
t.Errorf("normalizeContainer(%q) = %q, want %q", tc.in, got, tc.want)
}
}
}
func TestNormalizeAspectRatio(t *testing.T) {
cases := []struct {
name string
file *models.MediaFile
want string
}{
{"empty", &models.MediaFile{}, ""},
{"16:9 string", &models.MediaFile{
VideoTracks: []models.VideoTrack{{AspectRatio: "16:9"}},
}, "16:9"},
{"239:100 normalizes to 2.39:1", &models.MediaFile{
VideoTracks: []models.VideoTrack{{AspectRatio: "239:100"}},
}, "2.39:1"},
{"2.40:1 snaps to 2.39:1", &models.MediaFile{
VideoTracks: []models.VideoTrack{{AspectRatio: "2.40:1"}},
}, "2.39:1"},
{"derives from 1920x1080", &models.MediaFile{
VideoTracks: []models.VideoTrack{{Width: 1920, Height: 1080}},
}, "16:9"},
{"derives from 4096x1716 (cinemascope)", &models.MediaFile{
VideoTracks: []models.VideoTrack{{Width: 4096, Height: 1716}},
}, "2.39:1"},
{"derives from 720x480", &models.MediaFile{
VideoTracks: []models.VideoTrack{{Width: 720, Height: 480}},
}, "1.50:1"},
{"malformed ratio falls back to dimensions", &models.MediaFile{
VideoTracks: []models.VideoTrack{{AspectRatio: "garbage", Width: 1920, Height: 1080}},
}, "16:9"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := normalizeAspectRatio(tc.file); got != tc.want {
t.Errorf("normalizeAspectRatio = %q, want %q", got, tc.want)
}
})
}
}
func TestDetectMultiAudio(t *testing.T) {
cases := []struct {
name string
file *models.MediaFile
want bool
}{
{"empty", &models.MediaFile{}, false},
{"single track", &models.MediaFile{
AudioTracks: []models.AudioTrack{{Language: "eng"}},
}, false},
{"two same language", &models.MediaFile{
AudioTracks: []models.AudioTrack{{Language: "eng"}, {Language: "eng"}},
}, false},
{"two distinct", &models.MediaFile{
AudioTracks: []models.AudioTrack{{Language: "eng"}, {Language: "jpn"}},
}, true},
{"undefined ignored", &models.MediaFile{
AudioTracks: []models.AudioTrack{{Language: "und"}, {Language: ""}, {Language: "eng"}},
}, false},
{"three distinct returns true at 2", &models.MediaFile{
AudioTracks: []models.AudioTrack{{Language: "eng"}, {Language: "spa"}, {Language: "fre"}},
}, true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := detectMultiAudio(tc.file); got != tc.want {
t.Errorf("detectMultiAudio = %v, want %v", got, tc.want)
}
})
}
}
func TestDetectMultiSub(t *testing.T) {
cases := []struct {
name string
file *models.MediaFile
want bool
}{
{"empty", &models.MediaFile{}, false},
{"one embedded", &models.MediaFile{
SubtitleTracks: []models.SubtitleTrack{{Language: "eng"}},
}, true},
{"one external", &models.MediaFile{
ExternalSubtitles: []models.ExternalSubtitle{{Language: "eng"}},
}, true},
{"both present", &models.MediaFile{
SubtitleTracks: []models.SubtitleTrack{{Language: "eng"}},
ExternalSubtitles: []models.ExternalSubtitle{{Language: "jpn"}},
}, true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := detectMultiSub(tc.file); got != tc.want {
t.Errorf("detectMultiSub = %v, want %v", got, tc.want)
}
})
}
}
func TestNormalizeReleaseType(t *testing.T) {
cases := []struct {
path, want string
}{
{"", ""},
{"/movies/Foo.2020.1080p.REMUX.mkv", "REMUX"},
{"/movies/Foo.2020.1080p.WEB-DL.mkv", "WEB-DL"},
{"/movies/Foo.2020.1080p.BluRay.mkv", "BluRay"},
{"/movies/Foo.2020.480p.DVDRip.mkv", "DVD"},
// Word-boundary check: "dvd" embedded in a benign word must not match.
{"/movies/goodvideos/Foo.2020.1080p.mkv", ""},
}
for _, tc := range cases {
if got := normalizeReleaseType(tc.path); got != tc.want {
t.Errorf("normalizeReleaseType(%q) = %q, want %q", tc.path, got, tc.want)
}
}
}
func TestBuildSummaryAggregatesNewFields(t *testing.T) {
file := &models.MediaFile{
Resolution: "1080p",
CodecVideo: "hevc",
Container: "mkv",
VideoTracks: []models.VideoTrack{{
Codec: "hevc",
Width: 1920,
Height: 1080,
AspectRatio: "16:9",
}},
AudioTracks: []models.AudioTrack{
{Language: "eng", Channels: 8, Default: true, Title: "Atmos"},
{Language: "spa", Channels: 6},
},
SubtitleTracks: []models.SubtitleTrack{{Language: "eng"}},
}
got := BuildSummary([]*models.MediaFile{file})
if got == nil {
t.Fatal("expected non-nil summary")
}
if got.Resolution != "1080p" {
t.Errorf("Resolution = %q, want %q", got.Resolution, "1080p")
}
if got.VideoCodec != "H.265" {
t.Errorf("VideoCodec = %q, want %q", got.VideoCodec, "H.265")
}
if got.AudioChannels != "7.1" {
t.Errorf("AudioChannels = %q, want %q", got.AudioChannels, "7.1")
}
if got.Container != "MKV" {
t.Errorf("Container = %q, want %q", got.Container, "MKV")
}
if got.AspectRatio != "16:9" {
t.Errorf("AspectRatio = %q, want %q", got.AspectRatio, "16:9")
}
if got.Audio != "Atmos" {
t.Errorf("Audio = %q, want %q", got.Audio, "Atmos")
}
if !got.MultiAudio {
t.Error("MultiAudio = false, want true")
}
if !got.MultiSub {
t.Error("MultiSub = false, want true")
}
}