diff --git a/persistence/helpers_test.go b/persistence/helpers_test.go index a47c6008..f6060825 100644 --- a/persistence/helpers_test.go +++ b/persistence/helpers_test.go @@ -39,12 +39,14 @@ var _ = Describe("Helpers", func() { m := &Model{ID: "123", AlbumId: "456", CreatedAt: now, UpdatedAt: &now, PlayCount: 2} args, err := toSQLArgs(m) Expect(err).To(BeNil()) - Expect(args).To(HaveKeyWithValue("id", "123")) - Expect(args).To(HaveKeyWithValue("album_id", "456")) - Expect(args).To(HaveKeyWithValue("play_count", 2)) - Expect(args).To(HaveKeyWithValue("updated_at", now.Format(time.RFC3339Nano))) - Expect(args).To(HaveKeyWithValue("created_at", now.Format(time.RFC3339Nano))) - Expect(args).ToNot(HaveKey("Embed")) + Expect(args).To(SatisfyAll( + HaveKeyWithValue("id", "123"), + HaveKeyWithValue("album_id", "456"), + HaveKeyWithValue("play_count", 2), + HaveKeyWithValue("updated_at", now.Format(time.RFC3339Nano)), + HaveKeyWithValue("created_at", now.Format(time.RFC3339Nano)), + Not(HaveKey("Embed")), + )) }) }) diff --git a/scanner/metadata/ffmpeg/ffmpeg_test.go b/scanner/metadata/ffmpeg/ffmpeg_test.go index 370c018e..8f8409d1 100644 --- a/scanner/metadata/ffmpeg/ffmpeg_test.go +++ b/scanner/metadata/ffmpeg/ffmpeg_test.go @@ -31,13 +31,15 @@ Input #0, ape, from './Capture/02 01 - Symphony No. 5 in C minor, Op. 67 I. Alle CatalogNumber : PLD 1201 ` md, _ := e.extractMetadata("tests/fixtures/test.mp3", output) - Expect(md).To(HaveKeyWithValue("catalognumber", []string{"PLD 1201"})) - Expect(md).To(HaveKeyWithValue("musicbrainz_trackid", []string{"ffe06940-727a-415a-b608-b7e45737f9d8"})) - Expect(md).To(HaveKeyWithValue("musicbrainz_albumid", []string{"71eb5e4a-90e2-4a31-a2d1-a96485fcb667"})) - Expect(md).To(HaveKeyWithValue("musicbrainz_artistid", []string{"1f9df192-a621-4f54-8850-2c5373b7eac9"})) - Expect(md).To(HaveKeyWithValue("musicbrainz_albumartistid", []string{"89ad4ac3-39f7-470e-963a-56509c546377"})) - Expect(md).To(HaveKeyWithValue("musicbrainz_albumtype", []string{"album"})) - Expect(md).To(HaveKeyWithValue("musicbrainz_albumcomment", []string{"MP3"})) + Expect(md).To(SatisfyAll( + HaveKeyWithValue("catalognumber", []string{"PLD 1201"}), + HaveKeyWithValue("musicbrainz_trackid", []string{"ffe06940-727a-415a-b608-b7e45737f9d8"}), + HaveKeyWithValue("musicbrainz_albumid", []string{"71eb5e4a-90e2-4a31-a2d1-a96485fcb667"}), + HaveKeyWithValue("musicbrainz_artistid", []string{"1f9df192-a621-4f54-8850-2c5373b7eac9"}), + HaveKeyWithValue("musicbrainz_albumartistid", []string{"89ad4ac3-39f7-470e-963a-56509c546377"}), + HaveKeyWithValue("musicbrainz_albumtype", []string{"album"}), + HaveKeyWithValue("musicbrainz_albumcomment", []string{"MP3"}), + )) }) It("detects embedded cover art correctly", func() { @@ -244,14 +246,16 @@ Input #0, mp3, from '/Users/deluan/Downloads/椎名林檎 - 加爾基 精液 栗 ALBUMARTISTSORT : Shiina, Ringo ` md, _ := e.extractMetadata("tests/fixtures/test.mp3", output) - Expect(md).To(HaveKeyWithValue("title", []string{"ドツペルゲンガー"})) - Expect(md).To(HaveKeyWithValue("album", []string{"加爾基 精液 栗ノ花"})) - Expect(md).To(HaveKeyWithValue("artist", []string{"椎名林檎"})) - Expect(md).To(HaveKeyWithValue("album_artist", []string{"椎名林檎"})) - Expect(md).To(HaveKeyWithValue("title-sort", []string{"Dopperugengā"})) - Expect(md).To(HaveKeyWithValue("albumsort", []string{"Kalk Samen Kuri No Hana"})) - Expect(md).To(HaveKeyWithValue("artist_sort", []string{"Shiina, Ringo"})) - Expect(md).To(HaveKeyWithValue("albumartistsort", []string{"Shiina, Ringo"})) + Expect(md).To(SatisfyAll( + HaveKeyWithValue("title", []string{"ドツペルゲンガー"}), + HaveKeyWithValue("album", []string{"加爾基 精液 栗ノ花"}), + HaveKeyWithValue("artist", []string{"椎名林檎"}), + HaveKeyWithValue("album_artist", []string{"椎名林檎"}), + HaveKeyWithValue("title-sort", []string{"Dopperugengā"}), + HaveKeyWithValue("albumsort", []string{"Kalk Samen Kuri No Hana"}), + HaveKeyWithValue("artist_sort", []string{"Shiina, Ringo"}), + HaveKeyWithValue("albumartistsort", []string{"Shiina, Ringo"}), + )) }) It("ignores cover comment", func() { @@ -310,11 +314,12 @@ Input #0, mp3, from '/Users/deluan/Music/Music/Media/_/Wyclef Jean - From the Hu replaygain: track gain - -1.480000, track peak - 0.000011, album gain - 3.215180, album peak - 0.000021, ` md, _ := e.extractMetadata("tests/fixtures/test.mp3", output) - Expect(md).To(HaveKeyWithValue("replaygain_track_gain", []string{"-1.48 dB"})) - Expect(md).To(HaveKeyWithValue("replaygain_track_peak", []string{"0.4512"})) - Expect(md).To(HaveKeyWithValue("replaygain_album_gain", []string{"+3.21518 dB"})) - Expect(md).To(HaveKeyWithValue("replaygain_album_peak", []string{"0.9125"})) - + Expect(md).To(SatisfyAll( + HaveKeyWithValue("replaygain_track_gain", []string{"-1.48 dB"}), + HaveKeyWithValue("replaygain_track_peak", []string{"0.4512"}), + HaveKeyWithValue("replaygain_album_gain", []string{"+3.21518 dB"}), + HaveKeyWithValue("replaygain_album_peak", []string{"0.9125"}), + )) }) It("parses lyrics with language code", func() { @@ -327,12 +332,14 @@ Input #0, mp3, from '/Users/deluan/Music/Music/Media/_/Wyclef Jean - From the Hu : [00:02.50]unspecified ` md, _ := e.extractMetadata("tests/fixtures/test.mp3", output) - Expect(md).To(HaveKeyWithValue("lyrics-eng", []string{ - "[00:00.00]This is\n[00:02.50]English", - })) - Expect(md).To(HaveKeyWithValue("lyrics-xxx", []string{ - "[00:00.00]This is\n[00:02.50]unspecified", - })) + Expect(md).To(SatisfyAll( + HaveKeyWithValue("lyrics-eng", []string{ + "[00:00.00]This is\n[00:02.50]English", + }), + HaveKeyWithValue("lyrics-xxx", []string{ + "[00:00.00]This is\n[00:02.50]unspecified", + }), + )) }) It("parses normal LYRICS tag", func() { diff --git a/server/subsonic/api.go b/server/subsonic/api.go index 6e9af19e..f7134e81 100644 --- a/server/subsonic/api.go +++ b/server/subsonic/api.go @@ -233,7 +233,7 @@ func h501(r chi.Router, paths ...string) { for _, path := range paths { handle := func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Cache-Control", "no-cache") - w.WriteHeader(501) + w.WriteHeader(http.StatusNotImplemented) _, _ = w.Write([]byte("This endpoint is not implemented, but may be in future releases")) } addHandler(r, path, handle) @@ -244,7 +244,7 @@ func h501(r chi.Router, paths ...string) { func h410(r chi.Router, paths ...string) { for _, path := range paths { handle := func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(410) + w.WriteHeader(http.StatusGone) _, _ = w.Write([]byte("This endpoint will not be implemented")) } addHandler(r, path, handle) @@ -276,7 +276,7 @@ func mapToSubsonicError(err error) subError { func sendError(w http.ResponseWriter, r *http.Request, err error) { subErr := mapToSubsonicError(err) response := newResponse() - response.Status = "failed" + response.Status = responses.StatusFailed response.Error = &responses.Error{Code: int32(subErr.code), Message: subErr.Error()} sendResponse(w, r, response) @@ -305,11 +305,10 @@ func sendResponse(w http.ResponseWriter, r *http.Request, payload *responses.Sub // This should never happen, but if it does, we need to know if err != nil { log.Error(r.Context(), "Error marshalling response", "format", f, err) - w.WriteHeader(http.StatusInternalServerError) - _, _ = w.Write([]byte("Internal Server Error: " + err.Error())) + sendError(w, r, err) return } - if payload.Status == "ok" { + if payload.Status == responses.StatusOK { if log.IsGreaterOrEqualTo(log.LevelTrace) { log.Debug(r.Context(), "API: Successful response", "endpoint", r.URL.Path, "status", "OK", "body", string(response)) } else { diff --git a/server/subsonic/api_test.go b/server/subsonic/api_test.go index b75af4f9..94282f87 100644 --- a/server/subsonic/api_test.go +++ b/server/subsonic/api_test.go @@ -24,12 +24,12 @@ var _ = Describe("sendResponse", func() { w = httptest.NewRecorder() r = httptest.NewRequest("GET", "/somepath", nil) payload = &responses.Subsonic{ - Status: "ok", + Status: responses.StatusOK, Version: "1.16.1", } }) - Context("when format is JSON", func() { + When("format is JSON", func() { It("should set Content-Type to application/json and return the correct body", func() { q := r.URL.Query() q.Add("f", "json") @@ -48,7 +48,7 @@ var _ = Describe("sendResponse", func() { }) }) - Context("when format is JSONP", func() { + When("format is JSONP", func() { It("should set Content-Type to application/javascript and return the correct callback body", func() { q := r.URL.Query() q.Add("f", "jsonp") @@ -60,8 +60,8 @@ var _ = Describe("sendResponse", func() { Expect(w.Header().Get("Content-Type")).To(Equal("application/javascript")) body := w.Body.String() Expect(body).To(SatisfyAll( - ContainSubstring("testCallback("), - ContainSubstring(")"), + HavePrefix("testCallback("), + HaveSuffix(")"), )) // Extract JSON from the JSONP response @@ -73,7 +73,7 @@ var _ = Describe("sendResponse", func() { }) }) - Context("when format is XML or unspecified", func() { + When("format is XML or unspecified", func() { It("should set Content-Type to application/xml and return the correct body", func() { // No format specified, expecting XML by default sendResponse(w, r, payload) @@ -87,19 +87,25 @@ var _ = Describe("sendResponse", func() { }) }) - Context("when an error occurs during marshalling", func() { - It("should return HTTP 500", func() { + When("an error occurs during marshalling", func() { + It("should return a fail response", func() { payload.Song = &responses.Child{ + // An +Inf value will cause an error when marshalling to JSON ReplayGain: responses.ReplayGain{TrackGain: math.Inf(1)}, - } // This will cause an error when marshalling to JSON + } q := r.URL.Query() q.Add("f", "json") r.URL.RawQuery = q.Encode() + sendResponse(w, r, payload) - Expect(w.Code).To(Equal(http.StatusInternalServerError)) - body := w.Body.String() - Expect(body).To(ContainSubstring("Internal Server Error")) + Expect(w.Code).To(Equal(http.StatusOK)) + var wrapper responses.JsonWrapper + err := json.Unmarshal(w.Body.Bytes(), &wrapper) + Expect(err).NotTo(HaveOccurred()) + Expect(wrapper.Subsonic.Status).To(Equal(responses.StatusFailed)) + Expect(wrapper.Subsonic.Version).To(Equal(payload.Version)) + Expect(wrapper.Subsonic.Error.Message).To(ContainSubstring("json: unsupported value: +Inf")) }) }) diff --git a/server/subsonic/helpers.go b/server/subsonic/helpers.go index ef524f1c..4a50745e 100644 --- a/server/subsonic/helpers.go +++ b/server/subsonic/helpers.go @@ -19,7 +19,7 @@ import ( func newResponse() *responses.Subsonic { return &responses.Subsonic{ - Status: "ok", + Status: responses.StatusOK, Version: Version, Type: consts.AppName, ServerVersion: consts.Version, diff --git a/server/subsonic/responses/responses.go b/server/subsonic/responses/responses.go index f771176f..d818eecc 100644 --- a/server/subsonic/responses/responses.go +++ b/server/subsonic/responses/responses.go @@ -61,6 +61,11 @@ type Subsonic struct { LyricsList *LyricsList `xml:"lyricsList,omitempty" json:"lyricsList,omitempty"` } +const ( + StatusOK = "ok" + StatusFailed = "failed" +) + type JsonWrapper struct { Subsonic Subsonic `json:"subsonic-response"` } diff --git a/server/subsonic/responses/responses_test.go b/server/subsonic/responses/responses_test.go index b525ea7e..a1c37a39 100644 --- a/server/subsonic/responses/responses_test.go +++ b/server/subsonic/responses/responses_test.go @@ -20,7 +20,7 @@ var _ = Describe("Responses", func() { var response *Subsonic BeforeEach(func() { response = &Subsonic{ - Status: "ok", + Status: StatusOK, Version: "1.8.0", Type: consts.AppName, ServerVersion: "v0.0.0",