mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 03:26:37 +08:00
refactor: isolate download organize tests
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/ShukeBta/MediaStationGo/internal/config"
|
||||
"github.com/ShukeBta/MediaStationGo/internal/model"
|
||||
)
|
||||
|
||||
func TestDownloadCompleteAutoOrganizesContentPath(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
src := filepath.Join(root, "downloads", "国产剧", "狂飙.S01E01.2023.1080p.mkv")
|
||||
dest := filepath.Join(root, "media")
|
||||
if err := os.MkdirAll(filepath.Dir(src), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(src, []byte("episode"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
repos := newOrganizerTestRepo(t)
|
||||
if err := repos.DB.AutoMigrate(&model.DownloadTask{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for key, value := range map[string]string{
|
||||
"organizer.auto_after_download": "true",
|
||||
"organize.target_dir": dest,
|
||||
"organize.transfer_mode": "copy",
|
||||
} {
|
||||
if err := repos.Setting.Set(t.Context(), key, value); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
org := NewOrganizerService(&config.Config{}, zap.NewNop(), repos)
|
||||
svc := NewDownloadService(zap.NewNop(), repos, NewHub(zap.NewNop()), org)
|
||||
svc.onTorrentComplete(t.Context(), QBitTorrent{
|
||||
Hash: "done123",
|
||||
Name: "狂飙.S01E01.2023.1080p",
|
||||
Progress: 1,
|
||||
SavePath: filepath.Join(root, "downloads", "国产剧"),
|
||||
ContentPath: src,
|
||||
})
|
||||
|
||||
want := filepath.Join(dest, "电视剧", "国产剧", "狂飙", "Season 01", "狂飙 - S01E01.mkv")
|
||||
if _, err := os.Stat(want); err != nil {
|
||||
t.Fatalf("auto organized file missing at %q: %v", want, err)
|
||||
}
|
||||
if _, err := os.Stat(src); err != nil {
|
||||
t.Fatalf("copy mode should keep source: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadCompleteAutoOrganizeUsesTaskMediaCategory(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
src := filepath.Join(root, "downloads", "Motherhood.of.Taihang.S01E01.2026.1080p.mkv")
|
||||
dest := filepath.Join(root, "media")
|
||||
writeOrgFile(t, src, "episode")
|
||||
|
||||
repos := newOrganizerTestRepo(t)
|
||||
if err := repos.DB.AutoMigrate(&model.DownloadTask{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for key, value := range map[string]string{
|
||||
"organizer.auto_after_download": "true",
|
||||
"organize.target_dir": dest,
|
||||
"organize.transfer_mode": "copy",
|
||||
} {
|
||||
if err := repos.Setting.Set(t.Context(), key, value); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
task := &model.DownloadTask{
|
||||
UserID: "u1",
|
||||
Source: "qbittorrent",
|
||||
URL: "magnet:?xt=urn:btih:motherhood",
|
||||
Title: "Motherhood.of.Taihang.S01E01.2026.1080p",
|
||||
SavePath: filepath.Join(root, "downloads"),
|
||||
MediaType: "tv",
|
||||
MediaCategory: "国产剧",
|
||||
Status: "completed",
|
||||
Progress: 1,
|
||||
}
|
||||
if err := repos.Download.Create(t.Context(), task); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
org := NewOrganizerService(&config.Config{}, zap.NewNop(), repos)
|
||||
svc := NewDownloadService(zap.NewNop(), repos, NewHub(zap.NewNop()), org)
|
||||
svc.onTorrentComplete(t.Context(), QBitTorrent{
|
||||
Hash: "done-category",
|
||||
Name: "Motherhood.of.Taihang.S01E01.2026.1080p",
|
||||
Progress: 1,
|
||||
SavePath: filepath.Join(root, "downloads"),
|
||||
ContentPath: src,
|
||||
})
|
||||
|
||||
categoryRoot := filepath.Join(dest, "电视剧", "国产剧")
|
||||
var organized string
|
||||
err := filepath.WalkDir(categoryRoot, func(path string, entry os.DirEntry, err error) error {
|
||||
if err != nil || entry.IsDir() {
|
||||
return err
|
||||
}
|
||||
if filepath.Ext(path) == ".mkv" {
|
||||
organized = path
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("walk organized category: %v", err)
|
||||
}
|
||||
if organized == "" {
|
||||
t.Fatalf("expected organized file under %q", categoryRoot)
|
||||
}
|
||||
wrongRoot := filepath.Join(dest, "电视剧", "Motherhood Of Taihang")
|
||||
if _, err := os.Stat(wrongRoot); !os.IsNotExist(err) {
|
||||
t.Fatalf("unexpected uncategorized organize root %q, err=%v", wrongRoot, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadCompleteOnlyReplacesExistingWhenTaskAllowsWash(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
allowWash bool
|
||||
wantContent string
|
||||
}{
|
||||
{name: "wash disabled keeps existing", allowWash: false, wantContent: "inception-1080p"},
|
||||
{name: "wash enabled replaces existing", allowWash: true, wantContent: "inception-2160p"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
src := filepath.Join(root, "downloads", "Inception 2010 2160p BluRay.mkv")
|
||||
dest := filepath.Join(root, "media")
|
||||
existing := filepath.Join(dest, "电影", "Inception (2010)", "Inception (2010).mkv")
|
||||
writeOrgFile(t, src, "inception-2160p")
|
||||
writeOrgFile(t, existing, "inception-1080p")
|
||||
|
||||
repos := newOrganizerTestRepo(t)
|
||||
if err := repos.DB.AutoMigrate(&model.DownloadTask{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for key, value := range map[string]string{
|
||||
"organizer.auto_after_download": "true",
|
||||
"organize.target_dir": dest,
|
||||
"organize.transfer_mode": "copy",
|
||||
} {
|
||||
if err := repos.Setting.Set(t.Context(), key, value); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := repos.Media.Upsert(t.Context(), &model.Media{
|
||||
Title: "Inception",
|
||||
Path: existing,
|
||||
Year: 2010,
|
||||
Container: "mkv",
|
||||
Width: 1920,
|
||||
Height: 1080,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repos.Download.Create(t.Context(), &model.DownloadTask{
|
||||
Source: "qbittorrent",
|
||||
URL: "magnet:?xt=urn:btih:inception",
|
||||
Title: "Inception 2010 2160p BluRay",
|
||||
SavePath: filepath.Dir(src),
|
||||
Status: "completed",
|
||||
Progress: 1,
|
||||
AllowExistingLibrary: tc.allowWash,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
org := NewOrganizerService(&config.Config{}, zap.NewNop(), repos)
|
||||
svc := NewDownloadService(zap.NewNop(), repos, NewHub(zap.NewNop()), org)
|
||||
svc.onTorrentComplete(t.Context(), QBitTorrent{
|
||||
Hash: "inception",
|
||||
Name: "Inception 2010 2160p BluRay",
|
||||
Progress: 1,
|
||||
SavePath: filepath.Dir(src),
|
||||
ContentPath: src,
|
||||
})
|
||||
|
||||
got, err := os.ReadFile(existing)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(got) != tc.wantContent {
|
||||
t.Fatalf("existing content = %q, want %q", string(got), tc.wantContent)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -232,189 +232,6 @@ func TestProcessDownloadSnapshotSkipsUntrackedCompletedTorrentOnFirstSnapshot(t
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadCompleteAutoOrganizesContentPath(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
src := filepath.Join(root, "downloads", "国产剧", "狂飙.S01E01.2023.1080p.mkv")
|
||||
dest := filepath.Join(root, "media")
|
||||
if err := os.MkdirAll(filepath.Dir(src), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(src, []byte("episode"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
repos := newOrganizerTestRepo(t)
|
||||
if err := repos.DB.AutoMigrate(&model.DownloadTask{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for key, value := range map[string]string{
|
||||
"organizer.auto_after_download": "true",
|
||||
"organize.target_dir": dest,
|
||||
"organize.transfer_mode": "copy",
|
||||
} {
|
||||
if err := repos.Setting.Set(t.Context(), key, value); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
org := NewOrganizerService(&config.Config{}, zap.NewNop(), repos)
|
||||
svc := NewDownloadService(zap.NewNop(), repos, NewHub(zap.NewNop()), org)
|
||||
svc.onTorrentComplete(t.Context(), QBitTorrent{
|
||||
Hash: "done123",
|
||||
Name: "狂飙.S01E01.2023.1080p",
|
||||
Progress: 1,
|
||||
SavePath: filepath.Join(root, "downloads", "国产剧"),
|
||||
ContentPath: src,
|
||||
})
|
||||
|
||||
want := filepath.Join(dest, "电视剧", "国产剧", "狂飙", "Season 01", "狂飙 - S01E01.mkv")
|
||||
if _, err := os.Stat(want); err != nil {
|
||||
t.Fatalf("auto organized file missing at %q: %v", want, err)
|
||||
}
|
||||
if _, err := os.Stat(src); err != nil {
|
||||
t.Fatalf("copy mode should keep source: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadCompleteAutoOrganizeUsesTaskMediaCategory(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
src := filepath.Join(root, "downloads", "Motherhood.of.Taihang.S01E01.2026.1080p.mkv")
|
||||
dest := filepath.Join(root, "media")
|
||||
writeOrgFile(t, src, "episode")
|
||||
|
||||
repos := newOrganizerTestRepo(t)
|
||||
if err := repos.DB.AutoMigrate(&model.DownloadTask{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for key, value := range map[string]string{
|
||||
"organizer.auto_after_download": "true",
|
||||
"organize.target_dir": dest,
|
||||
"organize.transfer_mode": "copy",
|
||||
} {
|
||||
if err := repos.Setting.Set(t.Context(), key, value); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
task := &model.DownloadTask{
|
||||
UserID: "u1",
|
||||
Source: "qbittorrent",
|
||||
URL: "magnet:?xt=urn:btih:motherhood",
|
||||
Title: "Motherhood.of.Taihang.S01E01.2026.1080p",
|
||||
SavePath: filepath.Join(root, "downloads"),
|
||||
MediaType: "tv",
|
||||
MediaCategory: "国产剧",
|
||||
Status: "completed",
|
||||
Progress: 1,
|
||||
}
|
||||
if err := repos.Download.Create(t.Context(), task); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
org := NewOrganizerService(&config.Config{}, zap.NewNop(), repos)
|
||||
svc := NewDownloadService(zap.NewNop(), repos, NewHub(zap.NewNop()), org)
|
||||
svc.onTorrentComplete(t.Context(), QBitTorrent{
|
||||
Hash: "done-category",
|
||||
Name: "Motherhood.of.Taihang.S01E01.2026.1080p",
|
||||
Progress: 1,
|
||||
SavePath: filepath.Join(root, "downloads"),
|
||||
ContentPath: src,
|
||||
})
|
||||
|
||||
categoryRoot := filepath.Join(dest, "电视剧", "国产剧")
|
||||
var organized string
|
||||
err := filepath.WalkDir(categoryRoot, func(path string, entry os.DirEntry, err error) error {
|
||||
if err != nil || entry.IsDir() {
|
||||
return err
|
||||
}
|
||||
if filepath.Ext(path) == ".mkv" {
|
||||
organized = path
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("walk organized category: %v", err)
|
||||
}
|
||||
if organized == "" {
|
||||
t.Fatalf("expected organized file under %q", categoryRoot)
|
||||
}
|
||||
wrongRoot := filepath.Join(dest, "电视剧", "Motherhood Of Taihang")
|
||||
if _, err := os.Stat(wrongRoot); !os.IsNotExist(err) {
|
||||
t.Fatalf("unexpected uncategorized organize root %q, err=%v", wrongRoot, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadCompleteOnlyReplacesExistingWhenTaskAllowsWash(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
allowWash bool
|
||||
wantContent string
|
||||
}{
|
||||
{name: "wash disabled keeps existing", allowWash: false, wantContent: "inception-1080p"},
|
||||
{name: "wash enabled replaces existing", allowWash: true, wantContent: "inception-2160p"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
src := filepath.Join(root, "downloads", "Inception 2010 2160p BluRay.mkv")
|
||||
dest := filepath.Join(root, "media")
|
||||
existing := filepath.Join(dest, "电影", "Inception (2010)", "Inception (2010).mkv")
|
||||
writeOrgFile(t, src, "inception-2160p")
|
||||
writeOrgFile(t, existing, "inception-1080p")
|
||||
|
||||
repos := newOrganizerTestRepo(t)
|
||||
if err := repos.DB.AutoMigrate(&model.DownloadTask{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for key, value := range map[string]string{
|
||||
"organizer.auto_after_download": "true",
|
||||
"organize.target_dir": dest,
|
||||
"organize.transfer_mode": "copy",
|
||||
} {
|
||||
if err := repos.Setting.Set(t.Context(), key, value); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := repos.Media.Upsert(t.Context(), &model.Media{
|
||||
Title: "Inception",
|
||||
Path: existing,
|
||||
Year: 2010,
|
||||
Container: "mkv",
|
||||
Width: 1920,
|
||||
Height: 1080,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repos.Download.Create(t.Context(), &model.DownloadTask{
|
||||
Source: "qbittorrent",
|
||||
URL: "magnet:?xt=urn:btih:inception",
|
||||
Title: "Inception 2010 2160p BluRay",
|
||||
SavePath: filepath.Dir(src),
|
||||
Status: "completed",
|
||||
Progress: 1,
|
||||
AllowExistingLibrary: tc.allowWash,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
org := NewOrganizerService(&config.Config{}, zap.NewNop(), repos)
|
||||
svc := NewDownloadService(zap.NewNop(), repos, NewHub(zap.NewNop()), org)
|
||||
svc.onTorrentComplete(t.Context(), QBitTorrent{
|
||||
Hash: "inception",
|
||||
Name: "Inception 2010 2160p BluRay",
|
||||
Progress: 1,
|
||||
SavePath: filepath.Dir(src),
|
||||
ContentPath: src,
|
||||
})
|
||||
|
||||
got, err := os.ReadFile(existing)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(got) != tc.wantContent {
|
||||
t.Fatalf("existing content = %q, want %q", string(got), tc.wantContent)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompletedTorrentSourceDoesNotFallbackToSavePath(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
savePath := filepath.Join(root, "downloads", "日番")
|
||||
|
||||
Reference in New Issue
Block a user