From e1b93de47f3490c570364c1d3792daaaf0386a50 Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 11:32:08 +0200 Subject: [PATCH 01/18] Update structure for go 1.25 --- Makefile | 7 ++++--- go.mod | 15 +++++++++++++++ go.sum | 14 ++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 go.mod create mode 100644 go.sum diff --git a/Makefile b/Makefile index 0634fb6..1ec32f9 100644 --- a/Makefile +++ b/Makefile @@ -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 ./... + golangci-lint 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 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7ae24cf --- /dev/null +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e1d67c0 --- /dev/null +++ b/go.sum @@ -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= From 22774e5347a404bcf4bee5e81478d2e333cb9ccc Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 11:31:46 +0200 Subject: [PATCH 02/18] fix: update code for Go 1.25 compatibility --- fip/client.go | 15 ++++++--------- fip/client_test.go | 9 +++++---- tidal/client_test.go | 17 +++++++++-------- utils/mocks/server_test.go | 7 +++++-- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/fip/client.go b/fip/client.go index c2dfe27..98bb468 100644 --- a/fip/client.go +++ b/fip/client.go @@ -1,4 +1,3 @@ -// Package fip extracts playlist data from fip.fr package fip import ( @@ -214,13 +213,11 @@ 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.Print(fmt.Sprintf( + "received response %q, %d bytes", + response.Header.Get("content-type"), + response.ContentLength, + )) return response, nil } @@ -242,7 +239,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) } diff --git a/fip/client_test.go b/fip/client_test.go index 4ea9f2c..494a404 100644 --- a/fip/client_test.go +++ b/fip/client_test.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "net/http" + "strconv" "testing" "time" @@ -20,7 +21,7 @@ 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.Header().Set("Content-Length", strconv.Itoa(length)) resp.Write(badReqResp) } server := mocks.Server(http.HandlerFunc(handler)) @@ -39,7 +40,7 @@ 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.Header().Set("Content-Length", strconv.Itoa(length)) resp.Write(historyJSON) } server := mocks.Server(http.HandlerFunc(handler)) @@ -71,7 +72,7 @@ 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.Header().Set("Content-Length", strconv.Itoa(len(emptyResp))) resp.Write(emptyResp) } server := mocks.Server(http.HandlerFunc(handler)) @@ -124,7 +125,7 @@ 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.Header().Set("Content-Length", strconv.Itoa(length)) resp.Write(historyJSON) } server := mocks.Server(http.HandlerFunc(handler)) diff --git a/tidal/client_test.go b/tidal/client_test.go index 9ce61b5..7ec5fe8 100644 --- a/tidal/client_test.go +++ b/tidal/client_test.go @@ -2,6 +2,7 @@ package tidal import ( "net/http" + "strconv" "strings" "testing" @@ -15,7 +16,7 @@ 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.Header().Set("Content-Length", strconv.Itoa(length)) resp.Write(tokensJSON) } server := mocks.Server(http.HandlerFunc(handler)) @@ -33,7 +34,7 @@ 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.Header().Set("Content-Length", strconv.Itoa(length)) resp.Write(JSON) } server := mocks.Server(http.HandlerFunc(handler)) @@ -85,7 +86,7 @@ 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.Header().Set("Content-Length", strconv.Itoa(length)) resp.Write(JSON) } server := mocks.Server(http.HandlerFunc(handler)) @@ -110,7 +111,7 @@ 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.Header().Set("Content-Length", strconv.Itoa(length)) resp.Write(JSON) } server := mocks.Server(http.HandlerFunc(handler)) @@ -131,7 +132,7 @@ 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.Header().Set("Content-Length", strconv.Itoa(length)) resp.Write(JSON) } server := mocks.Server(http.HandlerFunc(handler)) @@ -168,14 +169,14 @@ 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.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.Header().Set("Content-Length", strconv.Itoa(length)) resp.Write(JSON) } r := mux.NewRouter() @@ -216,7 +217,7 @@ 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.Header().Set("Content-Length", strconv.Itoa(length)) resp.Write(JSON) } server := mocks.Server(http.HandlerFunc(handler)) diff --git a/utils/mocks/server_test.go b/utils/mocks/server_test.go index 619fa62..742fab1 100644 --- a/utils/mocks/server_test.go +++ b/utils/mocks/server_test.go @@ -1,13 +1,16 @@ package mocks -import "net/http" +import ( + "net/http" + "strconv" +) func ExampleServer() { handler := func(resp http.ResponseWriter, req *http.Request) { length, historyJSON := LoadFixture("../fixtures/fip/history_response.json") resp.WriteHeader(http.StatusOK) resp.Header().Set("Content-Type", "application/json; charset=utf-8") - resp.Header().Set("Content-Length", string(length)) + resp.Header().Set("Content-Length", strconv.Itoa(length)) resp.Write(historyJSON) } server := Server(http.HandlerFunc(handler)) From 4b8db1eebbd34b2f857e7a740f90f401974b306b Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 11:42:08 +0200 Subject: [PATCH 03/18] fix: update example test to use mock fixture instead of live API --- fip/client_test.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/fip/client_test.go b/fip/client_test.go index 494a404..00d82e0 100644 --- a/fip/client_test.go +++ b/fip/client_test.go @@ -87,16 +87,27 @@ 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-05 00:00:00 GMT (adjusted to match fixture) + 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) { From 93bc524c93cb075f7998865e3e517f47bedecb5f Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 11:47:34 +0200 Subject: [PATCH 04/18] Undo useless deletes --- fip/client.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fip/client.go b/fip/client.go index 98bb468..6975e21 100644 --- a/fip/client.go +++ b/fip/client.go @@ -1,3 +1,4 @@ +// Package fip extracts playlist data from fip.fr package fip import ( @@ -213,11 +214,13 @@ 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.Print( + fmt.Sprintf( + "received response %q, %d bytes", + response.Header.Get("content-type"), + response.ContentLength, + ), + ) return response, nil } From 01fef535c03f9e063ee694c4f8eafb918a578e9f Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 11:55:06 +0200 Subject: [PATCH 05/18] Keep using my lucky date --- fip/client_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fip/client_test.go b/fip/client_test.go index 00d82e0..d79bb2d 100644 --- a/fip/client_test.go +++ b/fip/client_test.go @@ -100,7 +100,8 @@ func ExampleAPIClient_Playlist() { defer ResetEndpointURL() var fipClient APIClient - // Get the list of 10 tracks played on FIP since 2019-07-05 00:00:00 GMT (adjusted to match fixture) + // 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) From f0b5cb73018945802cb806ed2afcd5d23c3383da Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 11:56:08 +0200 Subject: [PATCH 06/18] Remove init (already done) --- .github/workflows/go_test.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/go_test.yml b/.github/workflows/go_test.yml index 1ea9a1d..38ef54b 100644 --- a/.github/workflows/go_test.yml +++ b/.github/workflows/go_test.yml @@ -15,8 +15,5 @@ jobs: - name: Go setup uses: actions/setup-go@v2 - - name: Add module - run: go mod init github.com/coaxial/tizinger - - name: Run tests run: make ci From 97628ed8dacf399303f44e52f0081efa9b425339 Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 11:58:49 +0200 Subject: [PATCH 07/18] Specify go version --- .github/workflows/go_test.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go_test.yml b/.github/workflows/go_test.yml index 38ef54b..9254497 100644 --- a/.github/workflows/go_test.yml +++ b/.github/workflows/go_test.yml @@ -10,10 +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: Run tests run: make ci From a015bbdf67f063d02b2a11301d4fe5c20b868124 Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 12:00:44 +0200 Subject: [PATCH 08/18] fix: update Makefile for Go 1.20+ coverage tool compatibility --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1ec32f9..8771747 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ testwatch: watch -n 5 make test ci: - go test -race -coverprofile=coverage.out ./... && go tool cover -func=coverage.out + go test -race -coverprofile=coverage.out ./... && go tool covdata func -i=coverage.out lint: golangci-lint run ./... @@ -15,4 +15,4 @@ gettools: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest testcovhtml: - go test -coverprofile=coverage.out ./... && go tool cover -html=coverage.out + go test -coverprofile=coverage.out ./... && go tool covdata textfmt -i=coverage.out -o coverage.txt && go tool cover -html=coverage.txt From 654f450f977fcdbb386cf2948b0330330d24d827 Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 12:03:42 +0200 Subject: [PATCH 09/18] fix: clean coverage.out before generating profiles --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8771747..59b499c 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ testwatch: watch -n 5 make test ci: - go test -race -coverprofile=coverage.out ./... && go tool covdata func -i=coverage.out + rm -rf coverage.out && go test -race -coverprofile=coverage.out ./... && go tool covdata func -i=coverage.out lint: golangci-lint run ./... @@ -15,4 +15,4 @@ gettools: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest testcovhtml: - go test -coverprofile=coverage.out ./... && go tool covdata textfmt -i=coverage.out -o coverage.txt && go tool cover -html=coverage.txt + rm -rf coverage.out && go test -coverprofile=coverage.out ./... && go tool covdata textfmt -i=coverage.out -o coverage.txt && go tool cover -html=coverage.txt From bc8f94abcde0e0ed36fbe928d6808e89357fc52e Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 12:19:03 +0200 Subject: [PATCH 10/18] ci: simplify coverage commands in Makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 59b499c..171eeea 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ testwatch: watch -n 5 make test ci: - rm -rf coverage.out && go test -race -coverprofile=coverage.out ./... && go tool covdata func -i=coverage.out + go test -race -coverprofile=coverage ./... lint: golangci-lint run ./... @@ -15,4 +15,4 @@ gettools: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest testcovhtml: - rm -rf coverage.out && go test -coverprofile=coverage.out ./... && go tool covdata textfmt -i=coverage.out -o coverage.txt && go tool cover -html=coverage.txt + go test -coverprofile=coverage ./... && go tool covdata textfmt -i=coverage -o=coverage.txt && go tool cover -html=coverage.txt From a745eaca6ecf96ba4ea74adcb9c6a2048d1b24eb Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 12:19:05 +0200 Subject: [PATCH 11/18] ci: add function and overall coverage stats to ci target --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 171eeea..f383cd6 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ testwatch: watch -n 5 make test ci: - go test -race -coverprofile=coverage ./... + go test -race -coverprofile=coverage ./... && go tool cover -func=coverage lint: golangci-lint run ./... From 16524b35daacfecb2af6cc8e3f39a531328ff4ce Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 12:26:27 +0200 Subject: [PATCH 12/18] Use newer coverage format --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index f383cd6..1ec32f9 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ testwatch: watch -n 5 make test ci: - go test -race -coverprofile=coverage ./... && go tool cover -func=coverage + go test -race -coverprofile=coverage.out ./... && go tool cover -func=coverage.out lint: golangci-lint run ./... @@ -15,4 +15,4 @@ gettools: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest testcovhtml: - go test -coverprofile=coverage ./... && go tool covdata textfmt -i=coverage -o=coverage.txt && go tool cover -html=coverage.txt + go test -coverprofile=coverage.out ./... && go tool cover -html=coverage.out From 603958072b5e9f94831fc539279fa4592fb077b2 Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 12:28:06 +0200 Subject: [PATCH 13/18] build: update lint target to use go run for golangci-lint --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1ec32f9..15b05f5 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ ci: go test -race -coverprofile=coverage.out ./... && go tool cover -func=coverage.out lint: - golangci-lint run ./... + go run github.com/golangci/golangci-lint/cmd/golangci-lint@latest run ./... gettools: go install golang.org/x/lint/golint@latest From 6a153adf81930c4369cf4851b4bd641d8e7fca47 Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 12:31:25 +0200 Subject: [PATCH 14/18] fix: resolve golangci-lint errors in Go code --- fip/client.go | 18 ++++++++++-------- fip/client_test.go | 4 ++-- tidal/client.go | 2 +- utils/mocks/server.go | 4 ++-- utils/mocks/server_test.go | 2 +- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/fip/client.go b/fip/client.go index 6975e21..498905f 100644 --- a/fip/client.go +++ b/fip/client.go @@ -6,7 +6,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "net/http" "strconv" "time" @@ -214,12 +214,10 @@ 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 @@ -227,7 +225,7 @@ func makeRequest(req *http.Request, client *http.Client) (*http.Response, error) // 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) @@ -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) diff --git a/fip/client_test.go b/fip/client_test.go index d79bb2d..250453d 100644 --- a/fip/client_test.go +++ b/fip/client_test.go @@ -22,7 +22,7 @@ func TestPlaylistErr(t *testing.T) { resp.Header().Set("Content-Type", "application/html") length, badReqResp := mocks.LoadFixture("../fixtures/fip/bad_req.json") resp.Header().Set("Content-Length", strconv.Itoa(length)) - resp.Write(badReqResp) + _, _ = resp.Write(badReqResp) } server := mocks.Server(http.HandlerFunc(handler)) defer server.Close() @@ -41,7 +41,7 @@ func TestPlaylist(t *testing.T) { 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) + _, _ = resp.Write(historyJSON) } server := mocks.Server(http.HandlerFunc(handler)) defer server.Close() diff --git a/tidal/client.go b/tidal/client.go index 7a118ab..b09022e 100644 --- a/tidal/client.go +++ b/tidal/client.go @@ -175,7 +175,7 @@ func queryTidal( qs := req.URL.RawQuery h := req.Header // Remove password from logs - re := regexp.MustCompile(`(?P.*"password":")(?P.*?)(?P".*)`) + re := regexp.MustCompile(`(?P.*"password":")(?P.*?)(?P.*)`) debugyBodyString = re.ReplaceAllString(debugyBodyString, `$1$3`) logger.Trace.Printf("request: body: %#v", string(debugyBodyString)) logger.Trace.Printf("qs: %q", string(qs)) diff --git a/utils/mocks/server.go b/utils/mocks/server.go index 4731158..773bb4d 100644 --- a/utils/mocks/server.go +++ b/utils/mocks/server.go @@ -1,9 +1,9 @@ package mocks import ( - "io/ioutil" "net/http" "net/http/httptest" + "os" "github.com/coaxial/tizinger/utils/logger" ) @@ -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) diff --git a/utils/mocks/server_test.go b/utils/mocks/server_test.go index 742fab1..c660f46 100644 --- a/utils/mocks/server_test.go +++ b/utils/mocks/server_test.go @@ -11,7 +11,7 @@ func ExampleServer() { resp.WriteHeader(http.StatusOK) resp.Header().Set("Content-Type", "application/json; charset=utf-8") resp.Header().Set("Content-Length", strconv.Itoa(length)) - resp.Write(historyJSON) + _, _ = resp.Write(historyJSON) } server := Server(http.HandlerFunc(handler)) defer server.Close() From 6a571737d292725d79b909a3e8d7456b2b1eb653 Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 13:04:37 +0200 Subject: [PATCH 15/18] fix: resolve linting errors for deprecated imports and unchecked errors --- fip/client_test.go | 6 +++--- tidal/client.go | 11 ++++------- tidal/types.go | 15 --------------- utils/credentials/credentials.go | 5 ++--- 4 files changed, 9 insertions(+), 28 deletions(-) diff --git a/fip/client_test.go b/fip/client_test.go index 250453d..0a070f9 100644 --- a/fip/client_test.go +++ b/fip/client_test.go @@ -73,7 +73,7 @@ func TestEmptyResponse(t *testing.T) { resp.Header().Set("Content-Type", "application/json; charset=utf-8") emptyResp := []byte("{}") resp.Header().Set("Content-Length", strconv.Itoa(len(emptyResp))) - resp.Write(emptyResp) + _, _ = resp.Write(emptyResp) } server := mocks.Server(http.HandlerFunc(handler)) defer server.Close() @@ -92,7 +92,7 @@ func ExampleAPIClient_Playlist() { 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) + _, _ = resp.Write(historyJSON) } server := mocks.Server(http.HandlerFunc(handler)) defer server.Close() @@ -138,7 +138,7 @@ func TestPlaylist200(t *testing.T) { resp.WriteHeader(http.StatusOK) resp.Header().Set("Content-Type", "application/json; charset=utf-8") resp.Header().Set("Content-Length", strconv.Itoa(length)) - resp.Write(historyJSON) + _, _ = resp.Write(historyJSON) } server := mocks.Server(http.HandlerFunc(handler)) defer server.Close() diff --git a/tidal/client.go b/tidal/client.go index b09022e..963c8ef 100644 --- a/tidal/client.go +++ b/tidal/client.go @@ -4,7 +4,7 @@ package tidal import ( "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "net/url" "regexp" @@ -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{} @@ -170,7 +167,7 @@ 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 @@ -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 { @@ -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) diff --git a/tidal/types.go b/tidal/types.go index 1e1bc8a..701e3cb 100644 --- a/tidal/types.go +++ b/tidal/types.go @@ -25,21 +25,6 @@ func (t tidalTimestamp) MarshalJSON() ([]byte, error) { return []byte(`"` + t.Time.Format("2006-01-02T15:04:05-0700") + `"`), nil } -// loginResponse is the JSON object returned from a successful login request. -type loginResponse struct { - SessionID string `json:"sessionId"` - CountryCode string `json:"countryCode"` - UserID int `json:"userId"` -} - -// trackResponse is the JSON object returned for a search request -type trackResponse struct { - Limit int `json:"limit"` - Offset int `json:"offset"` - TotalNumberOfItems int `json:"totalNumberOfItems"` - Tracks []track `json:"items"` -} - type artist struct { ID int `json:"id"` Name string `json:"name"` diff --git a/utils/credentials/credentials.go b/utils/credentials/credentials.go index 48a3037..282e33d 100644 --- a/utils/credentials/credentials.go +++ b/utils/credentials/credentials.go @@ -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" @@ -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 From 1a939b05b453deb67e56cb9d9ad80bf437d24f82 Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 13:07:28 +0200 Subject: [PATCH 16/18] fix: ignore resp.Write errors in test handlers --- tidal/client_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tidal/client_test.go b/tidal/client_test.go index 7ec5fe8..73b1b56 100644 --- a/tidal/client_test.go +++ b/tidal/client_test.go @@ -17,7 +17,7 @@ func TestFetchingTokens(t *testing.T) { resp.WriteHeader(http.StatusOK) resp.Header().Set("Content-Type", "application/json; charset=utf-8") resp.Header().Set("Content-Length", strconv.Itoa(length)) - resp.Write(tokensJSON) + _, _ = resp.Write(tokensJSON) } server := mocks.Server(http.HandlerFunc(handler)) defer server.Close() @@ -35,7 +35,7 @@ func TestLogin(t *testing.T) { resp.WriteHeader(http.StatusOK) resp.Header().Set("Content-Type", "application/json;charset=UTF-8") resp.Header().Set("Content-Length", strconv.Itoa(length)) - resp.Write(JSON) + _, _ = resp.Write(JSON) } server := mocks.Server(http.HandlerFunc(handler)) defer server.Close() @@ -87,7 +87,7 @@ func TestCreateEmptyPlaylist(t *testing.T) { resp.WriteHeader(http.StatusCreated) resp.Header().Set("Content-Type", "application/json;charset=UTF-8") resp.Header().Set("Content-Length", strconv.Itoa(length)) - resp.Write(JSON) + _, _ = resp.Write(JSON) } server := mocks.Server(http.HandlerFunc(handler)) defer server.Close() From 78d0709086f9dbafecd4667477e5837fcc43cc73 Mon Sep 17 00:00:00 2001 From: coaxial Date: Tue, 14 Oct 2025 13:08:11 +0200 Subject: [PATCH 17/18] fix: handle resp.Write errors in test handlers --- tidal/client_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tidal/client_test.go b/tidal/client_test.go index 73b1b56..d687d71 100644 --- a/tidal/client_test.go +++ b/tidal/client_test.go @@ -112,7 +112,7 @@ func TestSearch(t *testing.T) { resp.WriteHeader(http.StatusOK) resp.Header().Set("Content-Type", "application/json;charset=UTF-8") resp.Header().Set("Content-Length", strconv.Itoa(length)) - resp.Write(JSON) + _, _ = resp.Write(JSON) } server := mocks.Server(http.HandlerFunc(handler)) defer server.Close() @@ -133,7 +133,7 @@ func TestSearchNoResult(t *testing.T) { resp.WriteHeader(http.StatusOK) resp.Header().Set("Content-Type", "application/json;charset=UTF-8") resp.Header().Set("Content-Length", strconv.Itoa(length)) - resp.Write(JSON) + _, _ = resp.Write(JSON) } server := mocks.Server(http.HandlerFunc(handler)) defer server.Close() @@ -170,14 +170,14 @@ func TestPopulatePlaylist(t *testing.T) { resp.WriteHeader(http.StatusOK) resp.Header().Set("Content-Type", "application/json;charset=UTF-8") resp.Header().Set("Content-Length", strconv.Itoa(length)) - resp.Write(JSON) + _, _ = 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", strconv.Itoa(length)) - resp.Write(JSON) + _, _ = resp.Write(JSON) } r := mux.NewRouter() r.HandleFunc("/playlists/mockUUID/items", addTrackHandler) @@ -218,7 +218,7 @@ func TestGetLastUpdated(t *testing.T) { resp.WriteHeader(http.StatusOK) resp.Header().Set("Content-Type", "application/json;charset=UTF-8") resp.Header().Set("Content-Length", strconv.Itoa(length)) - resp.Write(JSON) + _, _ = resp.Write(JSON) } server := mocks.Server(http.HandlerFunc(handler)) defer server.Close() From 370a79d3556b2db087e89ad3e30ef21d18342aff Mon Sep 17 00:00:00 2001 From: coaxial Date: Sun, 19 Oct 2025 10:40:53 +0200 Subject: [PATCH 18/18] Restore Tidal types --- tidal/types.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tidal/types.go b/tidal/types.go index 701e3cb..1e1bc8a 100644 --- a/tidal/types.go +++ b/tidal/types.go @@ -25,6 +25,21 @@ func (t tidalTimestamp) MarshalJSON() ([]byte, error) { return []byte(`"` + t.Time.Format("2006-01-02T15:04:05-0700") + `"`), nil } +// loginResponse is the JSON object returned from a successful login request. +type loginResponse struct { + SessionID string `json:"sessionId"` + CountryCode string `json:"countryCode"` + UserID int `json:"userId"` +} + +// trackResponse is the JSON object returned for a search request +type trackResponse struct { + Limit int `json:"limit"` + Offset int `json:"offset"` + TotalNumberOfItems int `json:"totalNumberOfItems"` + Tracks []track `json:"items"` +} + type artist struct { ID int `json:"id"` Name string `json:"name"`