package service import ( "errors" "os" "path/filepath" "testing" "github.com/glebarez/sqlite" "go.uber.org/zap" "gorm.io/gorm" "github.com/ShukeBta/MediaStationGo/internal/config" "github.com/ShukeBta/MediaStationGo/internal/model" "github.com/ShukeBta/MediaStationGo/internal/repository" ) func newFileManagerTestServiceWithRepo(t *testing.T, root string) (*FileManagerService, *repository.Container) { t.Helper() db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) if err != nil { t.Fatal(err) } if err := db.AutoMigrate(&model.Library{}, &model.Media{}, &model.Setting{}); err != nil { t.Fatal(err) } repos := repository.New(db) lib := model.Library{Name: "downloads", Path: root, Type: "movie", Enabled: true} if err := repos.Library.Create(t.Context(), &lib); err != nil { t.Fatal(err) } cfg := &config.Config{} cfg.App.DataDir = root cfg.Cache.CacheDir = root return NewFileManagerService(cfg, zap.NewNop(), repos), repos } func newFileManagerTestService(t *testing.T, root string) *FileManagerService { t.Helper() svc, _ := newFileManagerTestServiceWithRepo(t, root) return svc } func TestFileManagerRecursiveListAndMutations(t *testing.T) { root := t.TempDir() nested := filepath.Join(root, "downloads", "国产剧") if err := os.MkdirAll(nested, 0o755); err != nil { t.Fatal(err) } mediaPath := filepath.Join(nested, "狂飙.S01E01.mkv") if err := os.WriteFile(mediaPath, []byte("video"), 0o644); err != nil { t.Fatal(err) } svc := newFileManagerTestService(t, root) listing, err := svc.List(root, 100, true) if err != nil { t.Fatal(err) } found := false for _, entry := range listing.Entries { if entry.Path == mediaPath { found = true break } } if !found { t.Fatalf("recursive listing did not include %s", mediaPath) } created, err := svc.CreateFolder(root, "整理目标") if err != nil { t.Fatal(err) } emptyListing, err := svc.List(created.Path, 100, false) if err != nil { t.Fatal(err) } if emptyListing.Entries == nil || len(emptyListing.Entries) != 0 { t.Fatalf("empty directory entries = %#v, want empty slice", emptyListing.Entries) } copied, err := svc.Transfer(mediaPath, created.Path, TransferCopy) if err != nil { t.Fatal(err) } if _, err := os.Stat(copied.Path); err != nil { t.Fatalf("copied file missing: %v", err) } renamed, err := svc.Rename(copied.Path, "狂飙 - S01E01.mkv") if err != nil { t.Fatal(err) } if _, err := os.Stat(renamed.Path); err != nil { t.Fatalf("renamed file missing: %v", err) } if err := svc.Delete(renamed.Path); err != nil { t.Fatal(err) } if _, err := os.Stat(renamed.Path); !os.IsNotExist(err) { t.Fatalf("expected file deleted, stat err=%v", err) } } func TestFileManagerRefusesRootMutation(t *testing.T) { root := t.TempDir() svc := newFileManagerTestService(t, root) if err := svc.Delete(root); !errors.Is(err, ErrRootMutation) { t.Fatalf("Delete(root) err = %v, want ErrRootMutation", err) } } func TestFileManagerIncludesConfiguredOrganizeRoots(t *testing.T) { root := t.TempDir() sourceDir := filepath.Join(root, "downloads") targetDir := filepath.Join(root, "media") qbDir := filepath.Join(root, "qb-save") for _, dir := range []string{sourceDir, targetDir, qbDir} { if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatal(err) } } svc, repos := newFileManagerTestServiceWithRepo(t, root) if err := repos.Setting.Set(t.Context(), "organize.source_dir", sourceDir); err != nil { t.Fatal(err) } if err := repos.Setting.Set(t.Context(), "organize.target_dir", targetDir); err != nil { t.Fatal(err) } if err := repos.Setting.Set(t.Context(), "qbittorrent.savepath", qbDir); err != nil { t.Fatal(err) } listing, err := svc.List("", 100) if err != nil { t.Fatal(err) } got := map[string]string{} for _, root := range listing.Roots { got[root.Label] = root.Path } for label, want := range map[string]string{ "organize-source": filepath.Clean(sourceDir), "organize-target": filepath.Clean(targetDir), "qb-savepath": filepath.Clean(qbDir), } { if got[label] != want { t.Fatalf("root %s = %q, want %q; roots=%#v", label, got[label], want, listing.Roots) } } }