mirror of
https://github.com/navidrome/navidrome.git
synced 2025-06-15 23:02:24 +03:00
Small fixes to response marshaling, introduced tests for response formats
This commit is contained in:
parent
b9fb5eb7ca
commit
dde130e84e
@ -17,7 +17,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func AddParams(endpoint string, params ...string) string {
|
func AddParams(endpoint string, params ...string) string {
|
||||||
url := fmt.Sprintf("%s?u=%s&p=%s&c=%s&v=%s", endpoint, testUser, testPassword, testClient, testVersion)
|
url := fmt.Sprintf("%s?u=%s&p=%s&c=%s&v=%s&f=json", endpoint, testUser, testPassword, testClient, testVersion)
|
||||||
if len(params) > 0 {
|
if len(params) > 0 {
|
||||||
url = url + "&" + strings.Join(params, "&")
|
url = url + "&" + strings.Join(params, "&")
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,7 @@ func (c *BaseAPIController) SendError(errorCode int, message ...interface{}) {
|
|||||||
func (c *BaseAPIController) SendResponse(response responses.Subsonic) {
|
func (c *BaseAPIController) SendResponse(response responses.Subsonic) {
|
||||||
f := c.GetString("f")
|
f := c.GetString("f")
|
||||||
if f == "json" {
|
if f == "json" {
|
||||||
type jsonWrapper struct {
|
w := &responses.JsonWrapper{Subsonic: response}
|
||||||
Subsonic responses.Subsonic `json:"subsonic-response"`
|
|
||||||
}
|
|
||||||
w := &jsonWrapper{Subsonic: response}
|
|
||||||
c.Data["json"] = &w
|
c.Data["json"] = &w
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
} else {
|
} else {
|
||||||
|
@ -62,6 +62,6 @@ func (c *GetIndexesController) Get() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
response := c.NewEmpty()
|
response := c.NewEmpty()
|
||||||
response.ArtistIndex = &res
|
response.Indexes = &res
|
||||||
c.SendResponse(response)
|
c.SendResponse(response)
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,19 @@ import (
|
|||||||
"github.com/deluan/gosonic/api/responses"
|
"github.com/deluan/gosonic/api/responses"
|
||||||
"github.com/deluan/gosonic/consts"
|
"github.com/deluan/gosonic/consts"
|
||||||
"github.com/deluan/gosonic/domain"
|
"github.com/deluan/gosonic/domain"
|
||||||
"github.com/deluan/gosonic/tests"
|
|
||||||
"github.com/deluan/gosonic/tests/mocks"
|
"github.com/deluan/gosonic/tests/mocks"
|
||||||
"github.com/deluan/gosonic/utils"
|
"github.com/deluan/gosonic/utils"
|
||||||
|
. "github.com/deluan/gosonic/tests"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
emptyResponse = `<indexes lastModified="1" ignoredArticles="The El La Los Las Le Les Os As O A"></indexes>`
|
emptyResponse = `{"indexes":{"ignoredArticles":"The El La Los Las Le Les Os As O A","lastModified":"1"}`
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetIndexes(t *testing.T) {
|
func TestGetIndexes(t *testing.T) {
|
||||||
tests.Init(t, false)
|
Init(t, false)
|
||||||
|
|
||||||
mockRepo := mocks.CreateMockArtistIndexRepo()
|
mockRepo := mocks.CreateMockArtistIndexRepo()
|
||||||
utils.DefineSingleton(new(domain.ArtistIndexRepository), func() domain.ArtistIndexRepository {
|
utils.DefineSingleton(new(domain.ArtistIndexRepository), func() domain.ArtistIndexRepository {
|
||||||
return mockRepo
|
return mockRepo
|
||||||
@ -56,13 +57,8 @@ func TestGetIndexes(t *testing.T) {
|
|||||||
Convey("Status code should be 200", func() {
|
Convey("Status code should be 200", func() {
|
||||||
So(w.Code, ShouldEqual, 200)
|
So(w.Code, ShouldEqual, 200)
|
||||||
})
|
})
|
||||||
Convey("It should return valid XML", func() {
|
|
||||||
v := new(string)
|
|
||||||
err := xml.Unmarshal(w.Body.Bytes(), &v)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
})
|
|
||||||
Convey("Then it should return an empty collection", func() {
|
Convey("Then it should return an empty collection", func() {
|
||||||
So(w.Body.String(), ShouldContainSubstring, emptyResponse)
|
So(UnindentJSON(w.Body.Bytes()), ShouldContainSubstring, emptyResponse)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
Convey("When the index is not empty", func() {
|
Convey("When the index is not empty", func() {
|
||||||
@ -85,7 +81,7 @@ func TestGetIndexes(t *testing.T) {
|
|||||||
|
|
||||||
_, w := Get(AddParams("/rest/getIndexes.view", "ifModifiedSince=2"), "TestGetIndexes")
|
_, w := Get(AddParams("/rest/getIndexes.view", "ifModifiedSince=2"), "TestGetIndexes")
|
||||||
|
|
||||||
So(w.Body.String(), ShouldContainSubstring, emptyResponse)
|
So(UnindentJSON(w.Body.Bytes()), ShouldContainSubstring, emptyResponse)
|
||||||
})
|
})
|
||||||
Convey("And it should return empty if 'ifModifiedSince' is the asme as tie index last update", func() {
|
Convey("And it should return empty if 'ifModifiedSince' is the asme as tie index last update", func() {
|
||||||
mockRepo.SetData(`[{"Id": "A","Artists": [
|
mockRepo.SetData(`[{"Id": "A","Artists": [
|
||||||
@ -95,7 +91,7 @@ func TestGetIndexes(t *testing.T) {
|
|||||||
|
|
||||||
_, w := Get(AddParams("/rest/getIndexes.view", "ifModifiedSince=1"), "TestGetIndexes")
|
_, w := Get(AddParams("/rest/getIndexes.view", "ifModifiedSince=1"), "TestGetIndexes")
|
||||||
|
|
||||||
So(w.Body.String(), ShouldContainSubstring, emptyResponse)
|
So(UnindentJSON(w.Body.Bytes()), ShouldContainSubstring, emptyResponse)
|
||||||
})
|
})
|
||||||
Reset(func() {
|
Reset(func() {
|
||||||
mockRepo.SetData("[]", 0)
|
mockRepo.SetData("[]", 0)
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package api_test
|
package api_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
. "github.com/deluan/gosonic/tests"
|
||||||
"github.com/deluan/gosonic/tests"
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetLicense(t *testing.T) {
|
func TestGetLicense(t *testing.T) {
|
||||||
tests.Init(t, false)
|
Init(t, false)
|
||||||
|
|
||||||
_, w := Get(AddParams("/rest/getLicense.view"), "TestGetLicense")
|
_, w := Get(AddParams("/rest/getLicense.view"), "TestGetLicense")
|
||||||
|
|
||||||
@ -17,10 +16,7 @@ func TestGetLicense(t *testing.T) {
|
|||||||
So(w.Code, ShouldEqual, 200)
|
So(w.Code, ShouldEqual, 200)
|
||||||
})
|
})
|
||||||
Convey("The license should always be valid", func() {
|
Convey("The license should always be valid", func() {
|
||||||
v := new(string)
|
So(UnindentJSON(w.Body.Bytes()), ShouldContainSubstring, `"license":{"valid":true}`)
|
||||||
err := xml.Unmarshal(w.Body.Bytes(), &v)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(w.Body.String(), ShouldContainSubstring, `license valid="true"`)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -3,13 +3,12 @@ package api_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"encoding/xml"
|
. "github.com/deluan/gosonic/tests"
|
||||||
"github.com/deluan/gosonic/tests"
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetMusicFolders(t *testing.T) {
|
func TestGetMusicFolders(t *testing.T) {
|
||||||
tests.Init(t, false)
|
Init(t, false)
|
||||||
|
|
||||||
_, w := Get(AddParams("/rest/getMusicFolders.view"), "TestGetMusicFolders")
|
_, w := Get(AddParams("/rest/getMusicFolders.view"), "TestGetMusicFolders")
|
||||||
|
|
||||||
@ -18,10 +17,7 @@ func TestGetMusicFolders(t *testing.T) {
|
|||||||
So(w.Code, ShouldEqual, 200)
|
So(w.Code, ShouldEqual, 200)
|
||||||
})
|
})
|
||||||
Convey("The response should include the default folder", func() {
|
Convey("The response should include the default folder", func() {
|
||||||
v := new(string)
|
So(UnindentJSON(w.Body.Bytes()), ShouldContainSubstring, `{"musicFolder":[{"id":"0","name":"iTunes Library"}]}`)
|
||||||
err := xml.Unmarshal(w.Body.Bytes(), &v)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(w.Body.String(), ShouldContainSubstring, `musicFolder id="0" name="iTunes Library"`)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
package api_test
|
package api_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
|
||||||
"github.com/deluan/gosonic/api/responses"
|
"github.com/deluan/gosonic/api/responses"
|
||||||
"github.com/deluan/gosonic/tests"
|
. "github.com/deluan/gosonic/tests"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
"testing"
|
"testing"
|
||||||
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPing(t *testing.T) {
|
func TestPing(t *testing.T) {
|
||||||
tests.Init(t, false)
|
Init(t, false)
|
||||||
|
|
||||||
_, w := Get(AddParams("/rest/ping.view"), "TestPing")
|
_, w := Get(AddParams("/rest/ping.view"), "TestPing")
|
||||||
|
|
||||||
@ -21,10 +21,11 @@ func TestPing(t *testing.T) {
|
|||||||
So(w.Body.Len(), ShouldBeGreaterThan, 0)
|
So(w.Body.Len(), ShouldBeGreaterThan, 0)
|
||||||
})
|
})
|
||||||
Convey("The result should be a valid ping response", func() {
|
Convey("The result should be a valid ping response", func() {
|
||||||
v := responses.Subsonic{}
|
v := responses.JsonWrapper{}
|
||||||
xml.Unmarshal(w.Body.Bytes(), &v)
|
err := json.Unmarshal(w.Body.Bytes(), &v)
|
||||||
So(v.Status, ShouldEqual, "ok")
|
So(err, ShouldBeNil)
|
||||||
So(v.Version, ShouldEqual, "1.0.0")
|
So(v.Subsonic.Status, ShouldEqual, "ok")
|
||||||
|
So(v.Subsonic.Version, ShouldEqual, "1.0.0")
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -9,7 +9,11 @@ type Subsonic struct {
|
|||||||
Error *Error `xml:",omitempty" json:"error,omitempty"`
|
Error *Error `xml:",omitempty" json:"error,omitempty"`
|
||||||
License *License `xml:",omitempty" json:"license,omitempty"`
|
License *License `xml:",omitempty" json:"license,omitempty"`
|
||||||
MusicFolders *MusicFolders `xml:",omitempty" json:"musicFolders,omitempty"`
|
MusicFolders *MusicFolders `xml:",omitempty" json:"musicFolders,omitempty"`
|
||||||
ArtistIndex *Indexes `xml:",omitempty" json:"indexes,omitempty"`
|
Indexes *Indexes `xml:",omitempty" json:"indexes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonWrapper struct {
|
||||||
|
Subsonic Subsonic `json:"subsonic-response"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Error struct {
|
type Error struct {
|
||||||
@ -31,7 +35,7 @@ type MusicFolder struct {
|
|||||||
|
|
||||||
type MusicFolders struct {
|
type MusicFolders struct {
|
||||||
XMLName xml.Name `xml:"musicFolders" json:"-"`
|
XMLName xml.Name `xml:"musicFolders" json:"-"`
|
||||||
Folders []MusicFolder `xml:"musicFolders" json:"musicFolder"`
|
Folders []MusicFolder `xml:"musicFolders" json:"musicFolder,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Artist struct {
|
type Artist struct {
|
||||||
@ -48,7 +52,7 @@ type Index struct {
|
|||||||
|
|
||||||
type Indexes struct {
|
type Indexes struct {
|
||||||
XMLName xml.Name `xml:"indexes" json:"-"`
|
XMLName xml.Name `xml:"indexes" json:"-"`
|
||||||
Index []Index `xml:"indexes" json:"index"`
|
Index []Index `xml:"indexes" json:"index,omitempty"`
|
||||||
LastModified string `xml:"lastModified,attr" json:"lastModified"`
|
LastModified string `xml:"lastModified,attr" json:"lastModified"`
|
||||||
IgnoredArticles string `xml:"ignoredArticles,attr" json:"ignoredArticles"`
|
IgnoredArticles string `xml:"ignoredArticles,attr" json:"ignoredArticles"`
|
||||||
}
|
}
|
||||||
|
90
api/responses/responses_test.go
Normal file
90
api/responses/responses_test.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package responses
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
. "github.com/deluan/gosonic/tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSubsonicResponses(t *testing.T) {
|
||||||
|
|
||||||
|
response := &Subsonic{Status: "ok", Version: "1.0.0"}
|
||||||
|
|
||||||
|
Convey("Subject: Subsonic Responses", t, func(){
|
||||||
|
Convey("EmptyResponse", func() {
|
||||||
|
Convey("XML", func() {
|
||||||
|
So(response, ShouldMatchXML, `<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.0.0"></subsonic-response>`)
|
||||||
|
})
|
||||||
|
Convey("JSON", func() {
|
||||||
|
So(response, ShouldMatchJSON, `{"status":"ok","version":"1.0.0"}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("License", func() {
|
||||||
|
response.License = &License{Valid: true}
|
||||||
|
Convey("XML", func() {
|
||||||
|
So(response, ShouldMatchXML, `<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.0.0"><license valid="true"></license></subsonic-response>`)
|
||||||
|
})
|
||||||
|
Convey("JSON", func() {
|
||||||
|
So(response, ShouldMatchJSON, `{"license":{"valid":true},"status":"ok","version":"1.0.0"}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("MusicFolders", func() {
|
||||||
|
response.MusicFolders = &MusicFolders{}
|
||||||
|
|
||||||
|
Convey("With data", func() {
|
||||||
|
folders := make([]MusicFolder, 2)
|
||||||
|
folders[0] = MusicFolder{Id: "111", Name: "aaa"}
|
||||||
|
folders[1] = MusicFolder{Id: "222", Name: "bbb"}
|
||||||
|
response.MusicFolders.Folders = folders
|
||||||
|
|
||||||
|
Convey("XML", func() {
|
||||||
|
So(response, ShouldMatchXML, `<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.0.0"><musicFolders><musicFolder id="111" name="aaa"></musicFolder><musicFolder id="222" name="bbb"></musicFolder></musicFolders></subsonic-response>`)
|
||||||
|
})
|
||||||
|
Convey("JSON", func() {
|
||||||
|
So(response, ShouldMatchJSON, `{"musicFolders":{"musicFolder":[{"id":"111","name":"aaa"},{"id":"222","name":"bbb"}]},"status":"ok","version":"1.0.0"}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Convey("Without data", func() {
|
||||||
|
Convey("XML", func() {
|
||||||
|
So(response, ShouldMatchXML, `<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.0.0"><musicFolders></musicFolders></subsonic-response>`)
|
||||||
|
})
|
||||||
|
Convey("JSON", func() {
|
||||||
|
So(response, ShouldMatchJSON, `{"musicFolders":{},"status":"ok","version":"1.0.0"}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Indexes", func() {
|
||||||
|
artists := make([]Artist, 1)
|
||||||
|
artists[0] = Artist{Id: "111", Name: "aaa"}
|
||||||
|
response.Indexes = &Indexes{LastModified:"1", IgnoredArticles:"A"}
|
||||||
|
|
||||||
|
Convey("With data", func() {
|
||||||
|
index := make([]Index, 1)
|
||||||
|
index[0] = Index{Name: "A", Artists: artists}
|
||||||
|
response.Indexes.Index = index
|
||||||
|
Convey("XML", func() {
|
||||||
|
So(response, ShouldMatchXML, `<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.0.0"><indexes lastModified="1" ignoredArticles="A"><index name="A"><artist id="111" name="aaa"></artist></index></indexes></subsonic-response>`)
|
||||||
|
})
|
||||||
|
Convey("JSON", func() {
|
||||||
|
So(response, ShouldMatchJSON, `{"indexes":{"ignoredArticles":"A","index":[{"artist":[{"id":"111","name":"aaa"}],"name":"A"}],"lastModified":"1"},"status":"ok","version":"1.0.0"}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Convey("Without data", func() {
|
||||||
|
Convey("XML", func() {
|
||||||
|
So(response, ShouldMatchXML, `<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.0.0"><indexes lastModified="1" ignoredArticles="A"></indexes></subsonic-response>`)
|
||||||
|
})
|
||||||
|
Convey("JSON", func() {
|
||||||
|
So(response, ShouldMatchJSON, `{"indexes":{"ignoredArticles":"A","lastModified":"1"},"status":"ok","version":"1.0.0"}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Reset(func() {
|
||||||
|
response = &Subsonic{Status: "ok", Version: "1.0.0"}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
33
tests/matchers.go
Normal file
33
tests/matchers.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ShouldMatchXML(actual interface{}, expected ...interface{}) string {
|
||||||
|
xml, err := xml.Marshal(actual)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("Malformed XML: %v", err)
|
||||||
|
}
|
||||||
|
return ShouldEqual(string(xml), expected[0].(string))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ShouldMatchJSON(actual interface{}, expected ...interface{}) string {
|
||||||
|
json, err := json.Marshal(actual)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("Malformed JSON: %v", err)
|
||||||
|
}
|
||||||
|
s := UnindentJSON(json)
|
||||||
|
return ShouldEqual(s, expected[0].(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnindentJSON(j []byte) string {
|
||||||
|
var m = make(map[string]interface{})
|
||||||
|
json.Unmarshal(j, &m)
|
||||||
|
s, _ := json.Marshal(m)
|
||||||
|
return string(s)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user