214 lines
6.8 KiB
Go
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)
|
|
}
|
|
}
|