package genius import ( "context" "errors" "io" "net/http" "strings" "testing" "github.com/stretchr/testify/require" ) type transportFunc func(*http.Request) (*http.Response, error) func (f transportFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) } func TestLyricsFormatting(t *testing.T) { text, err := parseLyrics([]byte(`
Not lyrics
Title
[Куплет 1]
Привет & мир

Next lineEmbed
[Припев]
Again
`)) require.NoError(t, err) require.Equal(t, "[Куплет 1]\nПривет & мир\n\nNext line\n\n[Припев]\nAgain", text) } func TestUnavailable(t *testing.T) { for _, page := range []string{`

Just a moment

`, `
`, ``} { _, err := parseLyrics([]byte(page)) require.ErrorIs(t, err, ErrLyricsUnavailable) } } func TestEmbeddedLyrics(t *testing.T) { text, err := parseLyrics([]byte(``)) require.NoError(t, err) require.Equal(t, "[Verse]\nIt's \"fine\" & good\nПривет", text) } func TestURLValidation(t *testing.T) { c := New("secret") c.http.Transport = transportFunc(func(*http.Request) (*http.Response, error) { t.Fatal("unexpected network call"); return nil, nil }) for _, u := range []string{"https://evil.test/song-lyrics", "http://genius.com/song-lyrics", "https://genius.com:443/song-lyrics", "https://secret@genius.com/song-lyrics", "https://genius.com/artists/test"} { _, err := c.FetchLyrics(context.Background(), u) require.Error(t, err) } } func TestRequests(t *testing.T) { c := New("secret") c.http.Transport = transportFunc(func(r *http.Request) (*http.Response, error) { body := `
Synthetic test text
` if r.URL.Host == "api.genius.com" { require.Equal(t, "Bearer secret", r.Header.Get("Authorization")) require.Equal(t, "artist & title", r.URL.Query().Get("q")) body = `{"response":{"hits":[{"type":"song","result":{"id":12,"title":"Example","url":"https://genius.com/example-lyrics","primary_artist":{"name":"Artist"}}}]}}` } else { require.Empty(t, r.Header.Get("Authorization")) require.Empty(t, r.URL.RawQuery) } return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(body)), Header: make(http.Header)}, nil }) songs, err := c.Search(context.Background(), "artist & title") require.NoError(t, err) require.Len(t, songs, 1) lyrics, err := c.FetchLyrics(context.Background(), songs[0].URL+"?tracking=1") require.NoError(t, err) require.Equal(t, "Synthetic test text", lyrics.Text) require.Equal(t, songs[0].URL, lyrics.SourceURL) require.False(t, lyrics.FetchedAt.IsZero()) } func TestHTTPFailures(t *testing.T) { for _, status := range []int{401, 403, 404, 429, 500} { c := New("secret") c.http.Transport = transportFunc(func(*http.Request) (*http.Response, error) { return &http.Response{StatusCode: status, Header: http.Header{"Retry-After": {"60"}}, Body: io.NopCloser(strings.NewReader("private upstream body"))}, nil }) _, err := c.Search(context.Background(), "test") var httpErr *HTTPError require.True(t, errors.As(err, &httpErr)) require.Equal(t, status, httpErr.StatusCode) require.Equal(t, "60", httpErr.RetryAfter) require.NotContains(t, err.Error(), "private") } } func TestRedirects(t *testing.T) { c := New("secret") first, _ := http.NewRequest("GET", "https://api.genius.com/search", nil) for _, endpoint := range []string{"http://api.genius.com/search", "https://genius.com/song-lyrics", "https://evil.test/"} { req, _ := http.NewRequest("GET", endpoint, nil) require.Error(t, c.http.CheckRedirect(req, []*http.Request{first})) } }