Move all Spotify and LastFM code into only one folder for each
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package lastfm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
apiBaseUrl = "https://ws.audioscrobbler.com/2.0/"
|
||||
)
|
||||
|
||||
type lastFMError struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *lastFMError) Error() string {
|
||||
return fmt.Sprintf("last.fm error(%d): %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
type httpDoer interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
func NewClient(apiKey string, lang string, hc httpDoer) *Client {
|
||||
return &Client{apiKey, lang, hc}
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
apiKey string
|
||||
lang string
|
||||
hc httpDoer
|
||||
}
|
||||
|
||||
func (c *Client) makeRequest(params url.Values) (*Response, error) {
|
||||
params.Add("format", "json")
|
||||
params.Add("api_key", c.apiKey)
|
||||
|
||||
req, _ := http.NewRequest("GET", apiBaseUrl, nil)
|
||||
req.URL.RawQuery = params.Encode()
|
||||
|
||||
resp, err := c.hc.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response Response
|
||||
jsonErr := json.Unmarshal(data, &response)
|
||||
|
||||
if resp.StatusCode != 200 && jsonErr != nil {
|
||||
return nil, fmt.Errorf("last.fm http status: (%d)", resp.StatusCode)
|
||||
}
|
||||
|
||||
if jsonErr != nil {
|
||||
return nil, jsonErr
|
||||
}
|
||||
|
||||
if response.Error != 0 {
|
||||
return &response, &lastFMError{Code: response.Error, Message: response.Message}
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (c *Client) ArtistGetInfo(ctx context.Context, name string, mbid string) (*Artist, error) {
|
||||
params := url.Values{}
|
||||
params.Add("method", "artist.getInfo")
|
||||
params.Add("artist", name)
|
||||
params.Add("mbid", mbid)
|
||||
params.Add("lang", c.lang)
|
||||
response, err := c.makeRequest(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.Artist, nil
|
||||
}
|
||||
|
||||
func (c *Client) ArtistGetSimilar(ctx context.Context, name string, mbid string, limit int) (*SimilarArtists, error) {
|
||||
params := url.Values{}
|
||||
params.Add("method", "artist.getSimilar")
|
||||
params.Add("artist", name)
|
||||
params.Add("mbid", mbid)
|
||||
params.Add("limit", strconv.Itoa(limit))
|
||||
response, err := c.makeRequest(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.SimilarArtists, nil
|
||||
}
|
||||
|
||||
func (c *Client) ArtistGetTopTracks(ctx context.Context, name string, mbid string, limit int) (*TopTracks, error) {
|
||||
params := url.Values{}
|
||||
params.Add("method", "artist.getTopTracks")
|
||||
params.Add("artist", name)
|
||||
params.Add("mbid", mbid)
|
||||
params.Add("limit", strconv.Itoa(limit))
|
||||
response, err := c.makeRequest(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.TopTracks, nil
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package lastfm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/navidrome/navidrome/tests"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Client", func() {
|
||||
var httpClient *tests.FakeHttpClient
|
||||
var client *Client
|
||||
|
||||
BeforeEach(func() {
|
||||
httpClient = &tests.FakeHttpClient{}
|
||||
client = NewClient("API_KEY", "pt", httpClient)
|
||||
})
|
||||
|
||||
Describe("ArtistGetInfo", func() {
|
||||
It("returns an artist for a successful response", func() {
|
||||
f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.json")
|
||||
httpClient.Res = http.Response{Body: f, StatusCode: 200}
|
||||
|
||||
artist, err := client.ArtistGetInfo(context.TODO(), "U2", "123")
|
||||
Expect(err).To(BeNil())
|
||||
Expect(artist.Name).To(Equal("U2"))
|
||||
Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=U2&format=json&lang=pt&mbid=123&method=artist.getInfo"))
|
||||
})
|
||||
|
||||
It("fails if Last.FM returns an http status != 200", func() {
|
||||
httpClient.Res = http.Response{
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString(`Internal Server Error`)),
|
||||
StatusCode: 500,
|
||||
}
|
||||
|
||||
_, err := client.ArtistGetInfo(context.TODO(), "U2", "123")
|
||||
Expect(err).To(MatchError("last.fm http status: (500)"))
|
||||
})
|
||||
|
||||
It("fails if Last.FM returns an http status != 200", func() {
|
||||
httpClient.Res = http.Response{
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString(`{"error":3,"message":"Invalid Method - No method with that name in this package"}`)),
|
||||
StatusCode: 400,
|
||||
}
|
||||
|
||||
_, err := client.ArtistGetInfo(context.TODO(), "U2", "123")
|
||||
Expect(err).To(MatchError(&lastFMError{Code: 3, Message: "Invalid Method - No method with that name in this package"}))
|
||||
})
|
||||
|
||||
It("fails if Last.FM returns an error", func() {
|
||||
httpClient.Res = http.Response{
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString(`{"error":6,"message":"The artist you supplied could not be found"}`)),
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
_, err := client.ArtistGetInfo(context.TODO(), "U2", "123")
|
||||
Expect(err).To(MatchError(&lastFMError{Code: 6, Message: "The artist you supplied could not be found"}))
|
||||
})
|
||||
|
||||
It("fails if HttpClient.Do() returns error", func() {
|
||||
httpClient.Err = errors.New("generic error")
|
||||
|
||||
_, err := client.ArtistGetInfo(context.TODO(), "U2", "123")
|
||||
Expect(err).To(MatchError("generic error"))
|
||||
})
|
||||
|
||||
It("fails if returned body is not a valid JSON", func() {
|
||||
httpClient.Res = http.Response{
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString(`<xml>NOT_VALID_JSON</xml>`)),
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
_, err := client.ArtistGetInfo(context.TODO(), "U2", "123")
|
||||
Expect(err).To(MatchError("invalid character '<' looking for beginning of value"))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Describe("ArtistGetSimilar", func() {
|
||||
It("returns an artist for a successful response", func() {
|
||||
f, _ := os.Open("tests/fixtures/lastfm.artist.getsimilar.json")
|
||||
httpClient.Res = http.Response{Body: f, StatusCode: 200}
|
||||
|
||||
similar, err := client.ArtistGetSimilar(context.TODO(), "U2", "123", 2)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(similar.Artists)).To(Equal(2))
|
||||
Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=U2&format=json&limit=2&mbid=123&method=artist.getSimilar"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("ArtistGetTopTracks", func() {
|
||||
It("returns top tracks for a successful response", func() {
|
||||
f, _ := os.Open("tests/fixtures/lastfm.artist.gettoptracks.json")
|
||||
httpClient.Res = http.Response{Body: f, StatusCode: 200}
|
||||
|
||||
top, err := client.ArtistGetTopTracks(context.TODO(), "U2", "123", 2)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(top.Track)).To(Equal(2))
|
||||
Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=U2&format=json&limit=2&mbid=123&method=artist.getTopTracks"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,164 @@
|
||||
package lastfm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/navidrome/navidrome/conf"
|
||||
"github.com/navidrome/navidrome/consts"
|
||||
"github.com/navidrome/navidrome/core/agents"
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
lastFMAgentName = "lastfm"
|
||||
lastFMAPIKey = "c2918986bf01b6ba353c0bc1bdd27bea"
|
||||
//lastFMAPISecret = "3ff2aa214a6d8f2242515083bbb70e79" // Will be needed when implementing Scrobbling
|
||||
)
|
||||
|
||||
type lastfmAgent struct {
|
||||
ctx context.Context
|
||||
apiKey string
|
||||
lang string
|
||||
client *Client
|
||||
}
|
||||
|
||||
func lastFMConstructor(ctx context.Context) agents.Interface {
|
||||
l := &lastfmAgent{
|
||||
ctx: ctx,
|
||||
lang: conf.Server.LastFM.Language,
|
||||
}
|
||||
if conf.Server.LastFM.ApiKey != "" {
|
||||
l.apiKey = conf.Server.LastFM.ApiKey
|
||||
} else {
|
||||
l.apiKey = lastFMAPIKey
|
||||
}
|
||||
hc := utils.NewCachedHTTPClient(http.DefaultClient, consts.DefaultCachedHttpClientTTL)
|
||||
l.client = NewClient(l.apiKey, l.lang, hc)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) AgentName() string {
|
||||
return lastFMAgentName
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) GetMBID(id string, name string) (string, error) {
|
||||
a, err := l.callArtistGetInfo(name, "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if a.MBID == "" {
|
||||
return "", agents.ErrNotFound
|
||||
}
|
||||
return a.MBID, nil
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) GetURL(id, name, mbid string) (string, error) {
|
||||
a, err := l.callArtistGetInfo(name, mbid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if a.URL == "" {
|
||||
return "", agents.ErrNotFound
|
||||
}
|
||||
return a.URL, nil
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) GetBiography(id, name, mbid string) (string, error) {
|
||||
a, err := l.callArtistGetInfo(name, mbid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if a.Bio.Summary == "" {
|
||||
return "", agents.ErrNotFound
|
||||
}
|
||||
return a.Bio.Summary, nil
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) GetSimilar(id, name, mbid string, limit int) ([]agents.Artist, error) {
|
||||
resp, err := l.callArtistGetSimilar(name, mbid, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(resp) == 0 {
|
||||
return nil, agents.ErrNotFound
|
||||
}
|
||||
var res []agents.Artist
|
||||
for _, a := range resp {
|
||||
res = append(res, agents.Artist{
|
||||
Name: a.Name,
|
||||
MBID: a.MBID,
|
||||
})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) GetTopSongs(id, artistName, mbid string, count int) ([]agents.Song, error) {
|
||||
resp, err := l.callArtistGetTopTracks(artistName, mbid, count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(resp) == 0 {
|
||||
return nil, agents.ErrNotFound
|
||||
}
|
||||
var res []agents.Song
|
||||
for _, t := range resp {
|
||||
res = append(res, agents.Song{
|
||||
Name: t.Name,
|
||||
MBID: t.MBID,
|
||||
})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) callArtistGetInfo(name string, mbid string) (*Artist, error) {
|
||||
a, err := l.client.ArtistGetInfo(l.ctx, name, mbid)
|
||||
lfErr, isLastFMError := err.(*lastFMError)
|
||||
if mbid != "" && ((err == nil && a.Name == "[unknown]") || (isLastFMError && lfErr.Code == 6)) {
|
||||
log.Warn(l.ctx, "LastFM/artist.getInfo could not find artist by mbid, trying again", "artist", name, "mbid", mbid)
|
||||
return l.callArtistGetInfo(name, "")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(l.ctx, "Error calling LastFM/artist.getInfo", "artist", name, "mbid", mbid, err)
|
||||
return nil, err
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) callArtistGetSimilar(name string, mbid string, limit int) ([]Artist, error) {
|
||||
s, err := l.client.ArtistGetSimilar(l.ctx, name, mbid, limit)
|
||||
lfErr, isLastFMError := err.(*lastFMError)
|
||||
if mbid != "" && ((err == nil && s.Attr.Artist == "[unknown]") || (isLastFMError && lfErr.Code == 6)) {
|
||||
log.Warn(l.ctx, "LastFM/artist.getSimilar could not find artist by mbid, trying again", "artist", name, "mbid", mbid)
|
||||
return l.callArtistGetSimilar(name, "", limit)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error(l.ctx, "Error calling LastFM/artist.getSimilar", "artist", name, "mbid", mbid, err)
|
||||
return nil, err
|
||||
}
|
||||
return s.Artists, nil
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) callArtistGetTopTracks(artistName, mbid string, count int) ([]Track, error) {
|
||||
t, err := l.client.ArtistGetTopTracks(l.ctx, artistName, mbid, count)
|
||||
lfErr, isLastFMError := err.(*lastFMError)
|
||||
if mbid != "" && ((err == nil && t.Attr.Artist == "[unknown]") || (isLastFMError && lfErr.Code == 6)) {
|
||||
log.Warn(l.ctx, "LastFM/artist.getTopTracks could not find artist by mbid, trying again", "artist", artistName, "mbid", mbid)
|
||||
return l.callArtistGetTopTracks(artistName, "", count)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error(l.ctx, "Error calling LastFM/artist.getTopTracks", "artist", artistName, "mbid", mbid, err)
|
||||
return nil, err
|
||||
}
|
||||
return t.Track, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
conf.AddHook(func() {
|
||||
if conf.Server.LastFM.Enabled {
|
||||
agents.Register(lastFMAgentName, lastFMConstructor)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package lastfm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/tests"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestLastFM(t *testing.T) {
|
||||
tests.Init(t, false)
|
||||
log.SetLevel(log.LevelCritical)
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "LastFM Test Suite")
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package lastfm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/navidrome/navidrome/conf"
|
||||
"github.com/navidrome/navidrome/core/agents"
|
||||
"github.com/navidrome/navidrome/tests"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const (
|
||||
lastfmError3 = `{"error":3,"message":"Invalid Method - No method with that name in this package","links":[]}`
|
||||
lastfmError6 = `{"error":6,"message":"The artist you supplied could not be found","links":[]}`
|
||||
)
|
||||
|
||||
var _ = Describe("lastfmAgent", func() {
|
||||
Describe("lastFMConstructor", func() {
|
||||
It("uses default api key and language if not configured", func() {
|
||||
conf.Server.LastFM.ApiKey = ""
|
||||
agent := lastFMConstructor(context.TODO())
|
||||
Expect(agent.(*lastfmAgent).apiKey).To(Equal(lastFMAPIKey))
|
||||
Expect(agent.(*lastfmAgent).lang).To(Equal("en"))
|
||||
})
|
||||
|
||||
It("uses configured api key and language", func() {
|
||||
conf.Server.LastFM.ApiKey = "123"
|
||||
conf.Server.LastFM.Language = "pt"
|
||||
agent := lastFMConstructor(context.TODO())
|
||||
Expect(agent.(*lastfmAgent).apiKey).To(Equal("123"))
|
||||
Expect(agent.(*lastfmAgent).lang).To(Equal("pt"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetBiography", func() {
|
||||
var agent *lastfmAgent
|
||||
var httpClient *tests.FakeHttpClient
|
||||
BeforeEach(func() {
|
||||
httpClient = &tests.FakeHttpClient{}
|
||||
client := NewClient("API_KEY", "pt", httpClient)
|
||||
agent = lastFMConstructor(context.TODO()).(*lastfmAgent)
|
||||
agent.client = client
|
||||
})
|
||||
|
||||
It("returns the biography", func() {
|
||||
f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.json")
|
||||
httpClient.Res = http.Response{Body: f, StatusCode: 200}
|
||||
Expect(agent.GetBiography("123", "U2", "mbid-1234")).To(Equal("U2 é uma das mais importantes bandas de rock de todos os tempos. Formada em 1976 em Dublin, composta por Bono (vocalista e guitarrista), The Edge (guitarrista, pianista e backing vocal), Adam Clayton (baixista), Larry Mullen, Jr. (baterista e percussionista).\n\nDesde a década de 80, U2 é uma das bandas mais populares no mundo. Seus shows são únicos e um verdadeiro festival de efeitos especiais, além de serem um dos que mais arrecadam anualmente. <a href=\"https://www.last.fm/music/U2\">Read more on Last.fm</a>"))
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
|
||||
})
|
||||
|
||||
It("returns an error if Last.FM call fails", func() {
|
||||
httpClient.Err = errors.New("error")
|
||||
_, err := agent.GetBiography("123", "U2", "mbid-1234")
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
|
||||
})
|
||||
|
||||
It("returns an error if Last.FM call returns an error", func() {
|
||||
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError3)), StatusCode: 200}
|
||||
_, err := agent.GetBiography("123", "U2", "mbid-1234")
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
|
||||
})
|
||||
|
||||
It("returns an error if Last.FM call returns an error 6 and mbid is empty", func() {
|
||||
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
|
||||
_, err := agent.GetBiography("123", "U2", "")
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
})
|
||||
|
||||
Context("MBID non existent in Last.FM", func() {
|
||||
It("calls again when the response is artist == [unknown]", func() {
|
||||
f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.unknown.json")
|
||||
httpClient.Res = http.Response{Body: f, StatusCode: 200}
|
||||
_, _ = agent.GetBiography("123", "U2", "mbid-1234")
|
||||
Expect(httpClient.RequestCount).To(Equal(2))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
|
||||
})
|
||||
It("calls again when last.fm returns an error 6", func() {
|
||||
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
|
||||
_, _ = agent.GetBiography("123", "U2", "mbid-1234")
|
||||
Expect(httpClient.RequestCount).To(Equal(2))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetSimilar", func() {
|
||||
var agent *lastfmAgent
|
||||
var httpClient *tests.FakeHttpClient
|
||||
BeforeEach(func() {
|
||||
httpClient = &tests.FakeHttpClient{}
|
||||
client := NewClient("API_KEY", "pt", httpClient)
|
||||
agent = lastFMConstructor(context.TODO()).(*lastfmAgent)
|
||||
agent.client = client
|
||||
})
|
||||
|
||||
It("returns similar artists", func() {
|
||||
f, _ := os.Open("tests/fixtures/lastfm.artist.getsimilar.json")
|
||||
httpClient.Res = http.Response{Body: f, StatusCode: 200}
|
||||
Expect(agent.GetSimilar("123", "U2", "mbid-1234", 2)).To(Equal([]agents.Artist{
|
||||
{Name: "Passengers", MBID: "e110c11f-1c94-4471-a350-c38f46b29389"},
|
||||
{Name: "INXS", MBID: "481bf5f9-2e7c-4c44-b08a-05b32bc7c00d"},
|
||||
}))
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
|
||||
})
|
||||
|
||||
It("returns an error if Last.FM call fails", func() {
|
||||
httpClient.Err = errors.New("error")
|
||||
_, err := agent.GetSimilar("123", "U2", "mbid-1234", 2)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
|
||||
})
|
||||
|
||||
It("returns an error if Last.FM call returns an error", func() {
|
||||
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError3)), StatusCode: 200}
|
||||
_, err := agent.GetSimilar("123", "U2", "mbid-1234", 2)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
|
||||
})
|
||||
|
||||
It("returns an error if Last.FM call returns an error 6 and mbid is empty", func() {
|
||||
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
|
||||
_, err := agent.GetSimilar("123", "U2", "", 2)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
})
|
||||
|
||||
Context("MBID non existent in Last.FM", func() {
|
||||
It("calls again when the response is artist == [unknown]", func() {
|
||||
f, _ := os.Open("tests/fixtures/lastfm.artist.getsimilar.unknown.json")
|
||||
httpClient.Res = http.Response{Body: f, StatusCode: 200}
|
||||
_, _ = agent.GetSimilar("123", "U2", "mbid-1234", 2)
|
||||
Expect(httpClient.RequestCount).To(Equal(2))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
|
||||
})
|
||||
It("calls again when last.fm returns an error 6", func() {
|
||||
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
|
||||
_, _ = agent.GetSimilar("123", "U2", "mbid-1234", 2)
|
||||
Expect(httpClient.RequestCount).To(Equal(2))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetTopSongs", func() {
|
||||
var agent *lastfmAgent
|
||||
var httpClient *tests.FakeHttpClient
|
||||
BeforeEach(func() {
|
||||
httpClient = &tests.FakeHttpClient{}
|
||||
client := NewClient("API_KEY", "pt", httpClient)
|
||||
agent = lastFMConstructor(context.TODO()).(*lastfmAgent)
|
||||
agent.client = client
|
||||
})
|
||||
|
||||
It("returns top songs", func() {
|
||||
f, _ := os.Open("tests/fixtures/lastfm.artist.gettoptracks.json")
|
||||
httpClient.Res = http.Response{Body: f, StatusCode: 200}
|
||||
Expect(agent.GetTopSongs("123", "U2", "mbid-1234", 2)).To(Equal([]agents.Song{
|
||||
{Name: "Beautiful Day", MBID: "f7f264d0-a89b-4682-9cd7-a4e7c37637af"},
|
||||
{Name: "With or Without You", MBID: "6b9a509f-6907-4a6e-9345-2f12da09ba4b"},
|
||||
}))
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
|
||||
})
|
||||
|
||||
It("returns an error if Last.FM call fails", func() {
|
||||
httpClient.Err = errors.New("error")
|
||||
_, err := agent.GetTopSongs("123", "U2", "mbid-1234", 2)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
|
||||
})
|
||||
|
||||
It("returns an error if Last.FM call returns an error", func() {
|
||||
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError3)), StatusCode: 200}
|
||||
_, err := agent.GetTopSongs("123", "U2", "mbid-1234", 2)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
|
||||
})
|
||||
|
||||
It("returns an error if Last.FM call returns an error 6 and mbid is empty", func() {
|
||||
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
|
||||
_, err := agent.GetTopSongs("123", "U2", "", 2)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(httpClient.RequestCount).To(Equal(1))
|
||||
})
|
||||
|
||||
Context("MBID non existent in Last.FM", func() {
|
||||
It("calls again when the response is artist == [unknown]", func() {
|
||||
f, _ := os.Open("tests/fixtures/lastfm.artist.gettoptracks.unknown.json")
|
||||
httpClient.Res = http.Response{Body: f, StatusCode: 200}
|
||||
_, _ = agent.GetTopSongs("123", "U2", "mbid-1234", 2)
|
||||
Expect(httpClient.RequestCount).To(Equal(2))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
|
||||
})
|
||||
It("calls again when last.fm returns an error 6", func() {
|
||||
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
|
||||
_, _ = agent.GetTopSongs("123", "U2", "mbid-1234", 2)
|
||||
Expect(httpClient.RequestCount).To(Equal(2))
|
||||
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,61 @@
|
||||
package lastfm
|
||||
|
||||
type Response struct {
|
||||
Artist Artist `json:"artist"`
|
||||
SimilarArtists SimilarArtists `json:"similarartists"`
|
||||
TopTracks TopTracks `json:"toptracks"`
|
||||
Error int `json:"error"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type Artist struct {
|
||||
Name string `json:"name"`
|
||||
MBID string `json:"mbid"`
|
||||
URL string `json:"url"`
|
||||
Image []ArtistImage `json:"image"`
|
||||
Streamable string `json:"streamable"`
|
||||
Stats struct {
|
||||
Listeners string `json:"listeners"`
|
||||
Plays string `json:"plays"`
|
||||
} `json:"stats"`
|
||||
Similar SimilarArtists `json:"similar"`
|
||||
Tags struct {
|
||||
Tag []ArtistTag `json:"tag"`
|
||||
} `json:"tags"`
|
||||
Bio ArtistBio `json:"bio"`
|
||||
}
|
||||
|
||||
type SimilarArtists struct {
|
||||
Artists []Artist `json:"artist"`
|
||||
Attr Attr `json:"@attr"`
|
||||
}
|
||||
|
||||
type Attr struct {
|
||||
Artist string `json:"artist"`
|
||||
}
|
||||
|
||||
type ArtistImage struct {
|
||||
URL string `json:"#text"`
|
||||
Size string `json:"size"`
|
||||
}
|
||||
|
||||
type ArtistTag struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type ArtistBio struct {
|
||||
Published string `json:"published"`
|
||||
Summary string `json:"summary"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type Track struct {
|
||||
Name string `json:"name"`
|
||||
MBID string `json:"mbid"`
|
||||
}
|
||||
|
||||
type TopTracks struct {
|
||||
Track []Track `json:"track"`
|
||||
Attr Attr `json:"@attr"`
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package lastfm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("LastFM responses", func() {
|
||||
Describe("Artist", func() {
|
||||
It("parses the response correctly", func() {
|
||||
var resp Response
|
||||
body, _ := ioutil.ReadFile("tests/fixtures/lastfm.artist.getinfo.json")
|
||||
err := json.Unmarshal(body, &resp)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
Expect(resp.Artist.Name).To(Equal("U2"))
|
||||
Expect(resp.Artist.MBID).To(Equal("a3cb23fc-acd3-4ce0-8f36-1e5aa6a18432"))
|
||||
Expect(resp.Artist.URL).To(Equal("https://www.last.fm/music/U2"))
|
||||
Expect(resp.Artist.Bio.Summary).To(ContainSubstring("U2 é uma das mais importantes bandas de rock de todos os tempos"))
|
||||
|
||||
similarArtists := []string{"Passengers", "INXS", "R.E.M.", "Simple Minds", "Bono"}
|
||||
for i, similar := range similarArtists {
|
||||
Expect(resp.Artist.Similar.Artists[i].Name).To(Equal(similar))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Describe("SimilarArtists", func() {
|
||||
It("parses the response correctly", func() {
|
||||
var resp Response
|
||||
body, _ := ioutil.ReadFile("tests/fixtures/lastfm.artist.getsimilar.json")
|
||||
err := json.Unmarshal(body, &resp)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
Expect(resp.SimilarArtists.Artists).To(HaveLen(2))
|
||||
Expect(resp.SimilarArtists.Artists[0].Name).To(Equal("Passengers"))
|
||||
Expect(resp.SimilarArtists.Artists[1].Name).To(Equal("INXS"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("TopTracks", func() {
|
||||
It("parses the response correctly", func() {
|
||||
var resp Response
|
||||
body, _ := ioutil.ReadFile("tests/fixtures/lastfm.artist.gettoptracks.json")
|
||||
err := json.Unmarshal(body, &resp)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
Expect(resp.TopTracks.Track).To(HaveLen(2))
|
||||
Expect(resp.TopTracks.Track[0].Name).To(Equal("Beautiful Day"))
|
||||
Expect(resp.TopTracks.Track[0].MBID).To(Equal("f7f264d0-a89b-4682-9cd7-a4e7c37637af"))
|
||||
Expect(resp.TopTracks.Track[1].Name).To(Equal("With or Without You"))
|
||||
Expect(resp.TopTracks.Track[1].MBID).To(Equal("6b9a509f-6907-4a6e-9345-2f12da09ba4b"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Error", func() {
|
||||
It("parses the error response correctly", func() {
|
||||
var error Response
|
||||
body := []byte(`{"error":3,"message":"Invalid Method - No method with that name in this package"}`)
|
||||
err := json.Unmarshal(body, &error)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
Expect(error.Error).To(Equal(3))
|
||||
Expect(error.Message).To(Equal("Invalid Method - No method with that name in this package"))
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user