show-ingestion/main_test.go

413 lines
11 KiB
Go

package main_test
import (
"context"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path"
"slices"
"testing"
main "gitea/show-ingestion"
)
func TestRunChoosesOne(t *testing.T) {
ind := t.TempDir()
outd := t.TempDir()
for _, given := range []string{
"Survivor.AU.S12E11.1080p.HEVC.x265-MeGusta[EZTVx.to].mkv",
"Survivor.AU.S12E11.720p.HEVC.x265-MeGusta[EZTVx.to].mkv",
"Survivor.AU.S12E12.720p.HEVC.x265-MeGusta[EZTVx.to].mkv",
} {
ioutil.WriteFile(path.Join(ind, given), []byte{}, os.ModePerm)
}
want := map[string]bool{
"Australian_Survivor_S12E11.mkv": false,
"Australian_Survivor_S12E12.mkv": false,
}
if err := main.RunWith(context.Background(),
outd,
ind,
[]string{
".urvivor.[Aa][Uu].*[sS](?P<season>[0-9]+)[eE](?P<episode>[0-9]*).*1080.*MeGusta",
".urvivor.[Aa][Uu].*[sS](?P<season>[0-9]+)[eE](?P<episode>[0-9]*).*720.*MeGusta",
},
main.Fields{
Title: "Australian_Survivor",
},
func(outf, inf string) error {
want[path.Base(outf)] = true
return nil
},
); err != nil {
t.Fatal(err)
}
for k, v := range want {
if !v {
t.Errorf("did not mv_n_ln(outf=%s)", k)
}
}
}
func TestRunWith(t *testing.T) {
cases := map[string]struct {
given []string
patterns []string
overrides main.Fields
want []string
}{
"empty": {},
"fallback survivor": {
given: []string{
"Survivor.AU.S12E11.1080p.HEVC.x265-MeGusta[EZTVx.to].mkv",
"Survivor.AU.S12E11.720p.HEVC.x265-MeGusta[EZTVx.to].mkv",
"Survivor.AU.S12E12.720p.HEVC.x265-MeGusta[EZTVx.to].mkv",
},
patterns: []string{
".urvivor.[Aa][Uu].*[sS](?P<season>[0-9]+)[eE](?P<episode>[0-9]*).*1080.*MeGusta",
".urvivor.[Aa][Uu].*[sS](?P<season>[0-9]+)[eE](?P<episode>[0-9]*).*720.*MeGusta",
},
overrides: main.Fields{
Title: "Australian_Survivor",
},
want: []string{
"Australian_Survivor_S12E11.mkv",
"Australian_Survivor_S12E12.mkv",
},
},
"hard w group": {
given: []string{
"[Yameii] Dr. Stone - S04E12 [English Dub] [CR WEB-DL 720p] [F6EF1948].mkv",
},
patterns: []string{
main.PatternGroupTitleHyphenSE,
},
want: []string{
"Dr_Stone_S04E12.mkv",
},
},
"easy w group": {
given: []string{
"[SubsPlease] Tokidoki Bosotto Russia-go de Dereru Tonari no Alya-san - 01 (720p) [A12844D5].mkv",
"[SubsPlease] Tokidoki Bosotto Russia-go de Dereru Tonari no Alya-san - 02 (720p) [2608F490].mkv",
},
patterns: []string{
`^\[[^\]]*\] (?P<title>.*) - (?<episode>[0-9]*)`,
},
overrides: main.Fields{
Season: "01",
},
want: []string{
"Tokidoki_Bosotto_Russia-go_de_Dereru_Tonari_no_Alya-san_S01E01.mkv",
"Tokidoki_Bosotto_Russia-go_de_Dereru_Tonari_no_Alya-san_S01E02.mkv",
},
},
}
for name, d := range cases {
c := d
t.Run(name, func(t *testing.T) {
ind := t.TempDir()
for _, f := range c.given {
ioutil.WriteFile(path.Join(ind, f), []byte{}, os.ModePerm)
}
outd := t.TempDir()
if err := main.RunWith(context.Background(), outd, ind, c.patterns, c.overrides, main.RealMvNLn); err != nil {
t.Fatal("err on first run:", err)
} else if err := main.RunWith(context.Background(), outd, ind, c.patterns, c.overrides, main.RealMvNLn); err != nil {
t.Fatal("err on second run:", err)
}
for _, f := range c.want {
if stat, err := os.Stat(path.Join(outd, f)); os.IsNotExist(err) {
t.Errorf("expected %s", f)
} else if !stat.Mode().IsRegular() {
t.Errorf("%s not a regular file: %v", f, stat.Mode())
}
}
if entries, err := os.ReadDir(outd); err != nil {
t.Error("failed to list outdir: %w", err)
} else {
for _, entry := range entries {
t.Logf("%s", entry.Name())
if !slices.Contains(c.want, path.Base(entry.Name())) {
t.Errorf("unexpected %s", entry.Name())
}
if !entry.Type().IsRegular() {
t.Errorf("non-regular file %s in out: %v", entry.Name(), entry.Type())
}
}
}
if entries, err := os.ReadDir(ind); err != nil {
t.Error("failed to list indir: %w", err)
} else {
for _, entry := range entries {
inf := path.Join(ind, entry.Name())
if _, err := os.Stat(inf); err != nil {
t.Errorf("%s no longer in ind: %v", inf, err)
}
}
}
})
}
}
func TestRecursive(t *testing.T) {
webhooks := []string{}
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPut {
t.Errorf("unexpected webhook method %s", r.Method)
}
if r.URL.User.String() != "" {
t.Errorf("unexpected auth on url %s", r.URL.String())
}
if u, p, _ := r.BasicAuth(); u != "u" || p != "p" {
t.Errorf("webhook didnt translate u:p to basic auth")
}
b, _ := ioutil.ReadAll(r.Body)
t.Logf("%s { %s }", r.URL.String(), b)
webhooks = append(webhooks, string(b))
}))
t.Cleanup(s.Close)
t.Cleanup(func() {
t.Logf("webhooks: %+v", webhooks)
if len(webhooks) == 0 {
t.Errorf("expected webhook calls but got none")
}
deduped := slices.Clone(webhooks)
slices.Sort(deduped)
slices.Compact(deduped)
if len(deduped) != len(webhooks) {
t.Errorf("expected no duplicate webhooks but got %+v", webhooks)
}
})
u, _ := url.Parse(s.URL)
u.User = url.UserPassword("u", "p")
main.WebhookOnRecursiveMiss = u.String()
main.WebhookOnRecursiveMissCacheD = t.TempDir()
t.Cleanup(func() {
main.WebhookOnRecursiveMiss = ""
main.WebhookOnRecursiveMissCacheD = ""
})
was, _ := os.Getwd()
t.Cleanup(func() { os.Chdir(was) })
os.Chdir(t.TempDir())
outd := t.TempDir()
os.MkdirAll(path.Join(outd, "A"), os.ModePerm)
// use config
write("./showA/.show-ingestion.yaml", `{
"c": {
"title": "A",
"season": "A",
"episode": "A"
},
"p": [".*"],
"o": "`+outd+`/A"
}`)
write("./showA/file.a")
// parse files and const wins
write("./showB/.show-ingestion.yaml", `{
"o": "`+outd+`/B_{{.Title}}_{{.Season}}_{{.Episode}}",
"p": [],
"c": {"title": "TITLE"}
}`)
write("./showB/title S01E02.b")
// use file pattern
write("./dirA/showC/.show-ingestion.yaml", `{
"o": "`+outd+`/C",
"p": ["^(?P<title>.) (?P<season>.) (?P<episode>.)"]
}`)
write("./dirA/showC/t s e.c")
// dry run
write("./dirA/showD/.show-ingestion.yaml", `{
"o": "`+outd+`/D",
"d": true
}`)
write("./dirA/showD/title S02E04.d")
// not configured
os.MkdirAll("./dirB/showE", os.ModePerm)
write("./dirB/showE/title S03E06.e")
// defaults
write("./dirA/showF/.show-ingestion.yaml", `{
"o": "`+outd+`/F"
}`)
write("./dirA/showF/[Yameii] Dr. Stone - S04E12 [English Dub] [CR WEB-DL 720p] [F6EF1948].mkv")
if err := main.Recursive(context.Background()); err != nil {
t.Fatal(err)
} else if err := main.Recursive(context.Background()); err != nil {
t.Fatalf("failed second run: %v", err)
}
exists(t, path.Join(outd, "A", "A_SAEA.a"))
exists(t, path.Join(outd, "B_TITLE_01_02", "TITLE_S01E02.b"))
exists(t, path.Join(outd, "C", "t_SsEe.c"))
notExists(t, path.Join(outd, "D", "title_S02E04.d"))
notExists(t, path.Join(outd, "title_S03E06.e"))
exists(t, path.Join(outd, "F", "Dr_Stone_S04E12.mkv"))
notExists(t, path.Join(outd, "F", "[Yameii]_Dr_Stone_-_S04E12.mkv"))
}
func write(f string, b ...string) {
if len(b) == 0 {
b = append(b, "")
}
os.MkdirAll(path.Dir(f), os.ModePerm)
os.WriteFile(f, []byte(b[0]), os.ModePerm)
}
func exists(t *testing.T, p string) {
if _, err := os.Stat(p); os.IsNotExist(err) {
d := path.Dir(path.Dir(p))
t.Errorf("expected %s of %s (%+v)", path.Base(p), d, ls(d))
}
}
func notExists(t *testing.T, p string) {
if _, err := os.Stat(p); !os.IsNotExist(err) {
d := path.Dir(path.Dir(p))
t.Errorf("unexpected %s of %s (%+v)", path.Base(p), d, ls(d))
}
}
func ls(d string) []string {
result := []string{}
entries, _ := os.ReadDir(d)
for _, entry := range entries {
p := path.Join(d, entry.Name())
if entry.IsDir() {
result = append(result, ls(p)...)
} else {
result = append(result, p)
}
}
slices.Sort(result)
return result
}
func TestParse(t *testing.T) {
cases := map[string]struct {
pattern string
want main.Fields
}{
"[SubsPlease] Tokidoki Bosotto Russia-go de Dereru Tonari no Alya-san - 01 (720p) [A12844D5].mkv": {
pattern: main.PatternGroupTitleHyphenSE,
want: main.Fields{
Title: "Tokidoki Bosotto Russia-go de Dereru Tonari no Alya-san",
Season: "",
Episode: "01",
},
},
"Survivor.AU.S12E11.1080p.HEVC.x265-MeGusta[EZTVx.to].mkv": {
pattern: main.PatternGroupTitleHyphenSE,
want: main.Fields{
Title: "Survivor.AU",
Season: "12",
Episode: "11",
},
},
"DAN DA DAN (2024) S01E01v2 (1080p WEB-DL H264 AAC DDP 2.0 Dual-Audio) [MALD].mkv": {
pattern: main.PatternGroupTitleHyphenSE,
want: main.Fields{
Title: "DAN DA DAN (2024)",
Season: "01",
Episode: "01",
},
},
"ZENSHU.S01E01.1080p.AMZN.WEB-DL.MULTi.DDP2.0.H.264.MSubs-ToonsHub.mkv": {
pattern: main.PatternGroupTitleHyphenSE,
want: main.Fields{
Title: "ZENSHU",
Season: "01",
Episode: "01",
},
},
"[Yameii] My Hero Academia - S07E08 [English Dub] [CR WEB-DL 720p] [DE5FFC3E].mkv": {
pattern: main.PatternGroupTitleHyphenSE,
want: main.Fields{
Title: "My Hero Academia",
Season: "07",
Episode: "08",
},
},
"Ranma1-2.2024.S01E03.Because.Theres.Someone.He.Likes.1080p.NF.WEB-DL.AAC2.0.H.264-VARYG.mkv": {
pattern: main.PatternGroupTitleHyphenSE,
want: main.Fields{
Title: "Ranma1-2.2024",
Season: "01",
Episode: "03",
},
},
"[Yameii] The Apothecary Diaries - S02E03 [English Dub] [CR WEB-DL 720p] [FD3E7434].mkv": {
pattern: main.PatternGroupTitleHyphenSE,
want: main.Fields{
Title: "The Apothecary Diaries",
Season: "02",
Episode: "03",
},
},
"The.Dinner.Table.Detective.S01E01.Welcome.to.the.murderous.party.File.1.1080p.AMZN.WEB-DL.DDP2.0.H.264-VARYG.mkv": {
pattern: main.PatternGroupTitleHyphenSE,
want: main.Fields{
Title: "The.Dinner.Table.Detective",
Season: "01",
Episode: "01",
},
},
"[Reza] Wistoria Wand and Sword - S01E01.mkv": {
pattern: main.PatternGroupTitleHyphenSE,
want: main.Fields{
Title: "Wistoria Wand and Sword",
Season: "01",
Episode: "01",
},
},
"[EMBER] Ao no Hako - 01.mkv": {
pattern: main.PatternGroupTitleHyphenSE,
want: main.Fields{
Title: "Ao no Hako",
Season: "",
Episode: "01",
},
},
"Niehime to Kemono no Ou - 12 [darkflux].mkv": {
pattern: main.PatternGroupTitleHyphenSE,
want: main.Fields{
Title: "Niehime to Kemono no Ou",
Season: "",
Episode: "12",
},
},
}
for f, d := range cases {
c := d
t.Run(f, func(t *testing.T) {
got, _ := main.Parse(f, c.pattern)
if got != c.want {
t.Errorf("expected \n\t%+v but got \n\t%+v", c.want, got)
}
})
}
}