package fetch import ( "os" "path/filepath" "sort" "testing" ) func TestPrettifyName_RestoresSpaces(t *testing.T) { got := prettifyName("01 - The_Law_of_Gravitation.mp3") if got != "01 - The Law of Gravitation.mp3" { t.Errorf("got %q", got) } } // yt-dlp's --restrict-filenames collapses odd characters into underscores, and // replacing them can leave runs of spaces behind. func TestPrettifyName_CollapsesRepeatedSpaces(t *testing.T) { got := prettifyName("02 - A___Gap.mp3") if got != "02 - A Gap.mp3" { t.Errorf("got %q", got) } } // Sources whose titles already begin with a track number produce // "01 - 01 - April.mp3" once the output template prepends its own index. Seen // for real against a LibriVox collection on the first live run. func TestPrettifyName_CollapsesDuplicatedTrackPrefix(t *testing.T) { cases := []struct{ in, want string }{ {"01 - 01 - April.mp3", "01 - April.mp3"}, {"02 - 02 - Ave Atque Vale.mp3", "02 - Ave Atque Vale.mp3"}, {"03 - 03_-_The_Average_Man.mp3", "03 - The Average Man.mp3"}, {"10 - 10 - Tenth.mp3", "10 - Tenth.mp3"}, } for _, c := range cases { if got := prettifyName(c.in); got != c.want { t.Errorf("prettifyName(%q) = %q, want %q", c.in, got, c.want) } } } // Only collapse when the two numbers agree. A title that genuinely starts with // a different number is not a duplicated prefix and must be left alone. func TestPrettifyName_KeepsDistinctLeadingNumbers(t *testing.T) { cases := []struct{ in, want string }{ {"01 - 1964 - The Messenger Lectures.mp3", "01 - 1964 - The Messenger Lectures.mp3"}, {"05 - 12 Angry Men.mp3", "05 - 12 Angry Men.mp3"}, } for _, c := range cases { if got := prettifyName(c.in); got != c.want { t.Errorf("prettifyName(%q) = %q, want %q", c.in, got, c.want) } } } func TestPrettifyName_LeavesCleanNamesAlone(t *testing.T) { for _, n := range []string{"01 - April.mp3", "Introduction.mp3"} { if got := prettifyName(n); got != n { t.Errorf("prettifyName(%q) = %q, want it unchanged", n, got) } } } func TestRename_RenamesOnDisk(t *testing.T) { dir := t.TempDir() for _, n := range []string{"01 - 01 - April.mp3", "02 - The_Average_Man.mp3", ".podkit-archive"} { if err := os.WriteFile(filepath.Join(dir, n), []byte("x"), 0o644); err != nil { t.Fatal(err) } } if _, err := Rename(dir); err != nil { t.Fatalf("Rename: %v", err) } entries, _ := os.ReadDir(dir) var names []string for _, e := range entries { names = append(names, e.Name()) } sort.Strings(names) want := []string{".podkit-archive", "01 - April.mp3", "02 - The Average Man.mp3"} if len(names) != len(want) { t.Fatalf("got %v, want %v", names, want) } for i := range want { if names[i] != want[i] { t.Errorf("got %v, want %v", names, want) break } } } // Renaming must never destroy an existing file to make a name prettier. func TestRename_DoesNotClobber(t *testing.T) { dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "01 - April.mp3"), []byte("original"), 0o644); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "01 - 01 - April.mp3"), []byte("candidate"), 0o644); err != nil { t.Fatal(err) } if _, err := Rename(dir); err != nil { t.Fatalf("Rename: %v", err) } body, err := os.ReadFile(filepath.Join(dir, "01 - April.mp3")) if err != nil { t.Fatal(err) } if string(body) != "original" { t.Error("an existing file was overwritten by the rename") } } func TestRename_MissingDirectoryIsAnError(t *testing.T) { if _, err := Rename(filepath.Join(t.TempDir(), "nope")); err == nil { t.Error("expected an error for a missing directory") } } // Check must explain what to install, not just report a lookup failure. func TestErrNotInstalled_SaysHowToFixIt(t *testing.T) { e := &ErrNotInstalled{} for _, want := range []string{"yt-dlp", "pip install", "ffmpeg"} { if !contains(e.Error(), want) { t.Errorf("error message should mention %q; got %q", want, e.Error()) } } } func contains(h, n string) bool { return len(h) >= len(n) && (func() bool { for i := 0; i+len(n) <= len(h); i++ { if h[i:i+len(n)] == n { return true } } return false })() }