From 9577d9ae875ba6130bddaa4ea0dc0b8d91d882d1 Mon Sep 17 00:00:00 2001 From: Deluan Date: Wed, 2 Mar 2016 20:00:55 -0500 Subject: [PATCH] Started the implementation of getMusicDirectory. Probably will need to introduce a new 'service' layer... --- api/get_indexes_test.go | 9 +--- api/get_music_directory.go | 44 +++++++++++++++++++ api/get_music_directory_test.go | 40 +++++++++++++++++ api/responses/responses.go | 6 +-- api/responses/responses_test.go | 3 +- conf/inject_definitions.go | 1 + conf/router.go | 1 + domain/base.go | 1 + persistence/base_repository.go | 5 +++ persistence/index_repository.go | 10 ++--- persistence/index_repository_test.go | 11 +++++ tests/{matchers_tests.go => matchers.go} | 13 ++++++ tests/mocks/mock_artist_repo.go | 38 ++++++++++++++++ ...index_repo_tests.go => mock_index_repo.go} | 0 ...ty_repo_tests.go => mock_property_repo.go} | 0 15 files changed, 166 insertions(+), 16 deletions(-) create mode 100644 api/get_music_directory.go create mode 100644 api/get_music_directory_test.go rename tests/{matchers_tests.go => matchers.go} (68%) create mode 100644 tests/mocks/mock_artist_repo.go rename tests/mocks/{mock_index_repo_tests.go => mock_index_repo.go} (100%) rename tests/mocks/{mock_property_repo_tests.go => mock_property_repo.go} (100%) diff --git a/api/get_indexes_test.go b/api/get_indexes_test.go index 0b3a7c404..4de3c28ff 100644 --- a/api/get_indexes_test.go +++ b/api/get_indexes_test.go @@ -3,7 +3,6 @@ package api_test import ( "testing" - "encoding/xml" "github.com/deluan/gosonic/api/responses" "github.com/deluan/gosonic/consts" "github.com/deluan/gosonic/domain" @@ -39,17 +38,13 @@ func TestGetIndexes(t *testing.T) { mockRepo.SetError(true) _, w := Get(AddParams("/rest/getIndexes.view", "ifModifiedSince=0"), "TestGetIndexes") - v := responses.Subsonic{} - xml.Unmarshal(w.Body.Bytes(), &v) - So(v.Status, ShouldEqual, "fail") + So(w.Body, ShouldReceiveError, responses.ERROR_GENERIC) }) Convey("Return fail on Property Table error", func() { propRepo.SetError(true) _, w := Get(AddParams("/rest/getIndexes.view"), "TestGetIndexes") - v := responses.Subsonic{} - xml.Unmarshal(w.Body.Bytes(), &v) - So(v.Status, ShouldEqual, "fail") + So(w.Body, ShouldReceiveError, responses.ERROR_GENERIC) }) Convey("When the index is empty", func() { _, w := Get(AddParams("/rest/getIndexes.view"), "TestGetIndexes") diff --git a/api/get_music_directory.go b/api/get_music_directory.go new file mode 100644 index 000000000..58991e4ce --- /dev/null +++ b/api/get_music_directory.go @@ -0,0 +1,44 @@ +package api + +import ( + "github.com/deluan/gosonic/api/responses" + "github.com/deluan/gosonic/domain" + "github.com/deluan/gosonic/utils" + "github.com/karlkfi/inject" + "github.com/astaxie/beego" +) + +type GetMusicDirectoryController struct { + BaseAPIController + artistRepo domain.ArtistRepository +} + +func (c *GetMusicDirectoryController) Prepare() { + inject.ExtractAssignable(utils.Graph, &c.artistRepo) +} + +func (c *GetMusicDirectoryController) Get() { + id := c.Input().Get("id") + + if id == "" { + c.SendError(responses.ERROR_MISSING_PARAMETER, "id parameter required") + } + + found, err := c.artistRepo.Exists(id) + if err != nil { + beego.Error("Error searching for Artist:", err) + c.SendError(responses.ERROR_GENERIC, "Internal Error") + } + + if found { + _, err := c.artistRepo.Get(id) + if err != nil { + beego.Error("Error reading Artist from DB", err) + c.SendError(responses.ERROR_GENERIC, "Internal Error") + } + } + + response := c.NewEmpty() + response.Directory = &responses.Directory{} + c.SendResponse(response) +} diff --git a/api/get_music_directory_test.go b/api/get_music_directory_test.go new file mode 100644 index 000000000..c85680a14 --- /dev/null +++ b/api/get_music_directory_test.go @@ -0,0 +1,40 @@ +package api_test + +import ( + "testing" + + "github.com/deluan/gosonic/domain" + "github.com/deluan/gosonic/tests/mocks" + "github.com/deluan/gosonic/utils" + . "github.com/deluan/gosonic/tests" + . "github.com/smartystreets/goconvey/convey" + "github.com/deluan/gosonic/api/responses" +) + +func TestGetMusicDirectory(t *testing.T) { + Init(t, false) + + mockRepo := mocks.CreateMockArtistRepo() + utils.DefineSingleton(new(domain.ArtistRepository), func() domain.ArtistRepository { + return mockRepo + }) + + mockRepo.SetData("[]") + mockRepo.SetError(false) + + Convey("Subject: GetMusicDirectory Endpoint", t, func() { + Convey("Should fail if missing Id parameter", func() { + _, w := Get(AddParams("/rest/getMusicDirectory.view"), "TestGetMusicDirectory") + + So(w.Body, ShouldReceiveError, responses.ERROR_MISSING_PARAMETER) + }) + Convey("Id is for an artist", func() { + Convey("Return fail on Artist Table error", func() { + mockRepo.SetError(true) + _, w := Get(AddParams("/rest/getMusicDirectory.view", "id=1"), "TestGetMusicDirectory") + + So(w.Body, ShouldReceiveError, responses.ERROR_GENERIC) + }) + }) + }) +} \ No newline at end of file diff --git a/api/responses/responses.go b/api/responses/responses.go index b21136bd9..d361486b8 100644 --- a/api/responses/responses.go +++ b/api/responses/responses.go @@ -6,7 +6,7 @@ type Subsonic struct { XMLName xml.Name `xml:"http://subsonic.org/restapi subsonic-response" json:"-"` Status string `xml:"status,attr" json:"status"` Version string `xml:"version,attr" json:"version"` - Error *Error `xml:",omitempty" json:"error,omitempty"` + Error *Error `xml:"error,omitempty" json:"error,omitempty"` License *License `xml:"license,omitempty" json:"license,omitempty"` MusicFolders *MusicFolders `xml:"musicFolders,omitempty" json:"musicFolders,omitempty"` Indexes *Indexes `xml:"indexes,omitempty" json:"indexes,omitempty"` @@ -18,8 +18,8 @@ type JsonWrapper struct { } type Error struct { - Code int `xml:"code,attr"` - Message string `xml:"message,attr"` + Code int `xml:"code,attr" json:"code"` + Message string `xml:"message,attr" json:"message"` } type License struct { diff --git a/api/responses/responses_test.go b/api/responses/responses_test.go index 8d3a81df7..e16095e59 100644 --- a/api/responses/responses_test.go +++ b/api/responses/responses_test.go @@ -1,9 +1,10 @@ -package responses +package responses_test import ( "testing" . "github.com/smartystreets/goconvey/convey" . "github.com/deluan/gosonic/tests" + . "github.com/deluan/gosonic/api/responses" ) func TestSubsonicResponses(t *testing.T) { diff --git a/conf/inject_definitions.go b/conf/inject_definitions.go index 3d30e1a00..ecd612848 100644 --- a/conf/inject_definitions.go +++ b/conf/inject_definitions.go @@ -10,4 +10,5 @@ func init() { utils.DefineSingleton(new(domain.ArtistIndexRepository), persistence.NewArtistIndexRepository) utils.DefineSingleton(new(domain.PropertyRepository), persistence.NewPropertyRepository) utils.DefineSingleton(new(domain.MediaFolderRepository), persistence.NewMediaFolderRepository) + utils.DefineSingleton(new(domain.ArtistRepository), persistence.NewArtistRepository) } diff --git a/conf/router.go b/conf/router.go index 7f940b2e5..468a63dd9 100644 --- a/conf/router.go +++ b/conf/router.go @@ -20,6 +20,7 @@ func mapEndpoints() { beego.NSRouter("/getLicense.view", &api.GetLicenseController{}, "*:Get"), beego.NSRouter("/getMusicFolders.view", &api.GetMusicFoldersController{}, "*:Get"), beego.NSRouter("/getIndexes.view", &api.GetIndexesController{}, "*:Get"), + beego.NSRouter("/getMusicDirectory.view", &api.GetMusicDirectoryController{}, "*:Get"), ) beego.AddNamespace(ns) diff --git a/domain/base.go b/domain/base.go index ee37a8074..cce372dd1 100644 --- a/domain/base.go +++ b/domain/base.go @@ -3,4 +3,5 @@ package domain type BaseRepository interface { NewId(fields ...string) string CountAll() (int, error) + Exists(id string) (bool, error) } diff --git a/persistence/base_repository.go b/persistence/base_repository.go index 519820459..3585a67ad 100644 --- a/persistence/base_repository.go +++ b/persistence/base_repository.go @@ -39,6 +39,11 @@ func (r *baseRepository) CountAll() (int, error) { return len(ids), err } +func (r *baseRepository) Exists(id string) (bool, error) { + res, err := db().SIsMember([]byte(r.table + "s:all"), []byte(id)) + return res != 0, err +} + func (r *baseRepository) saveOrUpdate(id string, entity interface{}) error { recordPrefix := fmt.Sprintf("%s:%s:", r.table, id) allKey := r.table + "s:all" diff --git a/persistence/index_repository.go b/persistence/index_repository.go index 2c25dc803..af1c13d8e 100644 --- a/persistence/index_repository.go +++ b/persistence/index_repository.go @@ -7,17 +7,17 @@ import ( "sort" ) -type artistIndex struct { +type artistIndexRepository struct { baseRepository } func NewArtistIndexRepository() domain.ArtistIndexRepository { - r := &artistIndex{} + r := &artistIndexRepository{} r.init("index", &domain.ArtistIndex{}) return r } -func (r *artistIndex) Put(m *domain.ArtistIndex) error { +func (r *artistIndexRepository) Put(m *domain.ArtistIndex) error { if m.Id == "" { return errors.New("Id is not set") } @@ -25,13 +25,13 @@ func (r *artistIndex) Put(m *domain.ArtistIndex) error { return r.saveOrUpdate(m.Id, m) } -func (r *artistIndex) Get(id string) (*domain.ArtistIndex, error) { +func (r *artistIndexRepository) Get(id string) (*domain.ArtistIndex, error) { var rec interface{} rec, err := r.readEntity(id) return rec.(*domain.ArtistIndex), err } -func (r *artistIndex) GetAll() ([]domain.ArtistIndex, error) { +func (r *artistIndexRepository) GetAll() ([]domain.ArtistIndex, error) { var indices = make([]domain.ArtistIndex, 0) err := r.loadAll(&indices, "") return indices, err diff --git a/persistence/index_repository_test.go b/persistence/index_repository_test.go index 519553919..e4943d709 100644 --- a/persistence/index_repository_test.go +++ b/persistence/index_repository_test.go @@ -23,6 +23,17 @@ func TestIndexRepository(t *testing.T) { So(s, shouldBeEqual, i) }) + Convey("It should be able to check for existance of an Id", func() { + i := &domain.ArtistIndex{Id: "123"} + + repo.Put(i) + + s, _ := repo.Exists("123") + So(s, ShouldBeTrue) + + s, _ = repo.Exists("NOT_FOUND") + So(s, ShouldBeFalse) + }) Convey("Method Put() should return error if Id is not set", func() { i := &domain.ArtistIndex{} diff --git a/tests/matchers_tests.go b/tests/matchers.go similarity index 68% rename from tests/matchers_tests.go rename to tests/matchers.go index 65a1ff838..92ade6aaf 100644 --- a/tests/matchers_tests.go +++ b/tests/matchers.go @@ -5,6 +5,8 @@ import ( "encoding/json" "encoding/xml" "fmt" + "github.com/deluan/gosonic/api/responses" + "bytes" ) func ShouldMatchXML(actual interface{}, expected ...interface{}) string { @@ -25,6 +27,17 @@ func ShouldMatchJSON(actual interface{}, expected ...interface{}) string { return ShouldEqual(s, expected[0].(string)) } +func ShouldReceiveError(actual interface{}, expected ...interface{}) string { + v := responses.Subsonic{} + err := xml.Unmarshal(actual.(*bytes.Buffer).Bytes(), &v) + if err != nil { + return fmt.Sprintf("Malformed XML: %v", err) + } + + return ShouldEqual(v.Error.Code, expected[0].(int)) + +} + func UnindentJSON(j []byte) string { var m = make(map[string]interface{}) json.Unmarshal(j, &m) diff --git a/tests/mocks/mock_artist_repo.go b/tests/mocks/mock_artist_repo.go new file mode 100644 index 000000000..5f3650c5c --- /dev/null +++ b/tests/mocks/mock_artist_repo.go @@ -0,0 +1,38 @@ +package mocks + +import ( + "encoding/json" + "fmt" + "github.com/deluan/gosonic/domain" +"errors" +) + +func CreateMockArtistRepo() *MockArtist { + return &MockArtist{} +} + +type MockArtist struct { + domain.ArtistRepository + data map[string]domain.Artist + err bool +} + +func (m *MockArtist) SetError(err bool) { + m.err = err +} + +func (m *MockArtist) SetData(j string) { + m.data = make(map[string]domain.Artist) + err := json.Unmarshal([]byte(j), &m.data) + if err != nil { + fmt.Println("ERROR: ", err) + } +} + +func (m *MockArtist) Exists(id string) (bool, error) { + if m.err { + return false, errors.New("Error!") + } + _, found := m.data[id]; + return found, nil +} \ No newline at end of file diff --git a/tests/mocks/mock_index_repo_tests.go b/tests/mocks/mock_index_repo.go similarity index 100% rename from tests/mocks/mock_index_repo_tests.go rename to tests/mocks/mock_index_repo.go diff --git a/tests/mocks/mock_property_repo_tests.go b/tests/mocks/mock_property_repo.go similarity index 100% rename from tests/mocks/mock_property_repo_tests.go rename to tests/mocks/mock_property_repo.go