183 lines
5.7 KiB
Go
183 lines
5.7 KiB
Go
package recognize
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func intp(n int) *int { return &n }
|
|
|
|
func inputWith(paths ...string) Input {
|
|
files := make([]File, len(paths))
|
|
for i, p := range paths {
|
|
files[i] = File{Path: p, Size: 1 << 20}
|
|
}
|
|
return Input{Files: files}
|
|
}
|
|
|
|
func TestValidateSchema_OK(t *testing.T) {
|
|
in := inputWith("a.mkv", "b.mkv")
|
|
p := Plan{
|
|
Type: MediaSeries,
|
|
Title: "Show",
|
|
Files: []PlanFile{
|
|
{Src: "a.mkv", Role: RoleEpisode, Season: intp(1), Episode: intp(1)},
|
|
{Src: "b.mkv", Role: RoleEpisode, Season: intp(1), Episode: intp(2)},
|
|
},
|
|
}
|
|
if err := validateSchema(&p, in); err != nil {
|
|
t.Fatalf("validateSchema: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateSchema_Errors(t *testing.T) {
|
|
in := inputWith("a.mkv")
|
|
tests := []struct {
|
|
name string
|
|
p Plan
|
|
want string
|
|
}{
|
|
{"empty type", Plan{Title: "x", Files: []PlanFile{{Src: "a.mkv", Role: RoleMain}}}, "type пустое"},
|
|
{"bad type", Plan{Type: "show", Title: "x", Files: []PlanFile{{Src: "a.mkv", Role: RoleMain}}}, "неизвестный type"},
|
|
{"empty title", Plan{Type: MediaMovie, Files: []PlanFile{{Src: "a.mkv", Role: RoleMain}}}, "title пустое"},
|
|
{"no files", Plan{Type: MediaMovie, Title: "x"}, "files пуст"},
|
|
{"bad role", Plan{Type: MediaMovie, Title: "x", Files: []PlanFile{{Src: "a.mkv", Role: "boss"}}}, "неизвестная role"},
|
|
{"empty src", Plan{Type: MediaMovie, Title: "x", Files: []PlanFile{{Src: "", Role: RoleMain}}}, "пустым src"},
|
|
{"unknown src", Plan{Type: MediaMovie, Title: "x", Files: []PlanFile{{Src: "z.mkv", Role: RoleMain}}}, "не найден"},
|
|
{"episode no num", Plan{Type: MediaSeries, Title: "x", Files: []PlanFile{{Src: "a.mkv", Role: RoleEpisode, Season: intp(1)}}}, "без номера episode"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validateSchema(&tt.p, in)
|
|
if err == nil || !strings.Contains(err.Error(), tt.want) {
|
|
t.Errorf("err = %v, want contains %q", err, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParsePlan_FencedJSON(t *testing.T) {
|
|
in := inputWith("film.mkv")
|
|
raw := "Вот результат:\n```json\n{\"type\":\"movie\",\"title\":\"Film\"," +
|
|
"\"files\":[{\"src\":\"film.mkv\",\"role\":\"main\"}]}\n```"
|
|
p, err := parsePlan(raw, in)
|
|
if err != nil {
|
|
t.Fatalf("parsePlan: %v", err)
|
|
}
|
|
if p.Title != "Film" || p.Type != MediaMovie {
|
|
t.Errorf("plan = %+v", p)
|
|
}
|
|
}
|
|
|
|
func TestParsePlan_UnknownFieldTolerated(t *testing.T) {
|
|
in := inputWith("film.mkv")
|
|
raw := `{"type":"movie","title":"Film","extra_field":123,
|
|
"files":[{"src":"film.mkv","role":"main"}]}`
|
|
if _, err := parsePlan(raw, in); err != nil {
|
|
t.Fatalf("unknown field should be tolerated: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestStructuralWarnings_Movie(t *testing.T) {
|
|
twoMains := Plan{Type: MediaMovie, Files: []PlanFile{
|
|
{Role: RoleMain}, {Role: RoleMain},
|
|
}}
|
|
if w := structuralWarnings(twoMains); len(w) != 1 || !strings.Contains(w[0], "ожидался ровно 1") {
|
|
t.Errorf("warnings = %v", w)
|
|
}
|
|
|
|
noMain := Plan{Type: MediaMovie, Files: []PlanFile{{Role: RoleSample}}}
|
|
if w := structuralWarnings(noMain); len(w) != 1 {
|
|
t.Errorf("want 1 warning for 0 mains, got %v", w)
|
|
}
|
|
|
|
clean := Plan{Type: MediaMovie, Files: []PlanFile{{Role: RoleMain}, {Role: RoleSample}}}
|
|
if w := structuralWarnings(clean); len(w) != 0 {
|
|
t.Errorf("clean movie should have no warnings, got %v", w)
|
|
}
|
|
}
|
|
|
|
func TestSeriesWarnings_GapAndDup(t *testing.T) {
|
|
files := []PlanFile{
|
|
{Role: RoleEpisode, Season: intp(1), Episode: intp(1)},
|
|
{Role: RoleEpisode, Season: intp(1), Episode: intp(1)}, // дубль
|
|
{Role: RoleEpisode, Season: intp(1), Episode: intp(4)}, // пропуск 2,3
|
|
}
|
|
w := seriesWarnings(files)
|
|
var dup, gap bool
|
|
for _, s := range w {
|
|
if strings.Contains(s, "дубль") {
|
|
dup = true
|
|
}
|
|
if strings.Contains(s, "пропуск") {
|
|
gap = true
|
|
}
|
|
}
|
|
if !dup || !gap {
|
|
t.Errorf("want dup and gap warnings, got %v", w)
|
|
}
|
|
}
|
|
|
|
func TestSeriesWarnings_Clean(t *testing.T) {
|
|
files := []PlanFile{
|
|
{Role: RoleEpisode, Season: intp(1), Episode: intp(1)},
|
|
{Role: RoleEpisode, Season: intp(1), Episode: intp(2)},
|
|
{Role: RoleEpisode, Season: intp(2), Episode: intp(1)},
|
|
}
|
|
if w := seriesWarnings(files); len(w) != 0 {
|
|
t.Errorf("clean series should have no warnings, got %v", w)
|
|
}
|
|
}
|
|
|
|
func TestConsistencyWarnings(t *testing.T) {
|
|
yearMismatch := consistencyWarnings(
|
|
Plan{Type: MediaMovie, Year: 2001},
|
|
PreParse{Year: 1999},
|
|
)
|
|
if len(yearMismatch) != 1 || !strings.Contains(yearMismatch[0], "год расходится") {
|
|
t.Errorf("warnings = %v", yearMismatch)
|
|
}
|
|
|
|
typeMismatch := consistencyWarnings(
|
|
Plan{Type: MediaMovie},
|
|
PreParse{Season: 2},
|
|
)
|
|
if len(typeMismatch) != 1 || !strings.Contains(typeMismatch[0], "тип расходится") {
|
|
t.Errorf("warnings = %v", typeMismatch)
|
|
}
|
|
|
|
agree := consistencyWarnings(
|
|
Plan{Type: MediaSeries, Year: 2006},
|
|
PreParse{Year: 2006, Season: 2},
|
|
)
|
|
if len(agree) != 0 {
|
|
t.Errorf("agreeing signals should not warn, got %v", agree)
|
|
}
|
|
}
|
|
|
|
func TestDecide_AlwaysReview(t *testing.T) {
|
|
p := Plan{Type: MediaMovie, Title: "X", Files: []PlanFile{{Role: RoleMain}}}
|
|
d := decide(p, PreParse{})
|
|
if d.Auto {
|
|
t.Error("Ф2 decision must never be auto")
|
|
}
|
|
if len(d.Reasons) == 0 || !strings.Contains(d.Reasons[0], "метабазы отключены") {
|
|
t.Errorf("first reason should be DB match, got %v", d.Reasons)
|
|
}
|
|
}
|
|
|
|
func TestPreParse(t *testing.T) {
|
|
pre := preParse("The.Matrix.1999.1080p.BluRay.x264")
|
|
if pre.Year != 1999 {
|
|
t.Errorf("year = %d, want 1999", pre.Year)
|
|
}
|
|
if !strings.Contains(strings.ToLower(pre.Title), "matrix") {
|
|
t.Errorf("title = %q", pre.Title)
|
|
}
|
|
|
|
series := preParse("Some.Show.S02E05.720p")
|
|
if series.Season != 2 || series.Episode != 5 {
|
|
t.Errorf("season/episode = %d/%d, want 2/5", series.Season, series.Episode)
|
|
}
|
|
}
|