Files
2026-08-23 14:13:18 -05:00

214 lines
6.8 KiB
Go

package artistphoto
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
func TestFetchPhotoURLUsesFanartTVWhenMBIDAndKeyProvided(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/mbid-1" {
t.Errorf("unexpected path %q", r.URL.Path)
}
if r.URL.Query().Get("api_key") != "secret" {
t.Errorf("api_key = %q", r.URL.Query().Get("api_key"))
}
json.NewEncoder(w).Encode(map[string]any{
"artistthumb": []map[string]string{{"url": "https://fanart.example/photo.jpg"}},
})
}))
defer srv.Close()
c := &Client{FanartTVBaseURL: srv.URL}
u, err := c.FetchPhotoURL(context.Background(), "mbid-1", "Some Artist", "secret")
if err != nil {
t.Fatalf("FetchPhotoURL: %v", err)
}
if u != "https://fanart.example/photo.jpg" {
t.Errorf("url = %q", u)
}
}
func TestFetchPhotoURLSkipsFanartTVWithoutMBID(t *testing.T) {
fanartCalled := false
fanart := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fanartCalled = true
}))
defer fanart.Close()
wds := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]any{"search": []map[string]any{}})
}))
defer wds.Close()
c := &Client{FanartTVBaseURL: fanart.URL, WikidataSearchBaseURL: wds.URL}
if _, err := c.FetchPhotoURL(context.Background(), "", "Some Artist", "secret"); err != nil {
t.Fatalf("FetchPhotoURL: %v", err)
}
if fanartCalled {
t.Error("fanart.tv should never be called without a MusicBrainz ID (it has no name-search endpoint)")
}
}
func TestFetchPhotoURLSearchesWikidataByName(t *testing.T) {
wds := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if got := r.URL.Query().Get("search"); got != "The Fray" {
t.Errorf("search query = %q", got)
}
json.NewEncoder(w).Encode(map[string]any{
"search": []map[string]any{
{"id": "Q191571", "label": "The Fray", "description": "American rock band"},
},
})
}))
defer wds.Close()
wde := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/Q191571.json" {
t.Errorf("unexpected entity path %q", r.URL.Path)
}
json.NewEncoder(w).Encode(map[string]any{
"entities": map[string]any{
"Q191571": map[string]any{
"claims": map[string]any{
// A real Wikidata entity carries many other
// properties whose datavalue.value is an
// object, not a string (P31 "instance of" is
// an entity reference, for example) — this
// must not prevent P18 from being read.
"P31": []map[string]any{
{"mainsnak": map[string]any{"datavalue": map[string]any{"value": map[string]any{"entity-type": "item", "id": "Q215380"}}}},
},
"P18": []map[string]any{
{"mainsnak": map[string]any{"datavalue": map[string]any{"value": "The Fray.jpg"}}},
},
},
},
},
})
}))
defer wde.Close()
commons := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
defer commons.Close()
c := &Client{WikidataSearchBaseURL: wds.URL, WikidataEntityBaseURL: wde.URL, CommonsBaseURL: commons.URL}
u, err := c.FetchPhotoURL(context.Background(), "", "The Fray", "")
if err != nil {
t.Fatalf("FetchPhotoURL: %v", err)
}
want := commons.URL + "/The%20Fray.jpg?width=800"
if u != want {
t.Errorf("url = %q, want %q", u, want)
}
}
func TestFetchPhotoURLFallsBackToWikidataWhenFanartHasNoPhoto(t *testing.T) {
fanart := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
defer fanart.Close()
wds := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]any{
"search": []map[string]any{
{"id": "Q1", "label": "Some Artist", "description": "American singer"},
},
})
}))
defer wds.Close()
wde := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]any{"entities": map[string]any{"Q1": map[string]any{"claims": map[string]any{}}}})
}))
defer wde.Close()
c := &Client{FanartTVBaseURL: fanart.URL, WikidataSearchBaseURL: wds.URL, WikidataEntityBaseURL: wde.URL}
u, err := c.FetchPhotoURL(context.Background(), "mbid-2", "Some Artist", "secret")
if err != nil {
t.Fatalf("FetchPhotoURL: %v", err)
}
if u != "" {
t.Errorf("expected empty URL (no P18 claim), got %q", u)
}
}
func TestSearchArtistWikidataIDPrefersMusicDescription(t *testing.T) {
wds := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]any{
"search": []map[string]any{
{"id": "Q-actor", "label": "Gigi Perez", "description": "Filipino actor"},
{"id": "Q-singer", "label": "Gigi Perez", "description": "American singer-songwriter"},
},
})
}))
defer wds.Close()
c := &Client{WikidataSearchBaseURL: wds.URL}
qid, err := c.SearchArtistWikidataID(context.Background(), "Gigi Perez")
if err != nil {
t.Fatalf("SearchArtistWikidataID: %v", err)
}
if qid != "Q-singer" {
t.Errorf("qid = %q, want the music-flavored match Q-singer", qid)
}
}
func TestSearchArtistWikidataIDRequiresExactLabelMatch(t *testing.T) {
wds := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]any{
"search": []map[string]any{
{"id": "Q-tribute", "label": "Not Related! with Luke Smith Tribute", "description": "cover band"},
},
})
}))
defer wds.Close()
c := &Client{WikidataSearchBaseURL: wds.URL}
qid, err := c.SearchArtistWikidataID(context.Background(), "Not Related! with Luke Smith")
if err != nil {
t.Fatalf("expected nil error, got %v", err)
}
if qid != "" {
t.Errorf("expected no match for a non-exact label, got %q", qid)
}
}
func TestFetchWikidataPhotoByNameRetriesOnTransientError(t *testing.T) {
var calls int
wds := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls++
if calls == 1 {
// Simulate a transient network hiccup by closing the
// connection without responding at all.
hj, ok := w.(http.Hijacker)
if !ok {
t.Fatal("ResponseWriter does not support hijacking")
}
conn, _, err := hj.Hijack()
if err != nil {
t.Fatal(err)
}
conn.Close()
return
}
json.NewEncoder(w).Encode(map[string]any{"search": []map[string]any{}})
}))
defer wds.Close()
c := &Client{WikidataSearchBaseURL: wds.URL}
u, err := c.FetchPhotoURL(context.Background(), "", "Retry Artist", "")
if err != nil {
t.Fatalf("expected the retry to recover from the first request's transient failure, got %v", err)
}
if u != "" {
t.Errorf("expected empty URL (no search results), got %q", u)
}
if calls != 2 {
t.Errorf("expected exactly one retry (2 total calls), got %d calls", calls)
}
}