package library import ( "database/sql" "path/filepath" "testing" "git.noroutes.net/NoRoutes/vibetunes/internal/database" "git.noroutes.net/NoRoutes/vibetunes/internal/metadata" ) func openTestDB(t *testing.T) *sql.DB { t.Helper() db, err := database.Open(filepath.Join(t.TempDir(), "test.db")) if err != nil { t.Fatalf("database.Open: %v", err) } t.Cleanup(func() { db.Close() }) return db } func makeMediaFile(t *testing.T, db *sql.DB, relPath string) *database.MediaFile { t.Helper() folder, err := database.CreateMusicFolder(db, "Music", "/music", "music") if err != nil { t.Fatalf("CreateMusicFolder: %v", err) } mf, err := database.CreateMediaFile(db, folder.ID, relPath, 1000, 0, "hash1", "flac", "song", 180, 900, 44100, 2) if err != nil { t.Fatalf("CreateMediaFile: %v", err) } return mf } func TestIndexFileCreatesArtistAlbumSong(t *testing.T) { db := openTestDB(t) mf := makeMediaFile(t, db, "Artist/Album/01 - Song.flac") tags := &metadata.Tags{ Title: "Song One", Album: "Great Album", AlbumArtist: "Great Artist", Artists: []string{"Great Artist", "Featured Person"}, Genres: []string{"Rock", "Indie"}, TrackNumber: 1, Year: 2020, } song, err := IndexFile(db, mf, tags) if err != nil { t.Fatalf("IndexFile: %v", err) } if song.Title != "Song One" { t.Errorf("Title = %q", song.Title) } if !song.AlbumID.Valid { t.Fatal("expected song to have an album") } album, err := database.GetAlbum(db, song.AlbumID.String) if err != nil { t.Fatalf("GetAlbum: %v", err) } if album.Title != "Great Album" { t.Errorf("Album title = %q", album.Title) } names, err := database.ListSongArtistNames(db, song.ID) if err != nil { t.Fatalf("ListSongArtistNames: %v", err) } if len(names) != 2 || names[0] != "Great Artist" || names[1] != "Featured Person" { t.Errorf("song artists = %v", names) } genres, err := database.ListGenres(db) if err != nil { t.Fatalf("ListGenres: %v", err) } if len(genres) != 2 { t.Errorf("genres = %v, want 2", genres) } } func TestIndexFileIsIdempotent(t *testing.T) { db := openTestDB(t) mf := makeMediaFile(t, db, "Artist/Album/01 - Song.flac") tags := &metadata.Tags{Title: "Song", Album: "Album", AlbumArtist: "Artist", Artists: []string{"Artist"}} song1, err := IndexFile(db, mf, tags) if err != nil { t.Fatalf("IndexFile (1st): %v", err) } song2, err := IndexFile(db, mf, tags) if err != nil { t.Fatalf("IndexFile (2nd): %v", err) } if song1.ID != song2.ID { t.Errorf("song ID changed across reindex: %s -> %s", song1.ID, song2.ID) } if song1.AlbumID.String != song2.AlbumID.String { t.Errorf("album ID changed across reindex: %s -> %s", song1.AlbumID.String, song2.AlbumID.String) } artists, err := database.ListSongArtistNames(db, song1.ID) if err != nil { t.Fatalf("ListSongArtistNames: %v", err) } if len(artists) != 1 { t.Errorf("reindexing duplicated song_artists rows: %v", artists) } } func TestIndexFileUsesFilenameWhenNoTitle(t *testing.T) { db := openTestDB(t) mf := makeMediaFile(t, db, "Unknown/Track.flac") song, err := IndexFile(db, mf, &metadata.Tags{}) if err != nil { t.Fatalf("IndexFile: %v", err) } if song.Title != mf.RelativePath { t.Errorf("Title = %q, want fallback to relative path %q", song.Title, mf.RelativePath) } } func TestListDirectory(t *testing.T) { db := openTestDB(t) folder, err := database.CreateMusicFolder(db, "Music", "/music", "music") if err != nil { t.Fatalf("CreateMusicFolder: %v", err) } for _, p := range []string{"ArtistA/Album1/01.flac", "ArtistA/Album1/02.flac", "ArtistA/Album2/01.flac", "ArtistB/Album3/01.flac"} { if _, err := database.CreateMediaFile(db, folder.ID, p, 1, 0, "h"+p, "flac", "song", 1, 1, 1, 1); err != nil { t.Fatalf("CreateMediaFile(%s): %v", p, err) } } root, err := ListDirectory(db, folder.ID, "") if err != nil { t.Fatalf("ListDirectory(root): %v", err) } if len(root) != 2 { t.Fatalf("root entries = %v, want 2 (ArtistA, ArtistB)", root) } for _, e := range root { if !e.Dir { t.Errorf("root entry %q should be a directory", e.Name) } } sub, err := ListDirectory(db, folder.ID, "ArtistA") if err != nil { t.Fatalf("ListDirectory(ArtistA): %v", err) } if len(sub) != 2 { t.Fatalf("ArtistA entries = %v, want 2 (Album1, Album2)", sub) } files, err := ListDirectory(db, folder.ID, "ArtistA/Album1") if err != nil { t.Fatalf("ListDirectory(ArtistA/Album1): %v", err) } if len(files) != 2 { t.Fatalf("Album1 entries = %v, want 2 files", files) } for _, e := range files { if e.Dir { t.Errorf("entry %q should be a file", e.Name) } } }