diff --git a/scrobble/lastfm/client.go b/scrobble/lastfm/client.go index 765285f..5e87e43 100644 --- a/scrobble/lastfm/client.go +++ b/scrobble/lastfm/client.go @@ -29,10 +29,12 @@ type Client struct { httpClient *http.Client } +func NewClientCustom(httpClient *http.Client) *Client { + return &Client{httpClient: httpClient} +} + func NewClient() *Client { - return &Client{ - httpClient: http.DefaultClient, - } + return NewClientCustom(http.DefaultClient) } func getParamSignature(params url.Values, secret string) string { diff --git a/scrobble/lastfm/client_test.go b/scrobble/lastfm/client_test.go index c968ad3..58df4c7 100644 --- a/scrobble/lastfm/client_test.go +++ b/scrobble/lastfm/client_test.go @@ -1,44 +1,21 @@ package lastfm import ( - "context" "crypto/md5" - "crypto/tls" - _ "embed" "encoding/xml" "fmt" - "net" "net/http" - "net/http/httptest" "net/url" "testing" "github.com/stretchr/testify/require" + "go.senan.xyz/gonic/scrobble/lastfm/mockclient" ) -func httpClientMock(handler http.Handler) (http.Client, func()) { - server := httptest.NewTLSServer(handler) - client := http.Client{ - Transport: &http.Transport{ - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return net.Dial(network, server.Listener.Addr().String()) - }, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, //nolint:gosec - }, - }, - } - - return client, server.Close -} - -//go:embed testdata/artist_get_info_response.xml -var artistGetInfoResponse string - func TestArtistGetInfo(t *testing.T) { // arrange require := require.New(t) - httpClient, shutdown := httpClientMock(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := Client{mockclient.New(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(http.MethodGet, r.Method) require.Equal(url.Values{ "method": []string{"artist.getInfo"}, @@ -49,11 +26,8 @@ func TestArtistGetInfo(t *testing.T) { require.Equal(baseURL, "https://"+r.Host+r.URL.Path) w.WriteHeader(http.StatusOK) - w.Write([]byte(artistGetInfoResponse)) - })) - defer shutdown() - - client := Client{&httpClient} + w.Write(mockclient.ArtistGetInfoResponse) + })} // act actual, err := client.ArtistGetInfo("apiKey1", "Artist 1") @@ -118,10 +92,10 @@ func TestArtistGetInfo(t *testing.T) { }, actual) } -func TestArtistGetInfo_clientRequestFails(t *testing.T) { +func TestArtistGetInfoClientRequestFails(t *testing.T) { // arrange require := require.New(t) - httpClient, shutdown := httpClientMock(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := Client{mockclient.New(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(http.MethodGet, r.Method) require.Equal(url.Values{ "method": []string{"artist.getInfo"}, @@ -132,10 +106,7 @@ func TestArtistGetInfo_clientRequestFails(t *testing.T) { require.Equal(baseURL, "https://"+r.Host+r.URL.Path) w.WriteHeader(http.StatusInternalServerError) - })) - defer shutdown() - - client := Client{&httpClient} + })} // act actual, err := client.ArtistGetInfo("apiKey1", "Artist 1") @@ -145,13 +116,10 @@ func TestArtistGetInfo_clientRequestFails(t *testing.T) { require.Zero(actual) } -//go:embed testdata/artist_get_top_tracks_response.xml -var artistGetTopTracksResponse string - func TestArtistGetTopTracks(t *testing.T) { // arrange require := require.New(t) - httpClient, shutdown := httpClientMock(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := Client{mockclient.New(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(http.MethodGet, r.Method) require.Equal(url.Values{ "method": []string{"artist.getTopTracks"}, @@ -162,11 +130,8 @@ func TestArtistGetTopTracks(t *testing.T) { require.Equal(baseURL, "https://"+r.Host+r.URL.Path) w.WriteHeader(http.StatusOK) - w.Write([]byte(artistGetTopTracksResponse)) - })) - defer shutdown() - - client := Client{&httpClient} + w.Write(mockclient.ArtistGetTopTracksResponse) + })} // act actual, err := client.ArtistGetTopTracks("apiKey1", "artist1") @@ -222,7 +187,7 @@ func TestArtistGetTopTracks(t *testing.T) { func TestArtistGetTopTracks_clientRequestFails(t *testing.T) { // arrange require := require.New(t) - httpClient, shutdown := httpClientMock(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := Client{mockclient.New(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(http.MethodGet, r.Method) require.Equal(url.Values{ "method": []string{"artist.getTopTracks"}, @@ -233,10 +198,7 @@ func TestArtistGetTopTracks_clientRequestFails(t *testing.T) { require.Equal(baseURL, "https://"+r.Host+r.URL.Path) w.WriteHeader(http.StatusInternalServerError) - })) - defer shutdown() - - client := Client{&httpClient} + })} // act actual, err := client.ArtistGetTopTracks("apiKey1", "artist1") @@ -246,13 +208,10 @@ func TestArtistGetTopTracks_clientRequestFails(t *testing.T) { require.Zero(actual) } -//go:embed testdata/artist_get_similar_response.xml -var artistGetSimilarResponse string - func TestArtistGetSimilar(t *testing.T) { // arrange require := require.New(t) - httpClient, shutdown := httpClientMock(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := Client{mockclient.New(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(http.MethodGet, r.Method) require.Equal(url.Values{ "method": []string{"artist.getSimilar"}, @@ -263,11 +222,8 @@ func TestArtistGetSimilar(t *testing.T) { require.Equal(baseURL, "https://"+r.Host+r.URL.Path) w.WriteHeader(http.StatusOK) - w.Write([]byte(artistGetSimilarResponse)) - })) - defer shutdown() - - client := Client{&httpClient} + w.Write(mockclient.ArtistGetSimilarResponse) + })} // act actual, err := client.ArtistGetSimilar("apiKey1", "artist1") @@ -331,7 +287,7 @@ func TestArtistGetSimilar(t *testing.T) { func TestArtistGetSimilar_clientRequestFails(t *testing.T) { // arrange require := require.New(t) - httpClient, shutdown := httpClientMock(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := Client{mockclient.New(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(http.MethodGet, r.Method) require.Equal(url.Values{ "method": []string{"artist.getSimilar"}, @@ -342,10 +298,7 @@ func TestArtistGetSimilar_clientRequestFails(t *testing.T) { require.Equal(baseURL, "https://"+r.Host+r.URL.Path) w.WriteHeader(http.StatusInternalServerError) - })) - defer shutdown() - - client := Client{&httpClient} + })} // act actual, err := client.ArtistGetSimilar("apiKey1", "artist1") @@ -355,13 +308,10 @@ func TestArtistGetSimilar_clientRequestFails(t *testing.T) { require.Zero(actual) } -//go:embed testdata/track_get_similar_response.xml -var trackGetSimilarResponse string - func TestTrackGetSimilarTracks(t *testing.T) { // arrange require := require.New(t) - httpClient, shutdown := httpClientMock(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := Client{mockclient.New(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(http.MethodGet, r.Method) require.Equal(url.Values{ "method": []string{"track.getSimilar"}, @@ -373,11 +323,8 @@ func TestTrackGetSimilarTracks(t *testing.T) { require.Equal(baseURL, "https://"+r.Host+r.URL.Path) w.WriteHeader(http.StatusOK) - w.Write([]byte(trackGetSimilarResponse)) - })) - defer shutdown() - - client := Client{&httpClient} + w.Write(mockclient.TrackGetSimilarResponse) + })} // act actual, err := client.TrackGetSimilarTracks("apiKey1", "artist1", "track1") @@ -430,7 +377,7 @@ func TestTrackGetSimilarTracks(t *testing.T) { func TestTrackGetSimilarTracks_clientRequestFails(t *testing.T) { // arrange require := require.New(t) - httpClient, shutdown := httpClientMock(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := Client{mockclient.New(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(http.MethodGet, r.Method) require.Equal(url.Values{ "method": []string{"track.getSimilar"}, @@ -442,10 +389,7 @@ func TestTrackGetSimilarTracks_clientRequestFails(t *testing.T) { require.Equal(baseURL, "https://"+r.Host+r.URL.Path) w.WriteHeader(http.StatusInternalServerError) - })) - defer shutdown() - - client := Client{&httpClient} + })} // act actual, err := client.TrackGetSimilarTracks("apiKey1", "artist1", "track1") @@ -455,13 +399,10 @@ func TestTrackGetSimilarTracks_clientRequestFails(t *testing.T) { require.Zero(actual) } -//go:embed testdata/get_session_response.xml -var getSessionResponse string - func TestGetSession(t *testing.T) { // arrange require := require.New(t) - httpClient, shutdown := httpClientMock(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := Client{mockclient.New(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(http.MethodGet, r.Method) require.Equal(url.Values{ "method": []string{"auth.getSession"}, @@ -473,11 +414,8 @@ func TestGetSession(t *testing.T) { require.Equal(baseURL, "https://"+r.Host+r.URL.Path) w.WriteHeader(http.StatusOK) - w.Write([]byte(getSessionResponse)) - })) - defer shutdown() - - client := Client{&httpClient} + w.Write(mockclient.GetSessionResponse) + })} // act actual, err := client.GetSession("apiKey1", "secret1", "token1") @@ -487,10 +425,10 @@ func TestGetSession(t *testing.T) { require.Equal("sessionKey1", actual) } -func TestGetSession_clientRequestFails(t *testing.T) { +func TestGetSessioeClientRequestFails(t *testing.T) { // arrange require := require.New(t) - httpClient, shutdown := httpClientMock(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := Client{mockclient.New(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(http.MethodGet, r.Method) require.Equal(url.Values{ "method": []string{"auth.getSession"}, @@ -502,10 +440,7 @@ func TestGetSession_clientRequestFails(t *testing.T) { require.Equal(baseURL, "https://"+r.Host+r.URL.Path) w.WriteHeader(http.StatusInternalServerError) - })) - defer shutdown() - - client := Client{&httpClient} + })} // act actual, err := client.GetSession("apiKey1", "secret1", "token1") diff --git a/scrobble/lastfm/testdata/artist_get_info_response.xml b/scrobble/lastfm/mockclient/artist_get_info_response.xml similarity index 100% rename from scrobble/lastfm/testdata/artist_get_info_response.xml rename to scrobble/lastfm/mockclient/artist_get_info_response.xml diff --git a/scrobble/lastfm/testdata/artist_get_similar_response.xml b/scrobble/lastfm/mockclient/artist_get_similar_response.xml similarity index 100% rename from scrobble/lastfm/testdata/artist_get_similar_response.xml rename to scrobble/lastfm/mockclient/artist_get_similar_response.xml diff --git a/scrobble/lastfm/testdata/artist_get_top_tracks_response.xml b/scrobble/lastfm/mockclient/artist_get_top_tracks_response.xml similarity index 100% rename from scrobble/lastfm/testdata/artist_get_top_tracks_response.xml rename to scrobble/lastfm/mockclient/artist_get_top_tracks_response.xml diff --git a/scrobble/lastfm/testdata/get_session_response.xml b/scrobble/lastfm/mockclient/get_session_response.xml similarity index 100% rename from scrobble/lastfm/testdata/get_session_response.xml rename to scrobble/lastfm/mockclient/get_session_response.xml diff --git a/scrobble/lastfm/mockclient/mockclient.go b/scrobble/lastfm/mockclient/mockclient.go new file mode 100644 index 0000000..2e3f8cf --- /dev/null +++ b/scrobble/lastfm/mockclient/mockclient.go @@ -0,0 +1,42 @@ +package mockclient + +import ( + "context" + "crypto/tls" + _ "embed" + "net" + "net/http" + "net/http/httptest" + "testing" +) + +func New(t testing.TB, handler http.HandlerFunc) *http.Client { + server := httptest.NewTLSServer(handler) + t.Cleanup(server.Close) + + return &http.Client{ + Transport: &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + return net.Dial(network, server.Listener.Addr().String()) + }, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, //nolint:gosec + }, + }, + } +} + +//go:embed artist_get_info_response.xml +var ArtistGetInfoResponse []byte + +//go:embed artist_get_top_tracks_response.xml +var ArtistGetTopTracksResponse []byte + +//go:embed artist_get_similar_response.xml +var ArtistGetSimilarResponse []byte + +//go:embed track_get_similar_response.xml +var TrackGetSimilarResponse []byte + +//go:embed get_session_response.xml +var GetSessionResponse []byte diff --git a/scrobble/lastfm/testdata/track_get_similar_response.xml b/scrobble/lastfm/mockclient/track_get_similar_response.xml similarity index 100% rename from scrobble/lastfm/testdata/track_get_similar_response.xml rename to scrobble/lastfm/mockclient/track_get_similar_response.xml diff --git a/scrobble/lastfm/scrobbler_test.go b/scrobble/lastfm/scrobbler_test.go index 9f2826b..31bd583 100644 --- a/scrobble/lastfm/scrobbler_test.go +++ b/scrobble/lastfm/scrobbler_test.go @@ -9,6 +9,7 @@ import ( _ "github.com/mattn/go-sqlite3" "github.com/stretchr/testify/require" "go.senan.xyz/gonic/db" + "go.senan.xyz/gonic/scrobble/lastfm/mockclient" ) func TestScrobble(t *testing.T) { @@ -44,7 +45,7 @@ func TestScrobble(t *testing.T) { stamp := time.Date(2023, 8, 12, 12, 34, 1, 200, time.UTC) - httpClient, shutdown := httpClientMock(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := Client{mockclient.New(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(http.MethodPost, r.Method) require.Equal(url.Values{ "album": []string{"album1"}, @@ -64,12 +65,10 @@ func TestScrobble(t *testing.T) { require.Equal(baseURL, "https://"+r.Host+r.URL.Path) w.WriteHeader(http.StatusOK) - w.Write([]byte(artistGetTopTracksResponse)) - })) - defer shutdown() + w.Write(mockclient.ArtistGetTopTracksResponse) + })} - client := &Client{&httpClient} - scrobbler := NewScrobbler(testDB, client) + scrobbler := NewScrobbler(testDB, &client) // act err = scrobbler.Scrobble(user, track, stamp, true) @@ -78,7 +77,7 @@ func TestScrobble(t *testing.T) { require.NoError(err) } -func TestScrobble_returnsWithoutLastFMSession(t *testing.T) { +func TestScrobbleReturnsWithoutLastFMSession(t *testing.T) { // arrange t.Parallel() require := require.New(t) @@ -92,7 +91,7 @@ func TestScrobble_returnsWithoutLastFMSession(t *testing.T) { require.NoError(err) } -func TestScrobble_failsWithoutLastFMAPIKey(t *testing.T) { +func TestScrobbleFailsWithoutLastFMAPIKey(t *testing.T) { // arrange t.Parallel() require := require.New(t)