Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .github/workflows/go_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v5

- name: Go setup
uses: actions/setup-go@v2
uses: actions/setup-go@v6
with:
go-version-file: "go.mod"

- run: go version

- name: Add module
run: go mod init github.com/coaxial/tizinger

- name: Run tests
run: make ci
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ testwatch:
watch -n 5 make test

ci:
go get -t -d -v ./... && go test -race -coverprofile=coverage.out ./... && go tool cover -func=coverage.out
go test -race -coverprofile=coverage.out ./... && go tool cover -func=coverage.out

lint:
golint ./...
go run github.com/golangci/golangci-lint/cmd/golangci-lint@latest run ./...

gettools:
go get -u golang.org/x/lint/golint
go install golang.org/x/lint/golint@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

testcovhtml:
go test -coverprofile=coverage.out ./... && go tool cover -html=coverage.out
20 changes: 11 additions & 9 deletions fip/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"strconv"
"time"
Expand Down Expand Up @@ -214,20 +214,18 @@ func makeRequest(req *http.Request, client *http.Client) (*http.Response, error)
return nil, err
}

logger.Info.Print(
fmt.Sprintf(
"received response %q, %d bytes",
response.Header.Get("content-type"),
response.ContentLength,
),
logger.Info.Printf(
"received response %q, %d bytes",
response.Header.Get("content-type"),
response.ContentLength,
)

return response, nil
}

// unmarshalResponse parses the API response and unmarshals it to JSON.
func unmarshalResponse(response *http.Response) (history historyResponse, err error) {
responseData, err := ioutil.ReadAll(response.Body)
responseData, err := io.ReadAll(response.Body)
defer response.Body.Close()
if err != nil {
errMsg := fmt.Sprintf("error while reading response data: %v", err)
Expand All @@ -242,7 +240,7 @@ func unmarshalResponse(response *http.Response) (history historyResponse, err er
response.StatusCode,
string(responseData),
)
logger.Error.Printf(errMsg)
logger.Error.Print(errMsg)
return history, errors.New(errMsg)
}

Expand Down Expand Up @@ -281,6 +279,10 @@ func extractEndCursor(JSON *historyResponse) (timestamp int64, err error) {
ec := JSON.Data.TimelineCursor.PageInfo.EndCursor
logger.Trace.Printf("converting %q to int64 timestamp", ec)
endCursorByte, err := base64.StdEncoding.DecodeString(ec)
if err != nil {
logger.Error.Printf("error decoding endCursor %q: %v", ec, err)
return timestamp, err
}
timestamp, err = strconv.ParseInt(string(endCursorByte), 0, 64)
if err != nil {
logger.Error.Printf("error decoding endCursor %q to timestamp: %v", ec, err)
Expand Down
37 changes: 25 additions & 12 deletions fip/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"log"
"net/http"
"strconv"
"testing"
"time"

Expand All @@ -20,8 +21,8 @@ func TestPlaylistErr(t *testing.T) {
resp.WriteHeader(http.StatusBadRequest)
resp.Header().Set("Content-Type", "application/html")
length, badReqResp := mocks.LoadFixture("../fixtures/fip/bad_req.json")
resp.Header().Set("Content-Length", string(length))
resp.Write(badReqResp)
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(badReqResp)
}
server := mocks.Server(http.HandlerFunc(handler))
defer server.Close()
Expand All @@ -39,8 +40,8 @@ func TestPlaylist(t *testing.T) {
resp.WriteHeader(http.StatusOK)
resp.Header().Set("Content-Type", "application/json; charset=utf-8")
length, historyJSON := mocks.LoadFixture("../fixtures/fip/history_response.json")
resp.Header().Set("Content-Length", string(length))
resp.Write(historyJSON)
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(historyJSON)
}
server := mocks.Server(http.HandlerFunc(handler))
defer server.Close()
Expand Down Expand Up @@ -71,8 +72,8 @@ func TestEmptyResponse(t *testing.T) {
resp.WriteHeader(http.StatusOK)
resp.Header().Set("Content-Type", "application/json; charset=utf-8")
emptyResp := []byte("{}")
resp.Header().Set("Content-Length", string(len(emptyResp)))
resp.Write(emptyResp)
resp.Header().Set("Content-Length", strconv.Itoa(len(emptyResp)))
_, _ = resp.Write(emptyResp)
}
server := mocks.Server(http.HandlerFunc(handler))
defer server.Close()
Expand All @@ -86,16 +87,28 @@ func TestEmptyResponse(t *testing.T) {
}

func ExampleAPIClient_Playlist() {
handler := func(resp http.ResponseWriter, req *http.Request) {
resp.WriteHeader(http.StatusOK)
resp.Header().Set("Content-Type", "application/json; charset=utf-8")
length, historyJSON := mocks.LoadFixture("../fixtures/fip/history_response.json")
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(historyJSON)
}
server := mocks.Server(http.HandlerFunc(handler))
defer server.Close()
SetEndpointURL(server.URL)
defer ResetEndpointURL()

var fipClient APIClient
// Get the list of 10 tracks played on FIP since 2020-07-25 00:30:00 GMT
tracks, err := fipClient.Playlist(1564014600, 10)
// Get the list of 10 tracks played on FIP since 2019-07-25 00:30:00 GMT (date
// doesn't matter as the fixture will return the same data for any timestamp)
tracks, err := fipClient.Playlist(1562284800, 10)
if err != nil {
log.Fatalf("Could not fetch FIP tracks: %v", err)
}

fmt.Println(tracks)
// Output: [{Riding the sun Howls Howls} {In the wake of adversity Dead Can Dance Within the realm of a dying sun} {Madame rêve Alain Bashung Osez Josephine} {The Planets op 32 : 3. Mercury, the Winged Messenger Orchestre Symphonique De Chicago Gustav Holst : Les Planètes} {Annie : The hard-knock life Alicia Morton BOF TV / Annie} {Bruce Lee Catastrophe Bruce Lee} {New comer 1 Walt Rockman Dusty fingers} {Cars Gary Numan The pleasure principle / Warriors} {Radio #1 Air 10000 hz legend} {Previsão do tempo Marcos Valle Previsao do tempo}]

// Output: [{Scar tissue Red Hot Chili Peppers Greatest hits} {Off the wall Jil Is Lucky Off the wall} {Kalimba (Flute mix) Freakniks Electro tunes} {Tsukikaage no rendezvous Keiko Mari Nippon girls: Japanese pop, beat & bossa nova 1966-1970} {Un petit poisson, un petit oiseau Juliette Greco Déshabillez-moi 1965-1969} {I want to be happy Ray Brown Brown Ray trio / Some of my best friends are guitarists} {I'm so happy I can't stop crying Sting Mercury falling} {Sambarilove (feat. Roubinho Jacobina) Chiara Civello Eclipse} {Retiens l'été Double Francoise Les bijoux} {Serenade nº13 en Sol Maj K 525 ""une petite musique de nuit"" : I. Allegro I Musici Mozart, pachelbel, albinoni}]
}

func TestEndCursorConvert(t *testing.T) {
Expand Down Expand Up @@ -124,8 +137,8 @@ func TestPlaylist200(t *testing.T) {
length, historyJSON := mocks.LoadFixture(fixture)
resp.WriteHeader(http.StatusOK)
resp.Header().Set("Content-Type", "application/json; charset=utf-8")
resp.Header().Set("Content-Length", string(length))
resp.Write(historyJSON)
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(historyJSON)
}
server := mocks.Server(http.HandlerFunc(handler))
defer server.Close()
Expand Down
15 changes: 15 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module github.com/coaxial/tizinger

go 1.25.3

require (
github.com/bclicn/color v0.0.0-20180711051946-108f2023dc84
github.com/gorilla/mux v1.8.1
github.com/stretchr/testify v1.11.1
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
)
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
github.com/bclicn/color v0.0.0-20180711051946-108f2023dc84 h1:cutFptzj+ospnc1PETUqcSVTH3VQ44Bi0rpt3nE9gvo=
github.com/bclicn/color v0.0.0-20180711051946-108f2023dc84/go.mod h1:Va9ap1qxjAWkIVaW1E9rH0aNgE8SDI5A4n8Ds8P0fAA=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
13 changes: 5 additions & 8 deletions tidal/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package tidal
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"regexp"
Expand All @@ -24,9 +24,6 @@ type APIClient struct{}
// baseURL can be overridden while testing to avoid live calls.
var baseURL = "https://api.tidalhifi.com/v1"

// jar is the cookie jar for the tidal client.
var jar http.CookieJar

// tidalClient is the client making requests to the Tidal API. It is defined
// here so that this client instance is reused.
var tidalClient = &http.Client{}
Expand Down Expand Up @@ -170,12 +167,12 @@ func queryTidal(
logger.Error.Printf("error %v", err)
return err
}
debugBody, _ := ioutil.ReadAll(body)
debugBody, _ := io.ReadAll(body)
debugyBodyString := string(debugBody)
qs := req.URL.RawQuery
h := req.Header
// Remove password from logs
re := regexp.MustCompile(`(?P<firstHalf>.*"password":")(?P<passwordValue>.*?)(?P<SecondHalf>".*)`)
re := regexp.MustCompile(`(?P<firstHalf>.*"password":")(?P<passwordValue>.*?)(?P<SecondHalf>.*)`)
debugyBodyString = re.ReplaceAllString(debugyBodyString, `$1<redacted>$3`)
logger.Trace.Printf("request: body: %#v", string(debugyBodyString))
logger.Trace.Printf("qs: %q", string(qs))
Expand Down Expand Up @@ -203,7 +200,7 @@ func queryTidal(
// length. This is up to the server and there isn't much that can be
// done about it.
logger.Info.Printf("received response %q, %d bytes", resp.Header.Get("Content-Type"), resp.ContentLength)
contents, err := ioutil.ReadAll(resp.Body)
contents, err := io.ReadAll(resp.Body)
// The request succeeds only for HTTP 200 OK or HTTP 201 Created (for
// playlist creation)
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusCreated {
Expand Down Expand Up @@ -252,7 +249,7 @@ func setToken() (err error) {
return err
}

tokens, err := ioutil.ReadAll(resp.Body)
tokens, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
logger.Error.Printf("error reading response: %v", err)
Expand Down
33 changes: 17 additions & 16 deletions tidal/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tidal

import (
"net/http"
"strconv"
"strings"
"testing"

Expand All @@ -15,8 +16,8 @@ func TestFetchingTokens(t *testing.T) {
length, tokensJSON := mocks.LoadFixture("../fixtures/tidal/tokens.json")
resp.WriteHeader(http.StatusOK)
resp.Header().Set("Content-Type", "application/json; charset=utf-8")
resp.Header().Set("Content-Length", string(length))
resp.Write(tokensJSON)
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(tokensJSON)
}
server := mocks.Server(http.HandlerFunc(handler))
defer server.Close()
Expand All @@ -33,8 +34,8 @@ func TestLogin(t *testing.T) {
length, JSON := mocks.LoadFixture("../fixtures/tidal/login_response.json")
resp.WriteHeader(http.StatusOK)
resp.Header().Set("Content-Type", "application/json;charset=UTF-8")
resp.Header().Set("Content-Length", string(length))
resp.Write(JSON)
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(JSON)
}
server := mocks.Server(http.HandlerFunc(handler))
defer server.Close()
Expand Down Expand Up @@ -85,8 +86,8 @@ func TestCreateEmptyPlaylist(t *testing.T) {
length, JSON := mocks.LoadFixture("../fixtures/tidal/playlist-create_response.json")
resp.WriteHeader(http.StatusCreated)
resp.Header().Set("Content-Type", "application/json;charset=UTF-8")
resp.Header().Set("Content-Length", string(length))
resp.Write(JSON)
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(JSON)
}
server := mocks.Server(http.HandlerFunc(handler))
defer server.Close()
Expand All @@ -110,8 +111,8 @@ func TestSearch(t *testing.T) {
length, JSON := mocks.LoadFixture("../fixtures/tidal/search-track_result_response.json")
resp.WriteHeader(http.StatusOK)
resp.Header().Set("Content-Type", "application/json;charset=UTF-8")
resp.Header().Set("Content-Length", string(length))
resp.Write(JSON)
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(JSON)
}
server := mocks.Server(http.HandlerFunc(handler))
defer server.Close()
Expand All @@ -131,8 +132,8 @@ func TestSearchNoResult(t *testing.T) {
length, JSON := mocks.LoadFixture("../fixtures/tidal/search-track_noresult_response.json")
resp.WriteHeader(http.StatusOK)
resp.Header().Set("Content-Type", "application/json;charset=UTF-8")
resp.Header().Set("Content-Length", string(length))
resp.Write(JSON)
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(JSON)
}
server := mocks.Server(http.HandlerFunc(handler))
defer server.Close()
Expand Down Expand Up @@ -168,15 +169,15 @@ func TestPopulatePlaylist(t *testing.T) {
length, JSON := mocks.LoadFixture("../fixtures/tidal/playlist-add_success_response.json")
resp.WriteHeader(http.StatusOK)
resp.Header().Set("Content-Type", "application/json;charset=UTF-8")
resp.Header().Set("Content-Length", string(length))
resp.Write(JSON)
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(JSON)
}
getLastUpdatedHandler := func(resp http.ResponseWriter, req *http.Request) {
length, JSON := mocks.LoadFixture("../fixtures/tidal/playlist-get_response.json")
resp.WriteHeader(http.StatusOK)
resp.Header().Set("Content-Type", "application/json;charset=UTF-8")
resp.Header().Set("Content-Length", string(length))
resp.Write(JSON)
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(JSON)
}
r := mux.NewRouter()
r.HandleFunc("/playlists/mockUUID/items", addTrackHandler)
Expand Down Expand Up @@ -216,8 +217,8 @@ func TestGetLastUpdated(t *testing.T) {
length, JSON := mocks.LoadFixture("../fixtures/tidal/playlist-get_response.json")
resp.WriteHeader(http.StatusOK)
resp.Header().Set("Content-Type", "application/json;charset=UTF-8")
resp.Header().Set("Content-Length", string(length))
resp.Write(JSON)
resp.Header().Set("Content-Length", strconv.Itoa(length))
_, _ = resp.Write(JSON)
}
server := mocks.Server(http.HandlerFunc(handler))
defer server.Close()
Expand Down
5 changes: 2 additions & 3 deletions utils/credentials/credentials.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Package credentials abstracts away access to the data contained withing the credentials.yml file.
package credentials

import (
"io/ioutil"
"os"
"sync"

"github.com/coaxial/tizinger/utils/logger"
Expand Down Expand Up @@ -34,7 +33,7 @@ var once sync.Once
// loadConfig reads and unmarshalls the credentials file.
func loadConfig() {
logger.Trace.Printf("reading credentials from %q", credentialsFile)
content, err := ioutil.ReadFile(credentialsFile)
content, err := os.ReadFile(credentialsFile)
if err != nil {
logger.Error.Fatalf("could not read %q: %v", credentialsFile, err)
return
Expand Down
4 changes: 2 additions & 2 deletions utils/mocks/server.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package mocks

import (
"io/ioutil"
"net/http"
"net/http/httptest"
"os"

"github.com/coaxial/tizinger/utils/logger"
)
Expand All @@ -18,7 +18,7 @@ func Server(handler http.Handler) *httptest.Server {
// LoadFixture is a helper for loading canned responses to use in handler
// functions.
func LoadFixture(path string) (length int, content []byte) {
content, err := ioutil.ReadFile(path)
content, err := os.ReadFile(path)
length = len(content)
if err != nil {
logger.Error.Fatalf("Could not load %q: %v", path, err)
Expand Down
Loading