weee
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user