From 5244ae529a5c9cf00f2da6d8be21a14a850e6b8a Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 23 Oct 2014 17:29:07 -0800 Subject: [PATCH] Species: List species. --- api/handler.go | 1 + api/species.go | 17 +++++++++++++++++ api/species_test.go | 29 +++++++++++++++++++++++++++++ datastore/species.go | 12 ++++++++++++ datastore/species_test.go | 33 +++++++++++++++++++++++++++++++++ models/species.go | 35 +++++++++++++++++++++++++++++++++++ models/species_test.go | 32 ++++++++++++++++++++++++++++++++ router/api.go | 1 + router/routes.go | 1 + 9 files changed, 161 insertions(+) diff --git a/api/handler.go b/api/handler.go index 65bbc79..98c8952 100644 --- a/api/handler.go +++ b/api/handler.go @@ -31,6 +31,7 @@ func Handler() *mux.Router { m.Get(router.Species).Handler(handler(serveSpecies)) m.Get(router.CreateSpecies).Handler(handler(serveCreateSpecies)) + m.Get(router.SpeciesList).Handler(handler(serveSpeciesList)) return m } diff --git a/api/species.go b/api/species.go index b8bae18..7ff94ff 100644 --- a/api/species.go +++ b/api/species.go @@ -40,3 +40,20 @@ func serveCreateSpecies(w http.ResponseWriter, r *http.Request) error { return writeJSON(w, species) } + +func serveSpeciesList(w http.ResponseWriter, r *http.Request) error { + var opt models.SpeciesListOptions + if err := schemaDecoder.Decode(&opt, r.URL.Query()); err != nil { + return err + } + + species, err := store.Species.List(&opt) + if err != nil { + return err + } + if species == nil { + species = []*models.Species{} + } + + return writeJSON(w, species) +} diff --git a/api/species_test.go b/api/species_test.go index 24fd557..490c62c 100644 --- a/api/species_test.go +++ b/api/species_test.go @@ -59,3 +59,32 @@ func TestSpecies_Create(t *testing.T) { t.Error("!success") } } + +func TestSpecies_List(t *testing.T) { + setup() + + want := []*models.Species{{Id: 1, GenusId: 1, SpeciesName: "Test Species"}} + wantOpt := &models.SpeciesListOptions{ListOptions: models.ListOptions{Page: 1, PerPage: 10}} + + calledList := false + store.Species.(*models.MockSpeciesService).List_ = func(opt *models.SpeciesListOptions) ([]*models.Species, error) { + if !normalizeDeepEqual(wantOpt, opt) { + t.Errorf("wanted options %d but got %d", wantOpt, opt) + } + calledList = true + return want, nil + } + + species, err := apiClient.Species.List(wantOpt) + if err != nil { + t.Fatal(err) + } + + if !calledList { + t.Error("!calledList") + } + + if !normalizeDeepEqual(&want, &species) { + t.Errorf("got species %+v but wanted species %+v", species, want) + } +} diff --git a/datastore/species.go b/datastore/species.go index f7e6dee..cccdd07 100644 --- a/datastore/species.go +++ b/datastore/species.go @@ -27,3 +27,15 @@ func (s *speciesStore) Create(species *models.Species) (bool, error) { } return true, nil } + +func (s *speciesStore) List(opt *models.SpeciesListOptions) ([]*models.Species, error) { + if opt == nil { + opt = &models.SpeciesListOptions{} + } + var species []*models.Species + err := s.dbh.Select(&species, `SELECT * FROM species LIMIT $1 OFFSET $2;`, opt.PerPageOrDefault(), opt.Offset()) + if err != nil { + return nil, err + } + return species, nil +} diff --git a/datastore/species_test.go b/datastore/species_test.go index db81db6..a540a99 100644 --- a/datastore/species_test.go +++ b/datastore/species_test.go @@ -63,3 +63,36 @@ func TestSpeciesStore_Create_db(t *testing.T) { t.Error("want nonzero species.Id after submitting") } } + +func TestSpeciesStore_List_db(t *testing.T) { + tx, _ := DB.Begin() + defer tx.Rollback() + + // Test on a clean database + tx.Exec(`DELETE FROM species;`) + + genus := &models.Genus{} + + if err := tx.Insert(genus); err != nil { + t.Fatal(err) + } + + want := []*models.Species{{Id: 1, GenusId: genus.Id, SpeciesName: "Test Species"}} + + if err := tx.Insert(want[0]); err != nil { + t.Fatal(err) + } + + d := NewDatastore(tx) + species, err := d.Species.List(&models.SpeciesListOptions{ListOptions: models.ListOptions{Page: 1, PerPage: 10}}) + if err != nil { + t.Fatal(err) + } + + for _, g := range want { + normalizeTime(&g.CreatedAt, &g.UpdatedAt, &g.DeletedAt) + } + if !reflect.DeepEqual(species, want) { + t.Errorf("got species %+v, want %+v", species, want) + } +} diff --git a/models/species.go b/models/species.go index dbd696d..faf6820 100644 --- a/models/species.go +++ b/models/species.go @@ -24,6 +24,9 @@ type SpeciesService interface { // Get a species Get(id int64) (*Species, error) + // List all species + List(opt *SpeciesListOptions) ([]*Species, error) + // Create a species record Create(species *Species) (bool, error) } @@ -78,8 +81,33 @@ func (s *speciesService) Create(species *Species) (bool, error) { return resp.StatusCode == http.StatusCreated, nil } +type SpeciesListOptions struct { + ListOptions +} + +func (s *speciesService) List(opt *SpeciesListOptions) ([]*Species, error) { + url, err := s.client.url(router.SpeciesList, nil, opt) + if err != nil { + return nil, err + } + + req, err := s.client.NewRequest("GET", url.String(), nil) + if err != nil { + return nil, err + } + + var species []*Species + _, err = s.client.Do(req, &species) + if err != nil { + return nil, err + } + + return species, nil +} + type MockSpeciesService struct { Get_ func(id int64) (*Species, error) + List_ func(opt *SpeciesListOptions) ([]*Species, error) Create_ func(species *Species) (bool, error) } @@ -98,3 +126,10 @@ func (s *MockSpeciesService) Create(species *Species) (bool, error) { } return s.Create_(species) } + +func (s *MockSpeciesService) List(opt *SpeciesListOptions) ([]*Species, error) { + if s.List_ == nil { + return nil, nil + } + return s.List_(opt) +} diff --git a/models/species_test.go b/models/species_test.go index 338d13b..4e6873b 100644 --- a/models/species_test.go +++ b/models/species_test.go @@ -73,3 +73,35 @@ func TestSpeciesService_Create(t *testing.T) { t.Errorf("Species.Create returned %+v, want %+v", species, want) } } + +func TestSpeciesService_List(t *testing.T) { + setup() + defer teardown() + + want := []*Species{{Id: 1, GenusId: 1, SpeciesName: "Test Species"}} + + var called bool + mux.HandleFunc(urlPath(t, router.SpeciesList, nil), func(w http.ResponseWriter, r *http.Request) { + called = true + testMethod(t, r, "GET") + testFormValues(t, r, values{}) + + writeJSON(w, want) + }) + + species, err := client.Species.List(nil) + if err != nil { + t.Errorf("Species.List returned error: %v", err) + } + + if !called { + t.Fatal("!called") + } + + for _, u := range want { + normalizeTime(&u.CreatedAt, &u.UpdatedAt, &u.DeletedAt) + } + if !reflect.DeepEqual(species, want) { + t.Errorf("Species.List return %+v, want %+v", species, want) + } +} diff --git a/router/api.go b/router/api.go index 8ae8b02..2ffa19a 100644 --- a/router/api.go +++ b/router/api.go @@ -18,6 +18,7 @@ func API() *mux.Router { m.Path("/genera/{Id:.+}").Methods("DELETE").Name(DeleteGenus) // Species + m.Path("/species").Methods("GET").Name(SpeciesList) m.Path("/species").Methods("POST").Name(CreateSpecies) m.Path("/species/{Id:.+}").Methods("GET").Name(Species) diff --git a/router/routes.go b/router/routes.go index 8b2bd70..4d7a9ac 100644 --- a/router/routes.go +++ b/router/routes.go @@ -13,4 +13,5 @@ const ( Species = "species" CreateSpecies = "species:create" + SpeciesList = "species:list" )