package api import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/go-chi/chi/v5" "git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq" ) func newSuggestionsRouter(h *handlers) chi.Router { r := chi.NewRouter() r.Get("/api/discover/suggestions", h.handleListSuggestions) return r } func doListSuggestions(h *handlers, user dbq.User, query string) *httptest.ResponseRecorder { url := "/api/discover/suggestions" if query != "" { url += "?" + query } req := httptest.NewRequest(http.MethodGet, url, nil) req = withUser(req, user) w := httptest.NewRecorder() newSuggestionsRouter(h).ServeHTTP(w, req) return w } func TestSuggestions_HappyPath(t *testing.T) { h, pool := testHandlers(t) user := seedUser(t, pool, "alice", "pw", false) q := dbq.New(pool) seed, err := q.UpsertArtist(context.Background(), dbq.UpsertArtistParams{ Name: "Seed", SortName: "Seed", }) if err != nil { t.Fatalf("UpsertArtist: %v", err) } if _, err := pool.Exec(context.Background(), `INSERT INTO general_likes_artists (user_id, artist_id) VALUES ($1, $2)`, user.ID, seed.ID, ); err != nil { t.Fatalf("like artist: %v", err) } if err := q.UpsertArtistSimilarityUnmatched(context.Background(), dbq.UpsertArtistSimilarityUnmatchedParams{ SeedArtistID: seed.ID, CandidateMbid: "out-mbid", CandidateName: "Outsider", Score: 0.9, Source: "listenbrainz", }); err != nil { t.Fatalf("UpsertArtistSimilarityUnmatched: %v", err) } w := doListSuggestions(h, user, "") if w.Code != http.StatusOK { t.Fatalf("status = %d, want 200; body = %s", w.Code, w.Body.String()) } var got []suggestionView if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil { t.Fatalf("decode: %v", err) } if len(got) != 1 { t.Fatalf("len = %d, want 1; body = %s", len(got), w.Body.String()) } if got[0].MBID != "out-mbid" || got[0].Name != "Outsider" { t.Errorf("got = %+v", got[0]) } if len(got[0].Attribution) != 1 || got[0].Attribution[0].Name != "Seed" { t.Errorf("attribution = %+v, want one entry named Seed", got[0].Attribution) } if !got[0].Attribution[0].IsLiked { t.Errorf("attribution.IsLiked = false, want true") } } func TestSuggestions_EmptyForNewUser(t *testing.T) { h, pool := testHandlers(t) user := seedUser(t, pool, "newbie", "pw", false) w := doListSuggestions(h, user, "") if w.Code != http.StatusOK { t.Fatalf("status = %d, want 200; body = %s", w.Code, w.Body.String()) } body := w.Body.String() if body != "[]" && body != "[]\n" { t.Errorf("body = %q, want []", body) } } func TestSuggestions_BadLimit(t *testing.T) { h, pool := testHandlers(t) user := seedUser(t, pool, "alice", "pw", false) w := doListSuggestions(h, user, "limit=not-a-number") if w.Code != http.StatusBadRequest { t.Errorf("status = %d, want 400; body = %s", w.Code, w.Body.String()) } }