Rebooting

This commit is contained in:
Matthew Dillon 2015-03-20 15:52:29 -08:00
parent 41ee2857ee
commit 6030310caa
149 changed files with 1489 additions and 9755 deletions

View file

@ -1,208 +0,0 @@
package models
import (
"errors"
"fmt"
"net/http"
"strconv"
"time"
"github.com/thermokarst/bactdb/router"
)
// A Characteristic Type is a lookup type
type CharacteristicType struct {
Id int64 `json:"id,omitempty"`
CharacteristicTypeName string `db:"characteristic_type_name" json:"characteristicTypeName"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
DeletedAt NullTime `db:"deleted_at" json:"deletedAt"`
}
func (m *CharacteristicType) String() string {
return fmt.Sprintf("%v", *m)
}
func NewCharacteristicType() *CharacteristicType {
return &CharacteristicType{
CharacteristicTypeName: "Test Char Type",
}
}
type CharacteristicTypesService interface {
// Get a characteristic type
Get(id int64) (*CharacteristicType, error)
// List all characteristic types
List(opt *CharacteristicTypeListOptions) ([]*CharacteristicType, error)
// Create a characteristic type record
Create(characteristic_type *CharacteristicType) (bool, error)
// Update an existing characteristic type
Update(id int64, characteristic_type *CharacteristicType) (updated bool, err error)
// Delete an existing characteristic type
Delete(id int64) (deleted bool, err error)
}
var (
ErrCharacteristicTypeNotFound = errors.New("characteristic type not found")
)
type characteristicTypesService struct {
client *Client
}
func (s *characteristicTypesService) Get(id int64) (*CharacteristicType, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.CharacteristicType, map[string]string{"Id": strId}, nil)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var characteristic_type *CharacteristicType
_, err = s.client.Do(req, &characteristic_type)
if err != nil {
return nil, err
}
return characteristic_type, nil
}
func (s *characteristicTypesService) Create(characteristic_type *CharacteristicType) (bool, error) {
url, err := s.client.url(router.CreateCharacteristicType, nil, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("POST", url.String(), characteristic_type)
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &characteristic_type)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusCreated, nil
}
type CharacteristicTypeListOptions struct {
ListOptions
}
func (s *characteristicTypesService) List(opt *CharacteristicTypeListOptions) ([]*CharacteristicType, error) {
url, err := s.client.url(router.CharacteristicTypes, nil, opt)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var characteristic_types []*CharacteristicType
_, err = s.client.Do(req, &characteristic_types)
if err != nil {
return nil, err
}
return characteristic_types, nil
}
func (s *characteristicTypesService) Update(id int64, characteristic_type *CharacteristicType) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.UpdateCharacteristicType, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("PUT", url.String(), characteristic_type)
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &characteristic_type)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
func (s *characteristicTypesService) Delete(id int64) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.DeleteCharacteristicType, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("DELETE", url.String(), nil)
if err != nil {
return false, err
}
var characteristic_type *CharacteristicType
resp, err := s.client.Do(req, &characteristic_type)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
type MockCharacteristicTypesService struct {
Get_ func(id int64) (*CharacteristicType, error)
List_ func(opt *CharacteristicTypeListOptions) ([]*CharacteristicType, error)
Create_ func(characteristic_type *CharacteristicType) (bool, error)
Update_ func(id int64, characteristic_type *CharacteristicType) (bool, error)
Delete_ func(id int64) (bool, error)
}
var _ CharacteristicTypesService = &MockCharacteristicTypesService{}
func (s *MockCharacteristicTypesService) Get(id int64) (*CharacteristicType, error) {
if s.Get_ == nil {
return nil, nil
}
return s.Get_(id)
}
func (s *MockCharacteristicTypesService) Create(characteristic_type *CharacteristicType) (bool, error) {
if s.Create_ == nil {
return false, nil
}
return s.Create_(characteristic_type)
}
func (s *MockCharacteristicTypesService) List(opt *CharacteristicTypeListOptions) ([]*CharacteristicType, error) {
if s.List_ == nil {
return nil, nil
}
return s.List_(opt)
}
func (s *MockCharacteristicTypesService) Update(id int64, characteristic_type *CharacteristicType) (bool, error) {
if s.Update_ == nil {
return false, nil
}
return s.Update_(id, characteristic_type)
}
func (s *MockCharacteristicTypesService) Delete(id int64) (bool, error) {
if s.Delete_ == nil {
return false, nil
}
return s.Delete_(id)
}

View file

@ -1,174 +0,0 @@
package models
import (
"net/http"
"reflect"
"testing"
"github.com/thermokarst/bactdb/router"
)
func newCharacteristicType() *CharacteristicType {
characteristic_type := NewCharacteristicType()
characteristic_type.Id = 1
return characteristic_type
}
func TestCharacteristicTypeService_Get(t *testing.T) {
setup()
defer teardown()
want := newCharacteristicType()
var called bool
mux.HandleFunc(urlPath(t, router.CharacteristicType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
writeJSON(w, want)
})
characteristic_type, err := client.CharacteristicTypes.Get(want.Id)
if err != nil {
t.Errorf("CharacteristicTypes.Get returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(characteristic_type, want) {
t.Errorf("CharacteristicTypes.Get return %+v, want %+v", characteristic_type, want)
}
}
func TestCharacteristicTypeService_Create(t *testing.T) {
setup()
defer teardown()
want := newCharacteristicType()
var called bool
mux.HandleFunc(urlPath(t, router.CreateCharacteristicType, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "POST")
testBody(t, r, `{"id":1,"characteristicTypeName":"Test Char Type","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n")
w.WriteHeader(http.StatusCreated)
writeJSON(w, want)
})
characteristic_type := newCharacteristicType()
created, err := client.CharacteristicTypes.Create(characteristic_type)
if err != nil {
t.Errorf("CharacteristicTypes.Create returned error: %v", err)
}
if !created {
t.Error("!created")
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(characteristic_type, want) {
t.Errorf("CharacteristicTypes.Create returned %+v, want %+v", characteristic_type, want)
}
}
func TestCharacteristicTypeService_List(t *testing.T) {
setup()
defer teardown()
want := []*CharacteristicType{newCharacteristicType()}
var called bool
mux.HandleFunc(urlPath(t, router.CharacteristicTypes, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
testFormValues(t, r, values{})
writeJSON(w, want)
})
characteristic_types, err := client.CharacteristicTypes.List(nil)
if err != nil {
t.Errorf("CharacteristicTypes.List returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
for _, u := range want {
normalizeTime(&u.CreatedAt, &u.UpdatedAt, &u.DeletedAt)
}
if !reflect.DeepEqual(characteristic_types, want) {
t.Errorf("CharacteristicTypes.List return %+v, want %+v", characteristic_types, want)
}
}
func TestCharacteristicTypeService_Update(t *testing.T) {
setup()
defer teardown()
want := newCharacteristicType()
var called bool
mux.HandleFunc(urlPath(t, router.UpdateCharacteristicType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "PUT")
testBody(t, r, `{"id":1,"characteristicTypeName":"Test Char Type Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
characteristic_type := newCharacteristicType()
characteristic_type.CharacteristicTypeName = "Test Char Type Updated"
updated, err := client.CharacteristicTypes.Update(characteristic_type.Id, characteristic_type)
if err != nil {
t.Errorf("CharacteristicTypes.Update returned error: %v", err)
}
if !updated {
t.Error("!updated")
}
if !called {
t.Fatal("!called")
}
}
func TestCharacteristicTypeService_Delete(t *testing.T) {
setup()
defer teardown()
want := newCharacteristicType()
var called bool
mux.HandleFunc(urlPath(t, router.DeleteCharacteristicType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "DELETE")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
deleted, err := client.CharacteristicTypes.Delete(want.Id)
if err != nil {
t.Errorf("CharacteristicTypes.Delete returned error: %v", err)
}
if !deleted {
t.Error("!deleted")
}
if !called {
t.Fatal("!called")
}
}

View file

@ -1,225 +0,0 @@
package models
import (
"errors"
"fmt"
"net/http"
"strconv"
"time"
"github.com/thermokarst/bactdb/router"
)
// A Characteristic is a lookup type
type CharacteristicBase struct {
Id int64 `json:"id,omitempty"`
CharacteristicName string `db:"characteristic_name" json:"characteristicName"`
CharacteristicTypeId int64 `db:"characteristic_type_id" json:"characteristicTypeId"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
DeletedAt NullTime `db:"deleted_at" json:"deletedAt"`
}
type Characteristic struct {
*CharacteristicBase
Measurements NullSliceInt64 `db:"measurements" json:"measurements"`
}
type CharacteristicJSON struct {
Characteristic *Characteristic `json:"characteristic"`
}
type CharacteristicsJSON struct {
Characteristics []*Characteristic `json:"characteristics"`
}
func (m *Characteristic) String() string {
return fmt.Sprintf("%v", *m)
}
func NewCharacteristic() *Characteristic {
return &Characteristic{
&CharacteristicBase{
CharacteristicName: "Test Characteristic",
},
make([]int64, 0),
}
}
type CharacteristicsService interface {
// Get an characteristic
Get(id int64) (*Characteristic, error)
// List all characteristics
List(opt *CharacteristicListOptions) ([]*Characteristic, error)
// Create an characteristic
Create(characteristic *Characteristic) (bool, error)
// Update an characteristic
Update(id int64, Characteristic *Characteristic) (updated bool, err error)
// Delete an characteristic
Delete(id int64) (deleted bool, err error)
}
var (
ErrCharacteristicNotFound = errors.New("characteristic not found")
)
type characteristicsService struct {
client *Client
}
func (s *characteristicsService) Get(id int64) (*Characteristic, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.Characteristic, map[string]string{"Id": strId}, nil)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var characteristic *CharacteristicJSON
_, err = s.client.Do(req, &characteristic)
if err != nil {
return nil, err
}
return characteristic.Characteristic, nil
}
func (s *characteristicsService) Create(characteristic *Characteristic) (bool, error) {
url, err := s.client.url(router.CreateCharacteristic, nil, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("POST", url.String(), CharacteristicJSON{Characteristic: characteristic})
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &characteristic)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusCreated, nil
}
type CharacteristicListOptions struct {
ListOptions
}
func (s *characteristicsService) List(opt *CharacteristicListOptions) ([]*Characteristic, error) {
url, err := s.client.url(router.Characteristics, nil, opt)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var characteristics *CharacteristicsJSON
_, err = s.client.Do(req, &characteristics)
if err != nil {
return nil, err
}
return characteristics.Characteristics, nil
}
func (s *characteristicsService) Update(id int64, characteristic *Characteristic) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.UpdateCharacteristic, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("PUT", url.String(), CharacteristicJSON{Characteristic: characteristic})
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &characteristic)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
func (s *characteristicsService) Delete(id int64) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.DeleteCharacteristic, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("DELETE", url.String(), nil)
if err != nil {
return false, err
}
var characteristic *Characteristic
resp, err := s.client.Do(req, &characteristic)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
type MockCharacteristicsService struct {
Get_ func(id int64) (*Characteristic, error)
List_ func(opt *CharacteristicListOptions) ([]*Characteristic, error)
Create_ func(characteristic *Characteristic) (bool, error)
Update_ func(id int64, characteristic *Characteristic) (bool, error)
Delete_ func(id int64) (bool, error)
}
var _ CharacteristicsService = &MockCharacteristicsService{}
func (s *MockCharacteristicsService) Get(id int64) (*Characteristic, error) {
if s.Get_ == nil {
return nil, nil
}
return s.Get_(id)
}
func (s *MockCharacteristicsService) Create(characteristic *Characteristic) (bool, error) {
if s.Create_ == nil {
return false, nil
}
return s.Create_(characteristic)
}
func (s *MockCharacteristicsService) List(opt *CharacteristicListOptions) ([]*Characteristic, error) {
if s.List_ == nil {
return nil, nil
}
return s.List_(opt)
}
func (s *MockCharacteristicsService) Update(id int64, characteristic *Characteristic) (bool, error) {
if s.Update_ == nil {
return false, nil
}
return s.Update_(id, characteristic)
}
func (s *MockCharacteristicsService) Delete(id int64) (bool, error) {
if s.Delete_ == nil {
return false, nil
}
return s.Delete_(id)
}

View file

@ -1,175 +0,0 @@
package models
import (
"net/http"
"reflect"
"testing"
"github.com/thermokarst/bactdb/router"
)
func newCharacteristic() *Characteristic {
characteristic := NewCharacteristic()
characteristic.Id = 1
return characteristic
}
func TestCharacteristicService_Get(t *testing.T) {
setup()
defer teardown()
want := newCharacteristic()
var called bool
mux.HandleFunc(urlPath(t, router.Characteristic, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
writeJSON(w, CharacteristicJSON{Characteristic: want})
})
characteristic, err := client.Characteristics.Get(want.Id)
if err != nil {
t.Errorf("Characteristics.Get returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(characteristic, want) {
t.Errorf("Characteristics.Get return %+v, want %+v", characteristic, want)
}
}
func TestCharacteristicService_Create(t *testing.T) {
setup()
defer teardown()
want := newCharacteristic()
var called bool
mux.HandleFunc(urlPath(t, router.CreateCharacteristic, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "POST")
testBody(t, r, `{"characteristic":{"id":1,"characteristicName":"Test Characteristic","characteristicTypeId":0,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null,"measurements":[]}}`+"\n")
w.WriteHeader(http.StatusCreated)
writeJSON(w, want)
})
characteristic := newCharacteristic()
created, err := client.Characteristics.Create(characteristic)
if err != nil {
t.Errorf("Characteristics.Create returned error: %v", err)
}
if !created {
t.Error("!created")
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(characteristic, want) {
t.Errorf("Characteristics.Create returned %+v, want %+v", characteristic, want)
}
}
func TestCharacteristicService_List(t *testing.T) {
setup()
defer teardown()
want := []*Characteristic{newCharacteristic()}
var called bool
mux.HandleFunc(urlPath(t, router.Characteristics, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
testFormValues(t, r, values{})
writeJSON(w, CharacteristicsJSON{Characteristics: want})
})
characteristics, err := client.Characteristics.List(nil)
if err != nil {
t.Errorf("Characteristics.List returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
for _, u := range want {
normalizeTime(&u.CreatedAt, &u.UpdatedAt, &u.DeletedAt)
}
if !reflect.DeepEqual(characteristics, want) {
t.Errorf("Characteristics.List return %+v, want %+v", characteristics, want)
}
}
func TestCharacteristicService_Update(t *testing.T) {
setup()
defer teardown()
want := newCharacteristic()
var called bool
mux.HandleFunc(urlPath(t, router.UpdateCharacteristic, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "PUT")
testBody(t, r, `{"characteristic":{"id":1,"characteristicName":"Test Char Updated","characteristicTypeId":0,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null,"measurements":[]}}`+"\n")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
characteristic := newCharacteristic()
characteristic.CharacteristicName = "Test Char Updated"
updated, err := client.Characteristics.Update(characteristic.Id, characteristic)
if err != nil {
t.Errorf("Characteristics.Update returned error: %v", err)
}
if !updated {
t.Error("!updated")
}
if !called {
t.Fatal("!called")
}
}
func TestCharacteristicService_Delete(t *testing.T) {
setup()
defer teardown()
want := newCharacteristic()
var called bool
mux.HandleFunc(urlPath(t, router.DeleteCharacteristic, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "DELETE")
testBody(t, r, "")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
deleted, err := client.Characteristics.Delete(want.Id)
if err != nil {
t.Errorf("Characteristics.Delete returned error: %v", err)
}
if !deleted {
t.Error("!deleted")
}
if !called {
t.Fatal("!called")
}
}

View file

@ -1,211 +0,0 @@
package models
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"reflect"
"strings"
"github.com/google/go-querystring/query"
"github.com/thermokarst/bactdb/router"
)
// A Client communicates with bactdb's HTTP API.
type Client struct {
Users UsersService
Genera GeneraService
Species SpeciesService
Strains StrainsService
CharacteristicTypes CharacteristicTypesService
Characteristics CharacteristicsService
TextMeasurementTypes TextMeasurementTypesService
UnitTypes UnitTypesService
Measurements MeasurementsService
// BaseURL for HTTP requests to bactdb's API.
BaseURL *url.URL
//UserAgent used for HTTP requests to bactdb's API.
UserAgent string
httpClient *http.Client
}
const (
libraryVersion = "0.0.1"
userAgent = "bactdb-client/" + libraryVersion
)
// NewClient creates a new HTTP API client for bactdb. If httpClient == nil,
// then http.DefaultClient is used.
func NewClient(httpClient *http.Client) *Client {
if httpClient == nil {
httpClient = http.DefaultClient
}
c := &Client{
BaseURL: &url.URL{Scheme: "http", Host: "bactdb.org", Path: "/api/"},
UserAgent: userAgent,
httpClient: httpClient,
}
c.Users = &usersService{c}
c.Genera = &generaService{c}
c.Species = &speciesService{c}
c.Strains = &strainsService{c}
c.CharacteristicTypes = &characteristicTypesService{c}
c.Characteristics = &characteristicsService{c}
c.TextMeasurementTypes = &textMeasurementTypesService{c}
c.UnitTypes = &unitTypesService{c}
c.Measurements = &measurementsService{c}
return c
}
// ListOptions specifies general pagination options for fetching a list of results
type ListOptions struct {
PerPage int `url:",omitempty" json:",omitempty"`
Page int `url:",omitempty" json:",omitempty"`
}
func (o ListOptions) PageOrDefault() int {
if o.Page <= 0 {
return 1
}
return o.Page
}
func (o ListOptions) Offset() int {
return (o.PageOrDefault() - 1) * o.PerPageOrDefault()
}
func (o ListOptions) PerPageOrDefault() int {
if o.PerPage <= 0 {
return DefaultPerPage
}
return o.PerPage
}
// DefaultPerPage is the default number of items to return in a paginated result set
const DefaultPerPage = 10
// apiRouter is used to generate URLs for bactdb's HTTP API.
var apiRouter = router.API()
// url generates the URL to the named bactdb API endpoint, using the
// specified route variables and query options.
func (c *Client) url(apiRouteName string, routeVars map[string]string, opt interface{}) (*url.URL, error) {
route := apiRouter.Get(apiRouteName)
if route == nil {
return nil, fmt.Errorf("no API route named %q", apiRouteName)
}
routeVarsList := make([]string, 2*len(routeVars))
i := 0
for name, val := range routeVars {
routeVarsList[i*2] = name
routeVarsList[i*2+1] = val
i++
}
url, err := route.URL(routeVarsList...)
if err != nil {
return nil, err
}
// make the route URL path relative to BaseURL by trimming the leading "/"
url.Path = strings.TrimPrefix(url.Path, "/")
if opt != nil {
err = addOptions(url, opt)
if err != nil {
return nil, err
}
}
return url, nil
}
func (c *Client) URL(apiRouteName string, routeVars map[string]string, opt interface{}) (*url.URL, error) {
u, err := c.url(apiRouteName, routeVars, opt)
absURL := c.BaseURL.ResolveReference(u)
return absURL, err
}
// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client. Relative
// URLs should always be specified without a preceding slash. If specified, the
// value pointed to by body is JSON encoded and included as the request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
rel, err := url.Parse(urlStr)
if err != nil {
return nil, err
}
u := c.BaseURL.ResolveReference(rel)
buf := new(bytes.Buffer)
if body != nil {
err := json.NewEncoder(buf).Encode(body)
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, u.String(), buf)
if err != nil {
return nil, err
}
req.Header.Add("User-Agent", c.UserAgent)
return req, nil
}
// Do sends an API request and returns the API response. The API response is
// JSON-decoded and stored in the value pointed to by v, or returned as an error
// if an API error has occurred.
func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
err = CheckResponse(resp)
if err != nil {
// even though there was an error, we still return the response
// in case the caller wants to inspect it further
return resp, err
}
if v != nil {
if bp, ok := v.(*[]byte); ok {
*bp, err = ioutil.ReadAll(resp.Body)
} else {
err = json.NewDecoder(resp.Body).Decode(v)
}
}
if err != nil {
return nil, fmt.Errorf("error reading response from %s %s: %s", req.Method, req.URL.RequestURI(), err)
}
return resp, nil
}
// addOptions adds the parameters in opt as URL query parameters to u. opt
// must be a struct whose fields may contain "url" tags.
func addOptions(u *url.URL, opt interface{}) error {
v := reflect.ValueOf(opt)
if v.Kind() == reflect.Ptr && v.IsNil() {
return nil
}
qs, err := query.Values(opt)
if err != nil {
return err
}
u.RawQuery = qs.Encode()
return nil
}

View file

@ -1,107 +0,0 @@
package models
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"testing"
"time"
"github.com/lib/pq"
)
var (
// mux is the HTTP request multiplexer used with the test server.
mux *http.ServeMux
// client is the bactdb client being tested.
client *Client
// server is a test HTTP server used to provide mock API responses.
server *httptest.Server
)
// setup sets up a test HTTP server along with a Client that is
// configured to talk to that test server. Tests should register handlers on
// mux which provide mock responses for the API method being tested.
func setup() {
// test server
mux = http.NewServeMux()
server = httptest.NewServer(mux)
// bactdb client configured to use test server
client = NewClient(nil)
url, _ := url.Parse(server.URL)
client.BaseURL = url
}
// teardown closes the test HTTP server.
func teardown() {
server.Close()
}
func urlPath(t *testing.T, routeName string, routeVars map[string]string) string {
url, err := client.url(routeName, routeVars, nil)
if err != nil {
t.Fatalf("Error constructing URL path for route %q with vars %+v: %s", routeName, routeVars, err)
}
return "/" + url.Path
}
func writeJSON(w http.ResponseWriter, v interface{}) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
err := json.NewEncoder(w).Encode(v)
if err != nil {
panic("writeJSON: " + err.Error())
}
}
func testMethod(t *testing.T, r *http.Request, want string) {
if want != r.Method {
t.Errorf("Request method = %v, want %v", r.Method, want)
}
}
type values map[string]string
func testFormValues(t *testing.T, r *http.Request, values values) {
want := url.Values{}
for k, v := range values {
want.Add(k, v)
}
r.ParseForm()
if !reflect.DeepEqual(want, r.Form) {
t.Errorf("Request parameters = %v, want %v", r.Form, want)
}
}
func testBody(t *testing.T, r *http.Request, want string) {
b, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Errorf("Unable to read body")
}
str := string(b)
if want != str {
t.Errorf("Body = %s, want: %s", str, want)
}
}
func normalizeTime(t ...interface{}) {
for _, v := range t {
switch u := v.(type) {
default:
fmt.Printf("unexpected type %T", u)
case *time.Time:
x, _ := v.(*time.Time)
*x = x.In(time.UTC)
case *NullTime:
x, _ := v.(*NullTime)
*x = NullTime{pq.NullTime{Time: x.Time.In(time.UTC), Valid: x.Valid}}
}
}
}

View file

@ -1,56 +0,0 @@
package models
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
// An ErrorResponse reports errors caused by an API request.
type ErrorResponse struct {
Response *http.Response `json:",omitempty"`
Message string
}
func (r *ErrorResponse) Error() string {
return fmt.Sprintf("%v %v: %d %v",
r.Response.Request.Method, r.Response.Request.URL,
r.Response.StatusCode, r.Message)
}
func (r *ErrorResponse) HTTPStatusCode() int {
return r.Response.StatusCode
}
// CheckResponse checks the API response for errors, and returns them if
// present. A response is considered an error if it has a status code outside
// the 200 range. API error responses are expected to have either no response
// body, or a JSON response body that maps to ErrorResponse. Any other
// response body will be silently ignored.
func CheckResponse(r *http.Response) error {
if c := r.StatusCode; 200 <= c && c <= 299 {
return nil
}
errorResponse := &ErrorResponse{Response: r}
data, err := ioutil.ReadAll(r.Body)
if err == nil && data != nil {
json.Unmarshal(data, errorResponse)
}
return errorResponse
}
func IsHTTPErrorCode(err error, statusCode int) bool {
if err == nil {
return false
}
type httpError interface {
Error() string
HTTPStatusCode() int
}
if httpErr, ok := err.(httpError); ok {
return statusCode == httpErr.HTTPStatusCode()
}
return false
}

View file

@ -1,226 +0,0 @@
package models
import (
"errors"
"fmt"
"net/http"
"strconv"
"time"
"github.com/thermokarst/bactdb/router"
)
// A Genus is a high-level classifier in bactdb.
type GenusBase struct {
Id int64 `json:"id,omitempty"`
GenusName string `db:"genus_name" json:"genusName"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
DeletedAt NullTime `db:"deleted_at" json:"deletedAt"`
}
type Genus struct {
*GenusBase
Species NullSliceInt64 `db:"species" json:"species"`
}
type GenusJSON struct {
Genus *Genus `json:"genus"`
}
type GeneraJSON struct {
Genera []*Genus `json:"genera"`
}
func (m *Genus) String() string {
return fmt.Sprintf("%v", *m)
}
func (m *GenusBase) String() string {
return fmt.Sprintf("%v", *m)
}
func NewGenus() *Genus {
return &Genus{&GenusBase{GenusName: "Test Genus"}, make([]int64, 0)}
}
// GeneraService interacts with the genus-related endpoints in bactdb's API.
type GeneraService interface {
// Get a genus.
Get(id int64) (*Genus, error)
// List all genera.
List(opt *GenusListOptions) ([]*Genus, error)
// Create a new genus. The newly created genus's ID is written to genus.Id
Create(genus *Genus) (created bool, err error)
// Update an existing genus.
Update(id int64, genus *Genus) (updated bool, err error)
// Delete an existing genus.
Delete(id int64) (deleted bool, err error)
}
var (
ErrGenusNotFound = errors.New("genus not found")
)
type generaService struct {
client *Client
}
func (s *generaService) Get(id int64) (*Genus, error) {
// Pass in key value pairs as strings, so that the gorilla mux URL
// generation is happy.
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.Genus, map[string]string{"Id": strId}, nil)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var genus *GenusJSON
_, err = s.client.Do(req, &genus)
if err != nil {
return nil, err
}
return genus.Genus, nil
}
func (s *generaService) Create(genus *Genus) (bool, error) {
url, err := s.client.url(router.CreateGenus, nil, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("POST", url.String(), GenusJSON{Genus: genus})
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &genus)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusCreated, nil
}
type GenusListOptions struct {
ListOptions
}
func (s *generaService) List(opt *GenusListOptions) ([]*Genus, error) {
url, err := s.client.url(router.Genera, nil, opt)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var genera *GeneraJSON
_, err = s.client.Do(req, &genera)
if err != nil {
return nil, err
}
return genera.Genera, nil
}
func (s *generaService) Update(id int64, genus *Genus) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.UpdateGenus, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("PUT", url.String(), GenusJSON{Genus: genus})
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &genus)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
func (s *generaService) Delete(id int64) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.DeleteGenus, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("DELETE", url.String(), nil)
if err != nil {
return false, err
}
var genus *Genus
resp, err := s.client.Do(req, &genus)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
type MockGeneraService struct {
Get_ func(id int64) (*Genus, error)
List_ func(opt *GenusListOptions) ([]*Genus, error)
Create_ func(genus *Genus) (bool, error)
Update_ func(id int64, genus *Genus) (bool, error)
Delete_ func(id int64) (bool, error)
}
var _ GeneraService = &MockGeneraService{}
func (s *MockGeneraService) Get(id int64) (*Genus, error) {
if s.Get_ == nil {
return nil, nil
}
return s.Get_(id)
}
func (s *MockGeneraService) Create(genus *Genus) (bool, error) {
if s.Create_ == nil {
return false, nil
}
return s.Create_(genus)
}
func (s *MockGeneraService) List(opt *GenusListOptions) ([]*Genus, error) {
if s.List_ == nil {
return nil, nil
}
return s.List_(opt)
}
func (s *MockGeneraService) Update(id int64, genus *Genus) (bool, error) {
if s.Update_ == nil {
return false, nil
}
return s.Update_(id, genus)
}
func (s *MockGeneraService) Delete(id int64) (bool, error) {
if s.Delete_ == nil {
return false, nil
}
return s.Delete_(id)
}

View file

@ -1,177 +0,0 @@
package models
import (
"net/http"
"reflect"
"testing"
"github.com/thermokarst/bactdb/router"
)
func newGenus() *Genus {
genus := NewGenus()
genus.Id = 1
return genus
}
func TestGeneraService_Get(t *testing.T) {
setup()
defer teardown()
want := newGenus()
var called bool
mux.HandleFunc(urlPath(t, router.Genus, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
writeJSON(w, GenusJSON{Genus: want})
})
genus, err := client.Genera.Get(want.Id)
if err != nil {
t.Errorf("Genera.Get returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(genus, want) {
t.Errorf("Genera.Get returned %+v, want %+v", genus, want)
}
}
func TestGeneraService_Create(t *testing.T) {
setup()
defer teardown()
want := newGenus()
var called bool
mux.HandleFunc(urlPath(t, router.CreateGenus, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "POST")
testBody(t, r, `{"genus":{"id":1,"genusName":"Test Genus","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null,"species":[]}}`+"\n")
w.WriteHeader(http.StatusCreated)
writeJSON(w, want)
})
genus := newGenus()
created, err := client.Genera.Create(genus)
if err != nil {
t.Errorf("Genera.Create returned error: %v", err)
}
if !created {
t.Error("!created")
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(genus, want) {
t.Errorf("Genera.Create returned %+v, want %+v", genus, want)
}
}
func TestGeneraService_List(t *testing.T) {
setup()
defer teardown()
want := []*Genus{newGenus()}
var called bool
mux.HandleFunc(urlPath(t, router.Genera, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
testFormValues(t, r, values{})
writeJSON(w, GeneraJSON{Genera: want})
})
genera, err := client.Genera.List(nil)
if err != nil {
t.Errorf("Genera.List returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
for _, u := range want {
normalizeTime(&u.CreatedAt, &u.UpdatedAt, &u.DeletedAt)
}
if !reflect.DeepEqual(genera, want) {
t.Errorf("Genera.List return %+v, want %+v", genera, want)
}
}
func TestGeneraService_Update(t *testing.T) {
setup()
defer teardown()
want := newGenus()
var called bool
mux.HandleFunc(urlPath(t, router.UpdateGenus, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "PUT")
testBody(t, r, `{"genus":{"id":1,"genusName":"Test Genus Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null,"species":[]}}`+"\n")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
genus := newGenus()
genus.GenusName = "Test Genus Updated"
updated, err := client.Genera.Update(genus.Id, genus)
if err != nil {
t.Errorf("Genera.Update returned error: %v", err)
}
if !updated {
t.Error("!updated")
}
if !called {
t.Fatal("!called")
}
}
func TestGeneraService_Delete(t *testing.T) {
setup()
defer teardown()
want := newGenus()
var called bool
mux.HandleFunc(urlPath(t, router.DeleteGenus, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "DELETE")
testBody(t, r, "")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
deleted, err := client.Genera.Delete(1)
if err != nil {
t.Errorf("Genera.Delete returned error: %v", err)
}
if !deleted {
t.Error("!deleted")
}
if !called {
t.Fatal("!called")
}
}

View file

@ -1,228 +0,0 @@
package models
import (
"database/sql"
"errors"
"fmt"
"net/http"
"strconv"
"time"
"github.com/thermokarst/bactdb/router"
)
// A Measurement is the main data type for this application
// There are two types of supported measurements: text & numerical. The table
// has a constraint that will allow one or the other for a particular
// combination of strain & characteristic, but not both.
type Measurement struct {
Id int64 `json:"id,omitempty"`
StrainId int64 `db:"strain_id" json:"strain"`
CharacteristicId int64 `db:"characteristic_id" json:"characteristic"`
TextMeasurementTypeId NullInt64 `db:"text_measurement_type_id" json:"textMeasurementTypeId"`
TxtValue NullString `db:"txt_value" json:"txtValue"`
NumValue NullFloat64 `db:"num_value" json:"numValue"`
ConfidenceInterval NullFloat64 `db:"confidence_interval" json:"confidenceInterval"`
UnitTypeId NullInt64 `db:"unit_type_id" json:"unitTypeId"`
Notes NullString `db:"notes" json:"notes"`
TestMethodId NullInt64 `db:"test_method_id" json:"testMethodId"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
}
type MeasurementJSON struct {
Measurement *Measurement `json:"measurement"`
}
type MeasurementsJSON struct {
Measurements []*Measurement `json:"measurements"`
}
func (m *Measurement) String() string {
return fmt.Sprintf("%v", *m)
}
func NewMeasurement() *Measurement {
return &Measurement{
NumValue: NullFloat64{sql.NullFloat64{Float64: 1.23, Valid: true}},
}
}
type MeasurementsService interface {
// Get a measurement
Get(id int64) (*Measurement, error)
// List all measurements
List(opt *MeasurementListOptions) ([]*Measurement, error)
// Create a measurement
Create(measurement *Measurement) (bool, error)
// Update an existing measurement
Update(id int64, MeasurementType *Measurement) (bool, error)
// Delete a measurement
Delete(id int64) (deleted bool, err error)
}
var (
ErrMeasurementNotFound = errors.New("measurement not found")
)
type measurementsService struct {
client *Client
}
func (s *measurementsService) Get(id int64) (*Measurement, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.Measurement, map[string]string{"Id": strId}, nil)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var measurement *MeasurementJSON
_, err = s.client.Do(req, &measurement)
if err != nil {
return nil, err
}
return measurement.Measurement, nil
}
func (s *measurementsService) Create(measurement *Measurement) (bool, error) {
url, err := s.client.url(router.CreateMeasurement, nil, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("POST", url.String(), MeasurementJSON{Measurement: measurement})
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &measurement)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusCreated, nil
}
type MeasurementListOptions struct {
ListOptions
Genus string
}
func (s *measurementsService) List(opt *MeasurementListOptions) ([]*Measurement, error) {
url, err := s.client.url(router.Measurements, nil, opt)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var measurements *MeasurementsJSON
_, err = s.client.Do(req, &measurements)
if err != nil {
return nil, err
}
return measurements.Measurements, nil
}
func (s *measurementsService) Update(id int64, measurement *Measurement) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.UpdateMeasurement, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("PUT", url.String(), MeasurementJSON{Measurement: measurement})
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &measurement)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
func (s *measurementsService) Delete(id int64) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.DeleteMeasurement, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("DELETE", url.String(), nil)
if err != nil {
return false, err
}
var measurement *Measurement
resp, err := s.client.Do(req, &measurement)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
type MockMeasurementsService struct {
Get_ func(id int64) (*Measurement, error)
List_ func(opt *MeasurementListOptions) ([]*Measurement, error)
Create_ func(measurement *Measurement) (bool, error)
Update_ func(id int64, measurement *Measurement) (bool, error)
Delete_ func(id int64) (bool, error)
}
var _ MeasurementsService = &MockMeasurementsService{}
func (s *MockMeasurementsService) Get(id int64) (*Measurement, error) {
if s.Get_ == nil {
return nil, nil
}
return s.Get_(id)
}
func (s *MockMeasurementsService) Create(measurement *Measurement) (bool, error) {
if s.Create_ == nil {
return false, nil
}
return s.Create_(measurement)
}
func (s *MockMeasurementsService) List(opt *MeasurementListOptions) ([]*Measurement, error) {
if s.List_ == nil {
return nil, nil
}
return s.List_(opt)
}
func (s *MockMeasurementsService) Update(id int64, measurement *Measurement) (bool, error) {
if s.Update_ == nil {
return false, nil
}
return s.Update_(id, measurement)
}
func (s *MockMeasurementsService) Delete(id int64) (bool, error) {
if s.Delete_ == nil {
return false, nil
}
return s.Delete_(id)
}

View file

@ -1,180 +0,0 @@
package models
import (
"database/sql"
"net/http"
"reflect"
"testing"
"github.com/thermokarst/bactdb/router"
)
func newMeasurement() *Measurement {
measurement := NewMeasurement()
measurement.Id = 1
measurement.StrainId = 1
measurement.CharacteristicId = 1
measurement.UnitTypeId = NullInt64{sql.NullInt64{Int64: 1, Valid: true}}
measurement.Notes = NullString{sql.NullString{String: "a note", Valid: true}}
return measurement
}
func TestMeasurementService_Get(t *testing.T) {
setup()
defer teardown()
want := newMeasurement()
var called bool
mux.HandleFunc(urlPath(t, router.Measurement, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
writeJSON(w, MeasurementJSON{Measurement: want})
})
measurement, err := client.Measurements.Get(want.Id)
if err != nil {
t.Errorf("Measurements.Get returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt)
if !reflect.DeepEqual(measurement, want) {
t.Errorf("Measurements.Get return %+v, want %+v", measurement, want)
}
}
func TestMeasurementService_Create(t *testing.T) {
setup()
defer teardown()
want := newMeasurement()
var called bool
mux.HandleFunc(urlPath(t, router.CreateMeasurement, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "POST")
testBody(t, r, `{"measurement":{"id":1,"strain":1,"characteristic":1,"textMeasurementTypeId":null,"txtValue":null,"numValue":1.23,"confidenceInterval":null,"unitTypeId":1,"notes":"a note","testMethodId":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}}`+"\n")
w.WriteHeader(http.StatusCreated)
writeJSON(w, want)
})
measurement := newMeasurement()
created, err := client.Measurements.Create(measurement)
if err != nil {
t.Errorf("Measurements.Create returned error: %v", err)
}
if !created {
t.Error("!created")
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt)
if !reflect.DeepEqual(measurement, want) {
t.Errorf("Measurements.Create returned %+v, want %+v", measurement, want)
}
}
func TestMeasurementService_List(t *testing.T) {
setup()
defer teardown()
want := []*Measurement{newMeasurement()}
var called bool
mux.HandleFunc(urlPath(t, router.Measurements, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
testFormValues(t, r, values{})
writeJSON(w, MeasurementsJSON{Measurements: want})
})
measurements, err := client.Measurements.List(nil)
if err != nil {
t.Errorf("Measurements.List returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
for _, u := range want {
normalizeTime(&u.CreatedAt, &u.UpdatedAt)
}
if !reflect.DeepEqual(measurements, want) {
t.Errorf("Measurements.List return %+v, want %+v", measurements, want)
}
}
func TestMeasurementService_Update(t *testing.T) {
setup()
defer teardown()
want := newMeasurement()
var called bool
mux.HandleFunc(urlPath(t, router.UpdateMeasurement, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "PUT")
testBody(t, r, `{"measurement":{"id":1,"strain":1,"characteristic":1,"textMeasurementTypeId":null,"txtValue":null,"numValue":4.56,"confidenceInterval":null,"unitTypeId":1,"notes":"a note","testMethodId":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}}`+"\n")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
measurement := newMeasurement()
measurement.NumValue = NullFloat64{sql.NullFloat64{Float64: 4.56, Valid: true}}
updated, err := client.Measurements.Update(measurement.Id, measurement)
if err != nil {
t.Errorf("Measurements.Update returned error: %v", err)
}
if !updated {
t.Error("!updated")
}
if !called {
t.Fatal("!called")
}
}
func TestMeasurementService_Delete(t *testing.T) {
setup()
defer teardown()
want := newMeasurement()
var called bool
mux.HandleFunc(urlPath(t, router.DeleteMeasurement, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "DELETE")
testBody(t, r, "")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
deleted, err := client.Measurements.Delete(want.Id)
if err != nil {
t.Errorf("Measurements.Delete returned error: %v", err)
}
if !deleted {
t.Error("!deleted")
}
if !called {
t.Fatal("!called")
}
}

View file

@ -1,223 +0,0 @@
package models
import (
"errors"
"fmt"
"net/http"
"strconv"
"time"
"github.com/thermokarst/bactdb/router"
)
// A Species is a high-level classifier in bactdb.
type SpeciesBase struct {
Id int64 `json:"id,omitempty"`
GenusId int64 `db:"genus_id" json:"genus"`
SpeciesName string `db:"species_name" json:"speciesName"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
DeletedAt NullTime `db:"deleted_at" json:"deletedAt"`
}
type Species struct {
*SpeciesBase
Strains NullSliceInt64 `db:"strains" json:"strains"`
}
type SpeciesJSON struct {
Species *Species `json:"species"`
}
type SpeciesListJSON struct {
Species []*Species `json:"species"`
}
func (m *Species) String() string {
return fmt.Sprintf("%v", *m)
}
func NewSpecies() *Species {
return &Species{&SpeciesBase{SpeciesName: "Test Species"}, make([]int64, 0)}
}
// SpeciesService interacts with the species-related endpoints in bactdb's API.
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)
// Update an existing species
Update(id int64, species *Species) (updated bool, err error)
// Delete an existing species
Delete(id int64) (deleted bool, err error)
}
var (
ErrSpeciesNotFound = errors.New("species not found")
)
type speciesService struct {
client *Client
}
func (s *speciesService) Get(id int64) (*Species, error) {
// Pass in key value pairs as strings, sp that the gorilla mux URL generation is happy
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.Species, map[string]string{"Id": strId}, nil)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var species *SpeciesJSON
_, err = s.client.Do(req, &species)
if err != nil {
return nil, err
}
return species.Species, nil
}
func (s *speciesService) Create(species *Species) (bool, error) {
url, err := s.client.url(router.CreateSpecies, nil, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("POST", url.String(), SpeciesJSON{Species: species})
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &species)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusCreated, nil
}
type SpeciesListOptions struct {
ListOptions
Genus string
}
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 *SpeciesListJSON
_, err = s.client.Do(req, &species)
if err != nil {
return nil, err
}
return species.Species, nil
}
func (s *speciesService) Update(id int64, species *Species) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.UpdateSpecies, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("PUT", url.String(), SpeciesJSON{Species: species})
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &species)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
func (s *speciesService) Delete(id int64) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.DeleteSpecies, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("DELETE", url.String(), nil)
if err != nil {
return false, err
}
var species *Species
resp, err := s.client.Do(req, &species)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
type MockSpeciesService struct {
Get_ func(id int64) (*Species, error)
List_ func(opt *SpeciesListOptions) ([]*Species, error)
Create_ func(species *Species) (bool, error)
Update_ func(id int64, species *Species) (bool, error)
Delete_ func(id int64) (bool, error)
}
var _ SpeciesService = &MockSpeciesService{}
func (s *MockSpeciesService) Get(id int64) (*Species, error) {
if s.Get_ == nil {
return nil, nil
}
return s.Get_(id)
}
func (s *MockSpeciesService) Create(species *Species) (bool, error) {
if s.Create_ == nil {
return false, nil
}
return s.Create_(species)
}
func (s *MockSpeciesService) List(opt *SpeciesListOptions) ([]*Species, error) {
if s.List_ == nil {
return nil, nil
}
return s.List_(opt)
}
func (s *MockSpeciesService) Update(id int64, species *Species) (bool, error) {
if s.Update_ == nil {
return false, nil
}
return s.Update_(id, species)
}
func (s *MockSpeciesService) Delete(id int64) (bool, error) {
if s.Delete_ == nil {
return false, nil
}
return s.Delete_(id)
}

View file

@ -1,177 +0,0 @@
package models
import (
"net/http"
"reflect"
"testing"
"github.com/thermokarst/bactdb/router"
)
func newSpecies() *Species {
species := NewSpecies()
species.Id = 1
species.GenusId = 1
return species
}
func TestSpeciesService_Get(t *testing.T) {
setup()
defer teardown()
want := newSpecies()
var called bool
mux.HandleFunc(urlPath(t, router.Species, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
writeJSON(w, SpeciesJSON{Species: want})
})
species, err := client.Species.Get(want.Id)
if err != nil {
t.Errorf("Species.Get returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(species, want) {
t.Errorf("Species.Get returned %+v, want %+v", species, want)
}
}
func TestSpeciesService_Create(t *testing.T) {
setup()
defer teardown()
want := newSpecies()
var called bool
mux.HandleFunc(urlPath(t, router.CreateSpecies, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "POST")
testBody(t, r, `{"species":{"id":1,"genus":1,"speciesName":"Test Species","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null,"strains":[]}}`+"\n")
w.WriteHeader(http.StatusCreated)
writeJSON(w, want)
})
species := newSpecies()
created, err := client.Species.Create(species)
if err != nil {
t.Errorf("Species.Create returned error: %v", err)
}
if !created {
t.Error("!created")
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(species, want) {
t.Errorf("Species.Create returned %+v, want %+v", species, want)
}
}
func TestSpeciesService_List(t *testing.T) {
setup()
defer teardown()
want := []*Species{newSpecies()}
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, SpeciesListJSON{Species: 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)
}
}
func TestSpeciesService_Update(t *testing.T) {
setup()
defer teardown()
want := newSpecies()
var called bool
mux.HandleFunc(urlPath(t, router.UpdateSpecies, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "PUT")
testBody(t, r, `{"species":{"id":1,"genus":1,"speciesName":"Test Species Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null,"strains":[]}}`+"\n")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
species := newSpecies()
species.SpeciesName = "Test Species Updated"
updated, err := client.Species.Update(species.Id, species)
if err != nil {
t.Errorf("Species.Update returned error: %v", err)
}
if !updated {
t.Error("!updated")
}
if !called {
t.Fatal("!called")
}
}
func TestSpeciesService_Delete(t *testing.T) {
setup()
defer teardown()
want := newSpecies()
var called bool
mux.HandleFunc(urlPath(t, router.DeleteSpecies, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "DELETE")
testBody(t, r, "")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
deleted, err := client.Species.Delete(want.Id)
if err != nil {
t.Errorf("Species.Delete returned error: %v", err)
}
if !deleted {
t.Error("!deleted")
}
if !called {
t.Fatal("!called")
}
}

View file

@ -1,253 +0,0 @@
package models
import (
"database/sql"
"errors"
"fmt"
"net/http"
"strconv"
"time"
"github.com/thermokarst/bactdb/router"
)
// A Strain is a subclass of species
type StrainBase struct {
Id int64 `json:"id,omitempty"`
SpeciesId int64 `db:"species_id" json:"species"`
StrainName string `db:"strain_name" json:"strainName"`
StrainType string `db:"strain_type" json:"strainType"`
Etymology NullString `db:"etymology" json:"etymology"`
AccessionBanks string `db:"accession_banks" json:"accessionBanks"`
GenbankEmblDdb NullString `db:"genbank_embl_ddb" json:"genbankEmblDdb"`
IsolatedFrom NullString `db:"isolated_from" json:"isolatedFrom"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
DeletedAt NullTime `db:"deleted_at" json:"deletedAt"`
}
type Strain struct {
*StrainBase
Measurements NullSliceInt64 `db:"measurements" json:"measurements"`
}
type StrainJSON struct {
Strain *Strain `json:"strain"`
}
type StrainsJSON struct {
Strains []*Strain `json:"strains"`
}
func (s *Strain) String() string {
return fmt.Sprintf("%v", *s)
}
func NewStrain() *Strain {
return &Strain{
&StrainBase{
StrainName: "Test Strain",
StrainType: "Test Type",
Etymology: NullString{
sql.NullString{
String: "Test Etymology",
Valid: true,
},
},
AccessionBanks: "Test Accession",
GenbankEmblDdb: NullString{
sql.NullString{
String: "Test Genbank",
Valid: true,
},
},
IsolatedFrom: NullString{
sql.NullString{
String: "",
Valid: false,
},
},
},
make([]int64, 0),
}
}
// StrainService interacts with the strain-related endpoints in bactdb's API
type StrainsService interface {
// Get a strain
Get(id int64) (*Strain, error)
// List all strains
List(opt *StrainListOptions) ([]*Strain, error)
// Create a strain record
Create(strain *Strain) (bool, error)
// Update an existing strain
Update(id int64, strain *Strain) (updated bool, err error)
// Delete an existing strain
Delete(id int64) (deleted bool, err error)
}
var (
ErrStrainNotFound = errors.New("strain not found")
)
type strainsService struct {
client *Client
}
func (s *strainsService) Get(id int64) (*Strain, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.Strain, map[string]string{"Id": strId}, nil)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var strain *StrainJSON
_, err = s.client.Do(req, &strain)
if err != nil {
return nil, err
}
return strain.Strain, nil
}
func (s *strainsService) Create(strain *Strain) (bool, error) {
url, err := s.client.url(router.CreateStrain, nil, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("POST", url.String(), StrainJSON{Strain: strain})
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &strain)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusCreated, nil
}
type StrainListOptions struct {
ListOptions
Genus string
}
func (s *strainsService) List(opt *StrainListOptions) ([]*Strain, error) {
url, err := s.client.url(router.Strains, nil, opt)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var strains *StrainsJSON
_, err = s.client.Do(req, &strains)
if err != nil {
return nil, err
}
return strains.Strains, nil
}
func (s *strainsService) Update(id int64, strain *Strain) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.UpdateStrain, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("PUT", url.String(), StrainJSON{Strain: strain})
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &strain)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
func (s *strainsService) Delete(id int64) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.DeleteStrain, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("DELETE", url.String(), nil)
if err != nil {
return false, err
}
var strain *Strain
resp, err := s.client.Do(req, &strain)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
type MockStrainsService struct {
Get_ func(id int64) (*Strain, error)
List_ func(opt *StrainListOptions) ([]*Strain, error)
Create_ func(strain *Strain) (bool, error)
Update_ func(id int64, strain *Strain) (bool, error)
Delete_ func(id int64) (bool, error)
}
var _ StrainsService = &MockStrainsService{}
func (s *MockStrainsService) Get(id int64) (*Strain, error) {
if s.Get_ == nil {
return nil, nil
}
return s.Get_(id)
}
func (s *MockStrainsService) Create(strain *Strain) (bool, error) {
if s.Create_ == nil {
return false, nil
}
return s.Create_(strain)
}
func (s *MockStrainsService) List(opt *StrainListOptions) ([]*Strain, error) {
if s.List_ == nil {
return nil, nil
}
return s.List_(opt)
}
func (s *MockStrainsService) Update(id int64, strain *Strain) (bool, error) {
if s.Update_ == nil {
return false, nil
}
return s.Update_(id, strain)
}
func (s *MockStrainsService) Delete(id int64) (bool, error) {
if s.Delete_ == nil {
return false, nil
}
return s.Delete_(id)
}

View file

@ -1,179 +0,0 @@
package models
import (
"net/http"
"reflect"
"testing"
"github.com/thermokarst/bactdb/router"
)
func newStrain() *Strain {
strain := NewStrain()
strain.Id = 1
strain.SpeciesId = 1
return strain
}
func TestStrainService_Get(t *testing.T) {
setup()
defer teardown()
want := newStrain()
var called bool
mux.HandleFunc(urlPath(t, router.Strain, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
writeJSON(w, StrainJSON{Strain: want})
})
strain, err := client.Strains.Get(want.Id)
if err != nil {
t.Errorf("Strain.Get returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(strain, want) {
t.Errorf("Strain.Get return %+v, want %+v", strain, want)
}
}
func TestStrainService_Create(t *testing.T) {
setup()
defer teardown()
want := newStrain()
var called bool
mux.HandleFunc(urlPath(t, router.CreateStrain, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "POST")
testBody(t, r, `{"strain":{"id":1,"species":1,"strainName":"Test Strain","strainType":"Test Type","etymology":"Test Etymology","accessionBanks":"Test Accession","genbankEmblDdb":"Test Genbank","isolatedFrom":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null,"measurements":[]}}`+"\n")
w.WriteHeader(http.StatusCreated)
writeJSON(w, want)
})
strain := newStrain()
created, err := client.Strains.Create(strain)
if err != nil {
t.Errorf("Strains.Create returned error: %v", err)
}
if !created {
t.Error("!created")
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(strain, want) {
t.Errorf("Strains.Create returned %+v, want %+v", strain, want)
}
}
func TestStrainService_List(t *testing.T) {
setup()
defer teardown()
want := []*Strain{newStrain()}
var called bool
mux.HandleFunc(urlPath(t, router.Strains, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
testFormValues(t, r, values{})
writeJSON(w, StrainsJSON{Strains: want})
})
strains, err := client.Strains.List(nil)
if err != nil {
t.Errorf("Strains.List returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
for _, u := range want {
normalizeTime(&u.CreatedAt, &u.UpdatedAt, &u.DeletedAt)
}
if !reflect.DeepEqual(strains, want) {
t.Errorf("Strains.List return %+v, want %+v", strains, want)
}
}
func TestStrainService_Update(t *testing.T) {
setup()
defer teardown()
want := newStrain()
var called bool
mux.HandleFunc(urlPath(t, router.UpdateStrain, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "PUT")
testBody(t, r, `{"strain":{"id":1,"species":1,"strainName":"Test Strain Updated","strainType":"Test Type Updated","etymology":"Test Etymology","accessionBanks":"Test Accession Updated","genbankEmblDdb":"Test Genbank Updated","isolatedFrom":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null,"measurements":[]}}`+"\n")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
strain := newStrain()
strain.StrainName = "Test Strain Updated"
strain.StrainType = "Test Type Updated"
strain.AccessionBanks = "Test Accession Updated"
strain.GenbankEmblDdb.String = "Test Genbank Updated"
updated, err := client.Strains.Update(strain.Id, strain)
if err != nil {
t.Errorf("Strains.Update returned error: %v", err)
}
if !updated {
t.Error("!updated")
}
if !called {
t.Fatal("!called")
}
}
func TestStrainService_Delete(t *testing.T) {
setup()
defer teardown()
want := newStrain()
var called bool
mux.HandleFunc(urlPath(t, router.DeleteStrain, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "DELETE")
testBody(t, r, "")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
deleted, err := client.Strains.Delete(want.Id)
if err != nil {
t.Errorf("Strains.Delete returned error: %v", err)
}
if !deleted {
t.Error("!deleted")
}
if !called {
t.Fatal("!called")
}
}

View file

@ -1,208 +0,0 @@
package models
import (
"errors"
"fmt"
"net/http"
"strconv"
"time"
"github.com/thermokarst/bactdb/router"
)
// A TextMeasurementType is a lookup type
type TextMeasurementType struct {
Id int64 `json:"id,omitempty"`
TextMeasurementName string `db:"text_measurement_name" json:"textMeasurementName"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
DeletedAt NullTime `db:"deleted_at" json:"deletedAt"`
}
func (m *TextMeasurementType) String() string {
return fmt.Sprintf("%v", *m)
}
func NewTextMeasurementType() *TextMeasurementType {
return &TextMeasurementType{
TextMeasurementName: "Test Text Measurement Type",
}
}
type TextMeasurementTypesService interface {
// Get a text measurement type
Get(id int64) (*TextMeasurementType, error)
// List all text measurement types
List(opt *TextMeasurementTypeListOptions) ([]*TextMeasurementType, error)
// Create a text measurement type
Create(text_measurement_type *TextMeasurementType) (bool, error)
// Update a text measurement type
Update(id int64, TextMeasurementType *TextMeasurementType) (updated bool, err error)
// Delete a text measurement type
Delete(id int64) (deleted bool, err error)
}
var (
ErrTextMeasurementTypeNotFound = errors.New("text measurement type not found")
)
type textMeasurementTypesService struct {
client *Client
}
func (s *textMeasurementTypesService) Get(id int64) (*TextMeasurementType, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.TextMeasurementType, map[string]string{"Id": strId}, nil)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var text_measurement_type *TextMeasurementType
_, err = s.client.Do(req, &text_measurement_type)
if err != nil {
return nil, err
}
return text_measurement_type, nil
}
func (s *textMeasurementTypesService) Create(text_measurement_type *TextMeasurementType) (bool, error) {
url, err := s.client.url(router.CreateTextMeasurementType, nil, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("POST", url.String(), text_measurement_type)
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &text_measurement_type)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusCreated, nil
}
type TextMeasurementTypeListOptions struct {
ListOptions
}
func (s *textMeasurementTypesService) List(opt *TextMeasurementTypeListOptions) ([]*TextMeasurementType, error) {
url, err := s.client.url(router.TextMeasurementTypes, nil, opt)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var text_measurement_types []*TextMeasurementType
_, err = s.client.Do(req, &text_measurement_types)
if err != nil {
return nil, err
}
return text_measurement_types, nil
}
func (s *textMeasurementTypesService) Update(id int64, text_measurement_type *TextMeasurementType) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.UpdateTextMeasurementType, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("PUT", url.String(), text_measurement_type)
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &text_measurement_type)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
func (s *textMeasurementTypesService) Delete(id int64) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.DeleteTextMeasurementType, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("DELETE", url.String(), nil)
if err != nil {
return false, err
}
var text_measurement_type *TextMeasurementType
resp, err := s.client.Do(req, &text_measurement_type)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
type MockTextMeasurementTypesService struct {
Get_ func(id int64) (*TextMeasurementType, error)
List_ func(opt *TextMeasurementTypeListOptions) ([]*TextMeasurementType, error)
Create_ func(text_measurement_type *TextMeasurementType) (bool, error)
Update_ func(id int64, text_measurement_type *TextMeasurementType) (bool, error)
Delete_ func(id int64) (bool, error)
}
var _ TextMeasurementTypesService = &MockTextMeasurementTypesService{}
func (s *MockTextMeasurementTypesService) Get(id int64) (*TextMeasurementType, error) {
if s.Get_ == nil {
return nil, nil
}
return s.Get_(id)
}
func (s *MockTextMeasurementTypesService) Create(text_measurement_type *TextMeasurementType) (bool, error) {
if s.Create_ == nil {
return false, nil
}
return s.Create_(text_measurement_type)
}
func (s *MockTextMeasurementTypesService) List(opt *TextMeasurementTypeListOptions) ([]*TextMeasurementType, error) {
if s.List_ == nil {
return nil, nil
}
return s.List_(opt)
}
func (s *MockTextMeasurementTypesService) Update(id int64, text_measurement_type *TextMeasurementType) (bool, error) {
if s.Update_ == nil {
return false, nil
}
return s.Update_(id, text_measurement_type)
}
func (s *MockTextMeasurementTypesService) Delete(id int64) (bool, error) {
if s.Delete_ == nil {
return false, nil
}
return s.Delete_(id)
}

View file

@ -1,174 +0,0 @@
package models
import (
"net/http"
"reflect"
"testing"
"github.com/thermokarst/bactdb/router"
)
func newTextMeasurementType() *TextMeasurementType {
text_measurement_type := NewTextMeasurementType()
text_measurement_type.Id = 1
return text_measurement_type
}
func TestTextMeasurementTypeService_Get(t *testing.T) {
setup()
defer teardown()
want := newTextMeasurementType()
var called bool
mux.HandleFunc(urlPath(t, router.TextMeasurementType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
writeJSON(w, want)
})
text_measurement_type, err := client.TextMeasurementTypes.Get(want.Id)
if err != nil {
t.Errorf("TextMeasurementTypes.Get returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(text_measurement_type, want) {
t.Errorf("TextMeasurementTypes.Get return %+v, want %+v", text_measurement_type, want)
}
}
func TestTextMeasurementTypeService_Create(t *testing.T) {
setup()
defer teardown()
want := newTextMeasurementType()
var called bool
mux.HandleFunc(urlPath(t, router.CreateTextMeasurementType, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "POST")
testBody(t, r, `{"id":1,"textMeasurementName":"Test Text Measurement Type","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n")
w.WriteHeader(http.StatusCreated)
writeJSON(w, want)
})
text_measurement_type := newTextMeasurementType()
created, err := client.TextMeasurementTypes.Create(text_measurement_type)
if err != nil {
t.Errorf("TextMeasurementTypes.Create returned error: %v", err)
}
if !created {
t.Error("!created")
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(text_measurement_type, want) {
t.Errorf("TextMeasurementTypes.Create returned %+v, want %+v", text_measurement_type, want)
}
}
func TestTextMeasurementTypeService_List(t *testing.T) {
setup()
defer teardown()
want := []*TextMeasurementType{newTextMeasurementType()}
var called bool
mux.HandleFunc(urlPath(t, router.TextMeasurementTypes, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
testFormValues(t, r, values{})
writeJSON(w, want)
})
text_measurement_type, err := client.TextMeasurementTypes.List(nil)
if err != nil {
t.Errorf("TextMeasurementTypes.List returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
for _, u := range want {
normalizeTime(&u.CreatedAt, &u.UpdatedAt, &u.DeletedAt)
}
if !reflect.DeepEqual(text_measurement_type, want) {
t.Errorf("TextMeasurementTypes.List return %+v, want %+v", text_measurement_type, want)
}
}
func TestTextMeasurementTypeService_Update(t *testing.T) {
setup()
defer teardown()
want := newTextMeasurementType()
var called bool
mux.HandleFunc(urlPath(t, router.UpdateTextMeasurementType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "PUT")
testBody(t, r, `{"id":1,"textMeasurementName":"Test Text Measurement Type Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
text_measurement_type := newTextMeasurementType()
text_measurement_type.TextMeasurementName = "Test Text Measurement Type Updated"
updated, err := client.TextMeasurementTypes.Update(text_measurement_type.Id, text_measurement_type)
if err != nil {
t.Errorf("TextMeasurementTypes.Update returned error: %v", err)
}
if !updated {
t.Error("!updated")
}
if !called {
t.Fatal("!called")
}
}
func TestTextMeasurementTypeService_Delete(t *testing.T) {
setup()
defer teardown()
want := newTextMeasurementType()
var called bool
mux.HandleFunc(urlPath(t, router.DeleteTextMeasurementType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "DELETE")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
deleted, err := client.TextMeasurementTypes.Delete(want.Id)
if err != nil {
t.Errorf("TextMeasurementTypes.Delete returned error: %v", err)
}
if !deleted {
t.Error("!deleted")
}
if !called {
t.Fatal("!called")
}
}

View file

@ -1,158 +0,0 @@
package models
import (
"bytes"
"database/sql"
"encoding/json"
"errors"
"strconv"
"strings"
"time"
"github.com/lib/pq"
)
type NullString struct {
sql.NullString
}
func (s *NullString) MarshalJSON() ([]byte, error) {
if !s.Valid {
return []byte("null"), nil
}
return json.Marshal(s.String)
}
func (s *NullString) UnmarshalJSON(b []byte) error {
if bytes.Equal(b, []byte("null")) {
s.String = ""
s.Valid = false
return nil
}
var x interface{}
var err error
json.Unmarshal(b, &x)
switch x.(type) {
case string:
err = json.Unmarshal(b, &s.String)
case map[string]interface{}:
err = json.Unmarshal(b, &s.NullString)
}
s.Valid = true
return err
}
type NullInt64 struct {
sql.NullInt64
}
func (i *NullInt64) MarshalJSON() ([]byte, error) {
if !i.Valid {
return []byte("null"), nil
}
return json.Marshal(i.Int64)
}
func (i *NullInt64) UnmarshalJSON(b []byte) error {
if bytes.Equal(b, []byte("null")) {
i.Int64 = 0
i.Valid = false
return nil
}
var x interface{}
var err error
json.Unmarshal(b, &x)
switch x.(type) {
case float64:
err = json.Unmarshal(b, &i.Int64)
case map[string]interface{}:
err = json.Unmarshal(b, &i.NullInt64)
}
i.Valid = true
return err
}
type NullFloat64 struct {
sql.NullFloat64
}
func (f *NullFloat64) MarshalJSON() ([]byte, error) {
if !f.Valid {
return []byte("null"), nil
}
return json.Marshal(f.Float64)
}
func (f *NullFloat64) UnmarshalJSON(b []byte) error {
if bytes.Equal(b, []byte("null")) {
f.Float64 = 0
f.Valid = false
return nil
}
var x interface{}
var err error
json.Unmarshal(b, &x)
switch x.(type) {
case float64:
err = json.Unmarshal(b, &f.Float64)
case map[string]interface{}:
err = json.Unmarshal(b, &f.NullFloat64)
}
f.Valid = true
return err
}
type NullTime struct {
pq.NullTime
}
func (t *NullTime) MarshalJSON() ([]byte, error) {
if !t.Valid {
return []byte("null"), nil
}
return json.Marshal(t.Time)
}
func (t *NullTime) UnmarshalJSON(b []byte) error {
if bytes.Equal(b, []byte("null")) {
var nt time.Time
t.Time = nt.In(time.UTC)
t.Valid = false
return nil
}
var x interface{}
var err error
json.Unmarshal(b, &x)
switch x.(type) {
case time.Time:
err = json.Unmarshal(b, &t.Time)
case map[string]interface{}:
err = json.Unmarshal(b, &t.NullTime)
}
t.Valid = true
return err
}
type NullSliceInt64 []int64
func (i *NullSliceInt64) Scan(src interface{}) error {
asBytes, ok := src.([]byte)
if !ok {
return errors.New("Scan source was not []byte")
}
asString := string(asBytes)
(*i) = strToIntSlice(asString)
return nil
}
func strToIntSlice(s string) []int64 {
r := strings.Trim(s, "{}")
a := []int64(nil)
if r != "NULL" {
for _, t := range strings.Split(r, ",") {
i, _ := strconv.ParseInt(t, 10, 64)
a = append(a, i)
}
}
return a
}

View file

@ -1,210 +0,0 @@
package models
import (
"errors"
"fmt"
"net/http"
"strconv"
"time"
"github.com/thermokarst/bactdb/router"
)
// A UnitType is a lookup type
type UnitType struct {
Id int64 `json:"id,omitempty"`
Name string `db:"name" json:"name"`
Symbol string `db:"symbol" json:"symbol"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
DeletedAt NullTime `db:"deleted_at" json:"deletedAt"`
}
func (m *UnitType) String() string {
return fmt.Sprintf("%v", *m)
}
func NewUnitType() *UnitType {
return &UnitType{
Name: "Test Unit Type",
Symbol: "x",
}
}
type UnitTypesService interface {
// Get a unit type
Get(id int64) (*UnitType, error)
// List all unit types
List(opt *UnitTypeListOptions) ([]*UnitType, error)
// Create a unit type
Create(unit_type *UnitType) (bool, error)
// Update a unit type
Update(id int64, UnitType *UnitType) (bool, error)
// Delete a unit type
Delete(id int64) (deleted bool, err error)
}
var (
ErrUnitTypeNotFound = errors.New("unit type not found")
)
type unitTypesService struct {
client *Client
}
func (s *unitTypesService) Get(id int64) (*UnitType, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.UnitType, map[string]string{"Id": strId}, nil)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var unit_type *UnitType
_, err = s.client.Do(req, &unit_type)
if err != nil {
return nil, err
}
return unit_type, nil
}
func (s *unitTypesService) Create(unit_type *UnitType) (bool, error) {
url, err := s.client.url(router.CreateUnitType, nil, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("POST", url.String(), unit_type)
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &unit_type)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusCreated, nil
}
type UnitTypeListOptions struct {
ListOptions
}
func (s *unitTypesService) List(opt *UnitTypeListOptions) ([]*UnitType, error) {
url, err := s.client.url(router.UnitTypes, nil, opt)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var unit_types []*UnitType
_, err = s.client.Do(req, &unit_types)
if err != nil {
return nil, err
}
return unit_types, nil
}
func (s *unitTypesService) Update(id int64, unit_type *UnitType) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.UpdateUnitType, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("PUT", url.String(), unit_type)
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &unit_type)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
func (s *unitTypesService) Delete(id int64) (bool, error) {
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.DeleteUnitType, map[string]string{"Id": strId}, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("DELETE", url.String(), nil)
if err != nil {
return false, err
}
var unit_type *UnitType
resp, err := s.client.Do(req, &unit_type)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusOK, nil
}
type MockUnitTypesService struct {
Get_ func(id int64) (*UnitType, error)
List_ func(opt *UnitTypeListOptions) ([]*UnitType, error)
Create_ func(unit_type *UnitType) (bool, error)
Update_ func(id int64, unit_type *UnitType) (bool, error)
Delete_ func(id int64) (bool, error)
}
var _ UnitTypesService = &MockUnitTypesService{}
func (s *MockUnitTypesService) Get(id int64) (*UnitType, error) {
if s.Get_ == nil {
return nil, nil
}
return s.Get_(id)
}
func (s *MockUnitTypesService) Create(unit_type *UnitType) (bool, error) {
if s.Create_ == nil {
return false, nil
}
return s.Create_(unit_type)
}
func (s *MockUnitTypesService) List(opt *UnitTypeListOptions) ([]*UnitType, error) {
if s.List_ == nil {
return nil, nil
}
return s.List_(opt)
}
func (s *MockUnitTypesService) Update(id int64, unit_type *UnitType) (bool, error) {
if s.Update_ == nil {
return false, nil
}
return s.Update_(id, unit_type)
}
func (s *MockUnitTypesService) Delete(id int64) (bool, error) {
if s.Delete_ == nil {
return false, nil
}
return s.Delete_(id)
}

View file

@ -1,174 +0,0 @@
package models
import (
"net/http"
"reflect"
"testing"
"github.com/thermokarst/bactdb/router"
)
func newUnitType() *UnitType {
unit_type := NewUnitType()
unit_type.Id = 1
return unit_type
}
func TestUnitTypeService_Get(t *testing.T) {
setup()
defer teardown()
want := newUnitType()
var called bool
mux.HandleFunc(urlPath(t, router.UnitType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
writeJSON(w, want)
})
unit_type, err := client.UnitTypes.Get(want.Id)
if err != nil {
t.Errorf("UnitTypes.Get returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(unit_type, want) {
t.Errorf("UnitTypes.Get return %+v, want %+v", unit_type, want)
}
}
func TestUnitTypeService_Create(t *testing.T) {
setup()
defer teardown()
want := newUnitType()
var called bool
mux.HandleFunc(urlPath(t, router.CreateUnitType, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "POST")
testBody(t, r, `{"id":1,"name":"Test Unit Type","symbol":"x","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n")
w.WriteHeader(http.StatusCreated)
writeJSON(w, want)
})
unit_type := newUnitType()
created, err := client.UnitTypes.Create(unit_type)
if err != nil {
t.Errorf("UnitTypes.Create returned error: %v", err)
}
if !created {
t.Error("!created")
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(unit_type, want) {
t.Errorf("UnitTypes.Create returned %+v, want %+v", unit_type, want)
}
}
func TestUnitTypeService_List(t *testing.T) {
setup()
defer teardown()
want := []*UnitType{newUnitType()}
var called bool
mux.HandleFunc(urlPath(t, router.UnitTypes, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
testFormValues(t, r, values{})
writeJSON(w, want)
})
unit_types, err := client.UnitTypes.List(nil)
if err != nil {
t.Errorf("UnitTypes.List returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
for _, u := range want {
normalizeTime(&u.CreatedAt, &u.UpdatedAt, &u.DeletedAt)
}
if !reflect.DeepEqual(unit_types, want) {
t.Errorf("UnitTypes.List return %+v, want %+v", unit_types, want)
}
}
func TestUnitTypeService_Update(t *testing.T) {
setup()
defer teardown()
want := newUnitType()
var called bool
mux.HandleFunc(urlPath(t, router.UpdateUnitType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "PUT")
testBody(t, r, `{"id":1,"name":"Test Unit Type Updated","symbol":"x","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
unit_type := newUnitType()
unit_type.Name = "Test Unit Type Updated"
updated, err := client.UnitTypes.Update(unit_type.Id, unit_type)
if err != nil {
t.Errorf("UnitTypes.Update returned error: %v", err)
}
if !updated {
t.Error("!updated")
}
if !called {
t.Fatal("!called")
}
}
func TestUnitTypeService_Delete(t *testing.T) {
setup()
defer teardown()
want := newUnitType()
var called bool
mux.HandleFunc(urlPath(t, router.DeleteUnitType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "DELETE")
w.WriteHeader(http.StatusOK)
writeJSON(w, want)
})
deleted, err := client.UnitTypes.Delete(want.Id)
if err != nil {
t.Errorf("UnitTypes.Delete returned error: %v", err)
}
if !deleted {
t.Error("!deleted")
}
if !called {
t.Fatal("!called")
}
}

View file

@ -1,191 +0,0 @@
package models
import (
"errors"
"fmt"
"net/http"
"strconv"
"time"
"github.com/thermokarst/bactdb/router"
)
// A User is a person that has administrative access to bactdb.
// Todo: add password
type User struct {
Id int64 `json:"id,omitempty"`
Username string `db:"username" json:"username"`
Password string `db:"password" json:"-"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
DeletedAt NullTime `db:"deleted_at" json:"deletedAt"`
}
type UserJSON struct {
User *User `json:"user"`
}
type UsersJSON struct {
Users []*User `json:"users"`
}
func (m *User) String() string {
return fmt.Sprintf("%v", *m)
}
func NewUser() *User {
return &User{Username: "Test User"}
}
// UsersService interacts with the user-related endpoints in bactdb's API.
type UsersService interface {
// Get a user.
Get(id int64) (*User, error)
// List all users.
List(opt *UserListOptions) ([]*User, error)
// Create a new user. The newly created user's ID is written to user.Id
Create(user *User) (created bool, err error)
// Authenticate a user, returns their access level.
Authenticate(username string, password string) (user_session *UserSession, err error)
}
type UserSession struct {
Token string `json:"token"`
AccessLevel string `json:"access_level"`
Genus string `json:"genus"`
}
var (
ErrUserNotFound = errors.New("user not found")
)
type usersService struct {
client *Client
}
func (s *usersService) Get(id int64) (*User, error) {
// Pass in key value pairs as strings, so that the gorilla mux URL
// generation is happy.
strId := strconv.FormatInt(id, 10)
url, err := s.client.url(router.User, map[string]string{"Id": strId}, nil)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var user *UserJSON
_, err = s.client.Do(req, &user)
if err != nil {
return nil, err
}
return user.User, nil
}
func (s *usersService) Create(user *User) (bool, error) {
url, err := s.client.url(router.CreateUser, nil, nil)
if err != nil {
return false, err
}
req, err := s.client.NewRequest("POST", url.String(), UserJSON{User: user})
if err != nil {
return false, err
}
resp, err := s.client.Do(req, &user)
if err != nil {
return false, err
}
return resp.StatusCode == http.StatusCreated, nil
}
type UserListOptions struct {
ListOptions
}
func (s *usersService) List(opt *UserListOptions) ([]*User, error) {
url, err := s.client.url(router.Users, nil, opt)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
var users *UsersJSON
_, err = s.client.Do(req, &users)
if err != nil {
return nil, err
}
return users.Users, nil
}
func (s *usersService) Authenticate(username string, password string) (*UserSession, error) {
url, err := s.client.url(router.GetToken, nil, nil)
if err != nil {
return nil, err
}
req, err := s.client.NewRequest("POST", url.String(), nil)
if err != nil {
return nil, err
}
var user_session *UserSession
_, err = s.client.Do(req, &user_session)
if err != nil {
return nil, err
}
return user_session, nil
}
type MockUsersService struct {
Get_ func(id int64) (*User, error)
List_ func(opt *UserListOptions) ([]*User, error)
Create_ func(user *User) (bool, error)
Authenticate_ func(username string, password string) (*UserSession, error)
}
var _ UsersService = &MockUsersService{}
func (s *MockUsersService) Get(id int64) (*User, error) {
if s.Get_ == nil {
return nil, nil
}
return s.Get_(id)
}
func (s *MockUsersService) Create(user *User) (bool, error) {
if s.Create_ == nil {
return false, nil
}
return s.Create_(user)
}
func (s *MockUsersService) List(opt *UserListOptions) ([]*User, error) {
if s.List_ == nil {
return nil, nil
}
return s.List_(opt)
}
func (s *MockUsersService) Authenticate(username string, password string) (*UserSession, error) {
if s.Authenticate_ == nil {
return &UserSession{}, nil
}
return s.Authenticate_(username, password)
}

View file

@ -1,114 +0,0 @@
package models
import (
"net/http"
"reflect"
"testing"
"github.com/thermokarst/bactdb/router"
)
func newUser() *User {
user := NewUser()
user.Id = 1
return user
}
func TestUsersService_Get(t *testing.T) {
setup()
defer teardown()
want := newUser()
var called bool
mux.HandleFunc(urlPath(t, router.User, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
writeJSON(w, UserJSON{User: want})
})
user, err := client.Users.Get(1)
if err != nil {
t.Errorf("Users.Get returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(user, want) {
t.Errorf("Users.Get returned %+v, want %+v", user, want)
}
}
func TestUsersService_Create(t *testing.T) {
setup()
defer teardown()
want := newUser()
var called bool
mux.HandleFunc(urlPath(t, router.CreateUser, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "POST")
testBody(t, r, `{"user":{"id":1,"username":"Test User","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}}`+"\n")
w.WriteHeader(http.StatusCreated)
writeJSON(w, want)
})
user := newUser()
created, err := client.Users.Create(user)
if err != nil {
t.Errorf("Users.Create returned error: %v", err)
}
if !created {
t.Error("!created")
}
if !called {
t.Fatal("!called")
}
normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt)
if !reflect.DeepEqual(user, want) {
t.Errorf("Users.Create returned %+v, want %+v", user, want)
}
}
func TestUsersService_List(t *testing.T) {
setup()
defer teardown()
want := []*User{newUser()}
var called bool
mux.HandleFunc(urlPath(t, router.Users, nil), func(w http.ResponseWriter, r *http.Request) {
called = true
testMethod(t, r, "GET")
testFormValues(t, r, values{})
writeJSON(w, UsersJSON{Users: want})
})
users, err := client.Users.List(nil)
if err != nil {
t.Errorf("Users.List returned error: %v", err)
}
if !called {
t.Fatal("!called")
}
for _, u := range want {
normalizeTime(&u.CreatedAt, &u.UpdatedAt, &u.DeletedAt)
}
if !reflect.DeepEqual(users, want) {
t.Errorf("Users.List return %+v, want %+v", users, want)
}
}