package lidarr import ( "context" "errors" "net/http" "net/http/httptest" "os" "testing" ) func TestLookupAlbumByMBID_HappyPath(t *testing.T) { body, err := os.ReadFile("testdata/album_lookup_by_mbid.json") if err != nil { t.Fatalf("read fixture: %v", err) } c, srv := newTestClient(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/v1/album" { t.Errorf("path = %q, want /api/v1/album", r.URL.Path) } if got := r.URL.Query().Get("foreignAlbumId"); got != "3a2c2c8c-7e6f-4f8a-b1d2-9a8b6c4e3f1d" { t.Errorf("foreignAlbumId = %q", got) } if got := r.Header.Get("X-Api-Key"); got != "key123" { t.Errorf("X-Api-Key = %q, want key123", got) } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = w.Write(body) }) defer srv.Close() got, err := c.LookupAlbumByMBID(context.Background(), "3a2c2c8c-7e6f-4f8a-b1d2-9a8b6c4e3f1d") if err != nil { t.Fatalf("LookupAlbumByMBID: %v", err) } if got.ID != 42 || got.Title != "Music Has The Right To Children" { t.Errorf("got = %+v", got) } } func TestLookupAlbumByMBID_EmptyArrayReturnsErrNotFound(t *testing.T) { c, srv := newTestClient(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("[]")) }) defer srv.Close() _, err := c.LookupAlbumByMBID(context.Background(), "x") if !errors.Is(err, ErrNotFound) { t.Errorf("err = %v, want ErrNotFound", err) } } func TestLookupAlbumByMBID_AuthFailed(t *testing.T) { c, srv := newTestClient(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusUnauthorized) }) defer srv.Close() _, err := c.LookupAlbumByMBID(context.Background(), "x") if !errors.Is(err, ErrAuthFailed) { t.Errorf("err = %v, want ErrAuthFailed", err) } } func TestLookupAlbumByMBID_5xxReturnsErrServerError(t *testing.T) { c, srv := newTestClient(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusInternalServerError) }) defer srv.Close() _, err := c.LookupAlbumByMBID(context.Background(), "x") if !errors.Is(err, ErrServerError) { t.Errorf("err = %v, want ErrServerError", err) } } func TestLookupAlbumByMBID_BadJSON(t *testing.T) { c, srv := newTestClient(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("not json")) }) defer srv.Close() _, err := c.LookupAlbumByMBID(context.Background(), "x") if !errors.Is(err, ErrInvalidPayload) { t.Errorf("err = %v, want ErrInvalidPayload", err) } } func TestLookupAlbumByMBID_EmptyMBIDRejected(t *testing.T) { c, srv := newTestClient(func(http.ResponseWriter, *http.Request) { t.Error("server should not be called with empty mbid") }) defer srv.Close() if _, err := c.LookupAlbumByMBID(context.Background(), ""); err == nil { t.Error("err = nil, want empty-mbid rejection") } } func TestLookupArtistByMBID_EmptyArrayReturnsErrNotFound(t *testing.T) { c, srv := newTestClient(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("[]")) }) defer srv.Close() _, err := c.LookupArtistByMBID(context.Background(), "x") if !errors.Is(err, ErrNotFound) { t.Errorf("err = %v, want ErrNotFound", err) } } func TestLookupArtistByMBID_EmptyMBIDRejected(t *testing.T) { c, srv := newTestClient(func(http.ResponseWriter, *http.Request) { t.Error("server should not be called with empty mbid") }) defer srv.Close() if _, err := c.LookupArtistByMBID(context.Background(), ""); err == nil { t.Error("err = nil, want empty-mbid rejection") } } func TestLookupArtistByMBID_HappyPath(t *testing.T) { body, err := os.ReadFile("testdata/artist_lookup_by_mbid.json") if err != nil { t.Fatalf("read fixture: %v", err) } c, srv := newTestClient(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/v1/artist" { t.Errorf("path = %q, want /api/v1/artist", r.URL.Path) } if got := r.URL.Query().Get("mbId"); got != "069b64b6-7884-4f6a-94cc-e4c1d6c87a01" { t.Errorf("mbId = %q", got) } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = w.Write(body) }) defer srv.Close() got, err := c.LookupArtistByMBID(context.Background(), "069b64b6-7884-4f6a-94cc-e4c1d6c87a01") if err != nil { t.Fatalf("LookupArtistByMBID: %v", err) } if got.ID != 7 || got.ArtistName != "Boards of Canada" { t.Errorf("got = %+v", got) } } func TestDeleteAlbum_PassesBothFlags(t *testing.T) { var captured *http.Request c, srv := newTestClient(func(w http.ResponseWriter, r *http.Request) { captured = r w.WriteHeader(http.StatusOK) }) defer srv.Close() if err := c.DeleteAlbum(context.Background(), 42, true, true); err != nil { t.Fatalf("DeleteAlbum: %v", err) } if captured == nil || captured.Method != http.MethodDelete { t.Fatalf("method = %v, want DELETE", captured) } if captured.URL.Path != "/api/v1/album/42" { t.Errorf("path = %q", captured.URL.Path) } if got := captured.URL.Query().Get("deleteFiles"); got != "true" { t.Errorf("deleteFiles = %q", got) } if got := captured.URL.Query().Get("addImportListExclusion"); got != "true" { t.Errorf("addImportListExclusion = %q", got) } } func TestDeleteAlbum_5xxReturnsErrServerError(t *testing.T) { c, srv := newTestClient(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusInternalServerError) }) defer srv.Close() err := c.DeleteAlbum(context.Background(), 42, true, true) if !errors.Is(err, ErrServerError) { t.Errorf("err = %v, want ErrServerError", err) } } func TestDeleteAlbum_NetworkErrorReturnsErrUnreachable(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})) srv.Close() // server is closed; client should fail to connect c := NewClient(srv.URL, "key123") err := c.DeleteAlbum(context.Background(), 42, true, true) if !errors.Is(err, ErrUnreachable) { t.Errorf("err = %v, want ErrUnreachable", err) } } func TestDeleteAlbum_ZeroIDRejected(t *testing.T) { c, srv := newTestClient(func(_ http.ResponseWriter, _ *http.Request) { t.Error("server should not be called with zero id") }) defer srv.Close() if err := c.DeleteAlbum(context.Background(), 0, true, true); err == nil { t.Error("err = nil, want zero-id rejection") } }