diff --git a/internal/worker/review.go b/internal/worker/review.go index 225750a..46ee97a 100644 --- a/internal/worker/review.go +++ b/internal/worker/review.go @@ -743,9 +743,12 @@ func mapRole(r recognize.FileRole) (layout.Role, bool) { } } -// torrentByInfohash ищет торрент категории по infohash (v1/v2/hash). +// torrentByInfohash ищет торрент по infohash (v1/v2/hash). Листаем ВСЕ +// торренты (а не только свою категорию): раздача могла быть усыновлена по +// тегу и иметь чужую/пустую категорию — фильтр по категории её бы потерял +// (как и в Poll, см. там же). func (w *Worker) torrentByInfohash(ctx context.Context, infohash string) (qbt.Torrent, bool, error) { - torrents, err := w.qbt.Torrents(ctx, w.cfg.Category) + torrents, err := w.qbt.Torrents(ctx, "") if err != nil { return qbt.Torrent{}, false, err } diff --git a/internal/worker/review_test.go b/internal/worker/review_test.go index aabed9c..e461cf8 100644 --- a/internal/worker/review_test.go +++ b/internal/worker/review_test.go @@ -344,6 +344,42 @@ func TestRecognizeOne_CompletedToReview(t *testing.T) { } } +// TestRecognizeOne_FindsTagAdoptedTorrent — регрессия: раздача, усыновлённая +// по тегу, имеет чужую (или пустую) категорию. Поиск по infohash при +// распознавании обязан её найти; раньше фильтр по w.cfg.Category её терял и +// распознавание падало с «torrent not found in qBittorrent». +func TestRecognizeOne_FindsTagAdoptedTorrent(t *testing.T) { + st := newMemStore() + st.put(completedDownload(1)) + qb := &fakeQbt{ + torrents: []qbt.Torrent{{ + Hash: ihTest, Name: "ThePitt", SavePath: "/d", + Category: "movies", Tags: "jellybit", // тег наш, категория чужая + }}, + files: []qbt.File{{Name: "ThePitt/e1.mkv", Size: 100}, {Name: "ThePitt/e2.mkv", Size: 100}}, + } + rec := &fakeRecognizer{result: seriesResult()} + w := testWorkerWith(st, qb, rec, nil) + + w.recognizeOne(context.Background(), 1) + + if st.downloads[1].State != store.StateReview { + t.Fatalf("state = %q, want review", st.downloads[1].State) + } + // Recognizer вернул бы Title="Show" только если торрент найден по infohash; + // при потере (фильтр по категории) был бы пустой план с причиной «not found». + cur, _ := st.GetCurrentRecognition(context.Background(), 1) + if cur == nil || cur.Title.String != "Show" { + t.Fatalf("recognizer did not run on found torrent (title=%q): torrent must be found by infohash despite foreign category", + func() string { + if cur == nil { + return "" + } + return cur.Title.String + }()) + } +} + func TestRecognizeOne_DiscardsWhenStateChanged(t *testing.T) { st := newMemStore() st.put(completedDownload(1)) diff --git a/internal/worker/worker_test.go b/internal/worker/worker_test.go index d8d2daa..a82fddb 100644 --- a/internal/worker/worker_test.go +++ b/internal/worker/worker_test.go @@ -117,8 +117,21 @@ type fakeQbt struct { files []qbt.File } -func (f *fakeQbt) Torrents(_ context.Context, _ string) ([]qbt.Torrent, error) { - return f.torrents, nil +// Torrents имитирует /torrents/info: пустая категория — все торренты, иначе +// только торренты этой категории (как реальный qBittorrent). Это важно для +// регрессии: раздача, усыновлённая по тегу, имеет чужую категорию и не должна +// теряться при поиске по infohash. +func (f *fakeQbt) Torrents(_ context.Context, category string) ([]qbt.Torrent, error) { + if category == "" { + return f.torrents, nil + } + var out []qbt.Torrent + for _, t := range f.torrents { + if t.Category == category { + out = append(out, t) + } + } + return out, nil } func (f *fakeQbt) Add(_ context.Context, ar qbt.AddRequest) error {