Раскладка файлов после распознавния
This commit is contained in:
@@ -12,7 +12,10 @@ import (
|
||||
|
||||
"git.vakhrushev.me/av/jellybit/internal/httpapi"
|
||||
"git.vakhrushev.me/av/jellybit/internal/ingest"
|
||||
"git.vakhrushev.me/av/jellybit/internal/layout"
|
||||
"git.vakhrushev.me/av/jellybit/internal/recognize"
|
||||
"git.vakhrushev.me/av/jellybit/internal/store"
|
||||
"git.vakhrushev.me/av/jellybit/internal/worker"
|
||||
)
|
||||
|
||||
type fakeIngestor struct {
|
||||
@@ -175,3 +178,200 @@ func TestIndexRenders(t *testing.T) {
|
||||
type ingestErr string
|
||||
|
||||
func (e ingestErr) Error() string { return string(e) }
|
||||
|
||||
// --- Ревью ---
|
||||
|
||||
type fakeReviewer struct {
|
||||
data *worker.ReviewData
|
||||
applyErr error
|
||||
refined map[int64]string
|
||||
typed map[int64]string
|
||||
ignored map[int64]string
|
||||
applied []int64
|
||||
deferred []int64
|
||||
undone []int64
|
||||
}
|
||||
|
||||
func (f *fakeReviewer) ReviewData(_ context.Context, _ int64) (*worker.ReviewData, error) {
|
||||
return f.data, nil
|
||||
}
|
||||
func (f *fakeReviewer) Apply(_ context.Context, id int64) error {
|
||||
if f.applyErr != nil {
|
||||
return f.applyErr
|
||||
}
|
||||
f.applied = append(f.applied, id)
|
||||
return nil
|
||||
}
|
||||
func (f *fakeReviewer) Refine(_ context.Context, id int64, hint string) error {
|
||||
if f.refined == nil {
|
||||
f.refined = map[int64]string{}
|
||||
}
|
||||
f.refined[id] = hint
|
||||
return nil
|
||||
}
|
||||
func (f *fakeReviewer) SetType(_ context.Context, id int64, t string) error {
|
||||
if f.typed == nil {
|
||||
f.typed = map[int64]string{}
|
||||
}
|
||||
f.typed[id] = t
|
||||
return nil
|
||||
}
|
||||
func (f *fakeReviewer) IgnoreFile(_ context.Context, id int64, src string) error {
|
||||
if f.ignored == nil {
|
||||
f.ignored = map[int64]string{}
|
||||
}
|
||||
f.ignored[id] = src
|
||||
return nil
|
||||
}
|
||||
func (f *fakeReviewer) Defer(_ context.Context, id int64) error {
|
||||
f.deferred = append(f.deferred, id)
|
||||
return nil
|
||||
}
|
||||
func (f *fakeReviewer) Undo(_ context.Context, id int64) error {
|
||||
f.undone = append(f.undone, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func seriesReviewData() *worker.ReviewData {
|
||||
s, e := 2, 1
|
||||
return &worker.ReviewData{
|
||||
Download: store.Download{ID: 1, State: store.StateReview, SourceRef: "magnet:?xt=urn:btih:abc"},
|
||||
Recognition: &store.Recognition{
|
||||
ID: 1, DownloadID: 1, IsCurrent: true, Reasons: `["нет матча в базе"]`,
|
||||
},
|
||||
Plan: recognize.Plan{
|
||||
Type: recognize.MediaSeries, Title: "Фарго", Year: 2015,
|
||||
Files: []recognize.PlanFile{
|
||||
{Src: "Fargo/e1.mkv", Role: recognize.RoleEpisode, Season: &s, Episode: &e},
|
||||
},
|
||||
},
|
||||
Preview: []layout.Link{
|
||||
{Src: "Fargo/e1.mkv", Dst: "/srv/media/series/Фарго (2015)/Season 02/Фарго (2015) S02E01.mkv"},
|
||||
},
|
||||
Hints: []string{"второй сезон"},
|
||||
}
|
||||
}
|
||||
|
||||
// noRedirectClient — не следует за 3xx, чтобы проверять Location.
|
||||
func noRedirectClient() *http.Client {
|
||||
return &http.Client{CheckRedirect: func(*http.Request, []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}}
|
||||
}
|
||||
|
||||
func TestReviewRenders(t *testing.T) {
|
||||
rv := &fakeReviewer{data: seriesReviewData()}
|
||||
srv := newServer(t, httpapi.Deps{Ingestor: &fakeIngestor{}, Commander: &fakeCommander{},
|
||||
Reader: &fakeReader{}, Reviewer: rv})
|
||||
|
||||
resp, err := http.Get(srv.URL + "/review/1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("status = %d", resp.StatusCode)
|
||||
}
|
||||
for _, want := range []string{"Фарго", "нет матча в базе", "Fargo/e1.mkv",
|
||||
"Season 02", "Применить", "Уточнить"} {
|
||||
if !strings.Contains(string(body), want) {
|
||||
t.Errorf("страница ревью не содержит %q", want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyRedirectsToIndex(t *testing.T) {
|
||||
rv := &fakeReviewer{data: seriesReviewData()}
|
||||
srv := newServer(t, httpapi.Deps{Ingestor: &fakeIngestor{}, Commander: &fakeCommander{},
|
||||
Reader: &fakeReader{}, Reviewer: rv})
|
||||
|
||||
resp, err := noRedirectClient().Post(srv.URL+"/ui/downloads/1/apply", "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusSeeOther {
|
||||
t.Fatalf("status = %d, want 303", resp.StatusCode)
|
||||
}
|
||||
if loc := resp.Header.Get("Location"); loc != "/" {
|
||||
t.Errorf("Location = %q, want /", loc)
|
||||
}
|
||||
if len(rv.applied) != 1 {
|
||||
t.Errorf("Apply не вызван: %v", rv.applied)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyCollisionRedirectsToReview(t *testing.T) {
|
||||
rv := &fakeReviewer{data: seriesReviewData(), applyErr: ingestErr("collision")}
|
||||
srv := newServer(t, httpapi.Deps{Ingestor: &fakeIngestor{}, Commander: &fakeCommander{},
|
||||
Reader: &fakeReader{}, Reviewer: rv})
|
||||
|
||||
resp, err := noRedirectClient().Post(srv.URL+"/ui/downloads/1/apply", "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if loc := resp.Header.Get("Location"); !strings.HasPrefix(loc, "/review/1") {
|
||||
t.Errorf("Location = %q, want /review/1?err=...", loc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefinePostsHint(t *testing.T) {
|
||||
rv := &fakeReviewer{data: seriesReviewData()}
|
||||
srv := newServer(t, httpapi.Deps{Ingestor: &fakeIngestor{}, Commander: &fakeCommander{},
|
||||
Reader: &fakeReader{}, Reviewer: rv})
|
||||
|
||||
resp, err := noRedirectClient().PostForm(srv.URL+"/ui/downloads/1/refine",
|
||||
map[string][]string{"hint": {"это второй сезон"}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if rv.refined[1] != "это второй сезон" {
|
||||
t.Errorf("Refine получил %q", rv.refined[1])
|
||||
}
|
||||
if loc := resp.Header.Get("Location"); !strings.HasPrefix(loc, "/review/1") {
|
||||
t.Errorf("Location = %q", loc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIgnoreAndType(t *testing.T) {
|
||||
rv := &fakeReviewer{data: seriesReviewData()}
|
||||
srv := newServer(t, httpapi.Deps{Ingestor: &fakeIngestor{}, Commander: &fakeCommander{},
|
||||
Reader: &fakeReader{}, Reviewer: rv})
|
||||
cl := noRedirectClient()
|
||||
|
||||
if _, err := cl.PostForm(srv.URL+"/ui/downloads/1/ignore",
|
||||
map[string][]string{"src": {"Fargo/sample.mkv"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if rv.ignored[1] != "Fargo/sample.mkv" {
|
||||
t.Errorf("IgnoreFile получил %q", rv.ignored[1])
|
||||
}
|
||||
|
||||
if _, err := cl.PostForm(srv.URL+"/ui/downloads/1/type",
|
||||
map[string][]string{"type": {"movie"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if rv.typed[1] != "movie" {
|
||||
t.Errorf("SetType получил %q", rv.typed[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUndoAndDefer(t *testing.T) {
|
||||
rv := &fakeReviewer{data: seriesReviewData()}
|
||||
srv := newServer(t, httpapi.Deps{Ingestor: &fakeIngestor{}, Commander: &fakeCommander{},
|
||||
Reader: &fakeReader{}, Reviewer: rv})
|
||||
cl := noRedirectClient()
|
||||
|
||||
if _, err := cl.Post(srv.URL+"/ui/downloads/1/undo", "", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := cl.Post(srv.URL+"/ui/downloads/1/defer", "", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rv.undone) != 1 || len(rv.deferred) != 1 {
|
||||
t.Errorf("undo=%v defer=%v", rv.undone, rv.deferred)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user