package beatport import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "streamrip-go/internal/config" ) func newTestClient(t *testing.T, handler http.Handler) (*Client, func()) { t.Helper() ts := httptest.NewServer(handler) cfg := &config.Config{Session: config.DefaultConfigData(), File: config.DefaultConfigData()} cfg.Session.Beatport.Username = "user" cfg.Session.Beatport.Password = "pass" cfg.Session.Beatport.AccessToken = "token" cfg.Session.Beatport.TokenExpiry = time.Now().Add(time.Hour).Unix() c := New(cfg) c.baseURL = ts.URL c.http = ts.Client() c.loggedIn = true return c, ts.Close } func writeJSON(t *testing.T, w http.ResponseWriter, v any) { t.Helper() w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(v); err != nil { t.Fatalf("encode json: %v", err) } } func TestGetDownloadableMapsQuality(t *testing.T) { c, closeServer := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/catalog/tracks/42/download/" { t.Fatalf("path = %q", r.URL.Path) } if got := r.URL.Query().Get("quality"); got != "high" { t.Fatalf("quality = %q, want high", got) } writeJSON(t, w, map[string]any{"location": "https://cdn.example/42.m4a", "stream_quality": ".256k.aac.mp4"}) })) defer closeServer() d, err := c.GetDownloadable(context.Background(), "42", 2) if err != nil { t.Fatalf("GetDownloadable() error = %v", err) } if d.URL != "https://cdn.example/42.m4a" || d.Extension != "m4a" || d.Audio.BitrateKbps != 256 || d.Audio.Quality != "HIGH" { t.Fatalf("unexpected downloadable: %+v", d) } } func TestAlbumMetadataNormalizesReleaseTracks(t *testing.T) { c, closeServer := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/catalog/releases/7/": writeJSON(t, w, map[string]any{ "id": 7, "name": "Release Name", "new_release_date": "2024-01-02", "track_count": 1, "artists": []any{map[string]any{"id": 10, "name": "Album Artist"}}, "image": map[string]any{"dynamic_uri": "https://img.example/{w}x{h}.jpg"}, }) case "/catalog/releases/7/tracks/": writeJSON(t, w, map[string]any{ "next": nil, "results": []any{map[string]any{ "id": 42, "name": "Track Name", "mix_name": "Original Mix", "number": 1, "artists": []any{map[string]any{"id": 11, "name": "Track Artist"}}, "genre": map[string]any{"name": "House"}, "isrc": "USABC1234567", }}, }) default: t.Fatalf("unexpected path %q", r.URL.Path) } })) defer closeServer() meta, err := c.GetMetadata(context.Background(), "7", "album") if err != nil { t.Fatalf("GetMetadata() error = %v", err) } if meta["title"] != "Release Name" || meta["release_date_original"] != "2024-01-02" { t.Fatalf("unexpected album meta: %+v", meta) } tracks := meta["tracks"].(map[string]any)["items"].([]any) if len(tracks) != 1 { t.Fatalf("tracks len = %d", len(tracks)) } track := tracks[0].(map[string]any) if track["id"] != "42" || track["title"] != "Track Name" || track["version"] != "Original Mix" { t.Fatalf("unexpected track meta: %+v", track) } album := track["album"].(map[string]any) if album["title"] != "Release Name" { t.Fatalf("unexpected track album: %+v", album) } } func TestAlbumMetadataCollapsesManyReleaseArtists(t *testing.T) { c, closeServer := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/catalog/releases/7/": writeJSON(t, w, map[string]any{ "id": 7, "name": "Compilation", "track_count": 1, "artists": []any{ map[string]any{"id": 1, "name": "A"}, map[string]any{"id": 2, "name": "B"}, map[string]any{"id": 3, "name": "C"}, map[string]any{"id": 4, "name": "D"}, }, }) case "/catalog/releases/7/tracks/": writeJSON(t, w, map[string]any{ "next": nil, "results": []any{map[string]any{ "id": 42, "name": "Track Name", "number": 1, "artists": []any{map[string]any{"id": 1, "name": "A"}}, }}, }) default: t.Fatalf("unexpected path %q", r.URL.Path) } })) defer closeServer() meta, err := c.GetMetadata(context.Background(), "7", "album") if err != nil { t.Fatalf("GetMetadata() error = %v", err) } artist := meta["artist"].(map[string]any) if artist["name"] != "Various Artists" { t.Fatalf("album artist = %q, want Various Artists", artist["name"]) } track := meta["tracks"].(map[string]any)["items"].([]any)[0].(map[string]any) album := track["album"].(map[string]any) albumArtist := album["artist"].(map[string]any) if albumArtist["name"] != "Various Artists" { t.Fatalf("track album artist = %q, want Various Artists", albumArtist["name"]) } } func TestReleaseArtistCollapseCanBeDisabled(t *testing.T) { cfg := &config.Config{Session: config.DefaultConfigData(), File: config.DefaultConfigData()} cfg.Session.Beatport.VariousArtistsThreshold = -1 c := New(cfg) got := c.releaseArtistName([]any{ map[string]any{"name": "A"}, map[string]any{"name": "B"}, map[string]any{"name": "C"}, map[string]any{"name": "D"}, }) if got != "A, B, C, D" { t.Fatalf("releaseArtistName() = %q", got) } } func TestArtistMetadataUsesReleases(t *testing.T) { c, closeServer := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/catalog/artists/908468/": writeJSON(t, w, map[string]any{"id": 908468, "name": "D'ort"}) case "/catalog/releases/": if got := r.URL.Query().Get("artist_id"); got != "908468" { t.Fatalf("artist_id = %q", got) } writeJSON(t, w, map[string]any{ "next": nil, "results": []any{map[string]any{ "id": 123, "name": "An Artisan's Exhibition", "new_release_date": "2026-01-01", "track_count": 10, "artists": []any{map[string]any{"id": 908468, "name": "D'ort"}}, }}, }) default: t.Fatalf("unexpected path %q", r.URL.Path) } })) defer closeServer() meta, err := c.GetMetadata(context.Background(), "908468", "artist") if err != nil { t.Fatalf("GetMetadata() error = %v", err) } if meta["name"] != "D'ort" { t.Fatalf("artist name = %q", meta["name"]) } albums := meta["albums"].(map[string]any)["items"].([]any) if len(albums) != 1 { t.Fatalf("albums len = %d", len(albums)) } album := albums[0].(map[string]any) if album["id"] != "123" || album["title"] != "An Artisan's Exhibition" || album["tracks_count"] != 10 { t.Fatalf("unexpected album: %+v", album) } }