diff --git a/.circleci/config.yml b/.circleci/config.yml index 702f6723..4c79747e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,15 +2,15 @@ version: 2 jobs: build: docker: - - image: circleci/golang:1.8 + - image: circleci/golang:1.9 working_directory: /go/src/github.com/gocardless/draupnir environment: TEST_RESULTS: /tmp/test-results steps: - checkout - run: mkdir -p $TEST_RESULTS - - run: go get github.com/mattn/gom - - run: gom install + - run: go get github.com/golang/dep/cmd/dep + - run: dep ensure -vendor-only - run: make test - run: make build-production - run: make client diff --git a/Gomfile b/Gomfile deleted file mode 100644 index 9cb988eb..00000000 --- a/Gomfile +++ /dev/null @@ -1,10 +0,0 @@ -gom "github.com/kelseyhightower/envconfig" -gom "github.com/rubenv/sql-migrate" -gom "github.com/gorilla/mux" -gom "github.com/stretchr/testify" -gom "github.com/willdonnelly/passwd" -gom "github.com/google/jsonapi" -gom "github.com/coreos/go-iptables" -gom "github.com/urfave/cli" -gom "golang.org/x/oauth2" -gom "google.golang.org/api/oauth2/v1" diff --git a/Gomfile.lock b/Gomfile.lock deleted file mode 100644 index 6e1e1cba..00000000 --- a/Gomfile.lock +++ /dev/null @@ -1,10 +0,0 @@ -gom 'github.com/kelseyhightower/envconfig', :commit => '5c008110b20b657eb7e005b83d0a5f6aa6bb5f4b' -gom 'github.com/rubenv/sql-migrate', :commit => '1ed79968dfca5de79adb13c84523caaa4fc865a9' -gom 'github.com/gorilla/mux', :commit => '757bef944d0f21880861c2dd9c871ca543023cba' -gom 'github.com/stretchr/testify', :commit => '2402e8e7a02fc811447d11f881aa9746cdc57983' -gom 'github.com/willdonnelly/passwd', :commit => '7935dab3074ca1d47c8805e0230f8685116b6019' -gom 'github.com/google/jsonapi', :commit => '925ebf2136461bb9d121340ec95a719bf9073d1c' -gom 'github.com/coreos/go-iptables', :commit => '5463fbac3bcc6b990663941c2e12660d19f6b36d' -gom 'github.com/urfave/cli', :commit => '4b90d79a682b4bf685762c7452db20f2a676ecb2' -gom 'golang.org/x/oauth2', :commit => 'cce311a261e6fcf29de72ca96827bdb0b7d9c9e6' -gom 'google.golang.org/api/oauth2/v1', :commit => 'e665075b5ff79143ba49c58fab02df9dc122afd5' diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 00000000..dbb015af --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,92 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "github.com/davecgh/go-spew" + packages = ["spew"] + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" + +[[projects]] + branch = "master" + name = "github.com/golang/protobuf" + packages = ["proto"] + revision = "1643683e1b54a9e88ad26d98f81400c8c9d9f4f9" + +[[projects]] + name = "github.com/google/jsonapi" + packages = ["."] + revision = "925ebf2136461bb9d121340ec95a719bf9073d1c" + +[[projects]] + name = "github.com/gorilla/context" + packages = ["."] + revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a" + version = "v1.1" + +[[projects]] + name = "github.com/gorilla/mux" + packages = ["."] + revision = "24fca303ac6da784b9e8269f724ddeb0b2eea5e7" + version = "v1.5.0" + +[[projects]] + name = "github.com/kelseyhightower/envconfig" + packages = ["."] + revision = "f611eb38b3875cc3bd991ca91c51d06446afa14c" + version = "v1.3.0" + +[[projects]] + branch = "master" + name = "github.com/lib/pq" + packages = [".","oid"] + revision = "b0d5024adb34b4122c6ee7eeb6ab511f7223222d" + +[[projects]] + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + name = "github.com/stretchr/testify" + packages = ["assert"] + revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0" + version = "v1.1.4" + +[[projects]] + name = "github.com/urfave/cli" + packages = ["."] + revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" + version = "v1.20.0" + +[[projects]] + branch = "master" + name = "golang.org/x/net" + packages = ["context","context/ctxhttp"] + revision = "cd69bc3fc700721b709c3a59e16e24c67b58f6ff" + +[[projects]] + branch = "master" + name = "golang.org/x/oauth2" + packages = [".","internal"] + revision = "bb50c06baba3d0c76f9d125c0719093e315b5b44" + +[[projects]] + branch = "master" + name = "google.golang.org/api" + packages = ["gensupport","googleapi","googleapi/internal/uritemplates","oauth2/v1"] + revision = "7afc123cf726cd2f253faa3e144d2ab65477b18f" + +[[projects]] + name = "google.golang.org/appengine" + packages = ["internal","internal/base","internal/datastore","internal/log","internal/remote_api","internal/urlfetch","urlfetch"] + revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" + version = "v1.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "f5187f1c8826e109ea78092a56c30a73e55cb348ee9a3f9e7cc758329fda1365" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 00000000..9f4f4b38 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,58 @@ + +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + + +[[constraint]] + name = "github.com/gorilla/mux" + version = "1.5.0" + +[[constraint]] + name = "github.com/google/jsonapi" + revision = "925ebf2136461bb9d121340ec95a719bf9073d1c" + +[[constraint]] + name = "github.com/kelseyhightower/envconfig" + version = "1.3.0" + +[[constraint]] + branch = "master" + name = "github.com/lib/pq" + +[[constraint]] + name = "github.com/stretchr/testify" + version = "1.1.4" + +[[constraint]] + name = "github.com/urfave/cli" + version = "1.20.0" + +[[constraint]] + branch = "master" + name = "golang.org/x/net" + +[[constraint]] + branch = "master" + name = "golang.org/x/oauth2" + +[[constraint]] + branch = "master" + name = "google.golang.org/api" diff --git a/README.md b/README.md index ed86a59e..092f14e5 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,14 @@ Prerequisites: - Go - Postgresql -Install [gom](https://github.com/mattn/gom) +Install [dep](https://github.com/golang/dep) ``` -go get github.com/mattn/gom +brew install dep ``` Install dependencies ``` -gom install +dep ensure ``` Create the database diff --git a/models/instance.go b/models/instance.go index b93c355e..5634e15d 100644 --- a/models/instance.go +++ b/models/instance.go @@ -16,8 +16,8 @@ type Instance struct { func NewInstance(imageID int, email string) Instance { return Instance{ ImageID: imageID, + UserEmail: email, CreatedAt: time.Now(), UpdatedAt: time.Now(), - UserEmail: email, } } diff --git a/routes/fakes.go b/routes/fakes.go index c7eb357d..c2fe553f 100644 --- a/routes/fakes.go +++ b/routes/fakes.go @@ -99,7 +99,7 @@ func (f FakeAuthenticator) AuthenticateRequest(r *http.Request) (string, error) type AllowAll struct{} func (a AllowAll) AuthenticateRequest(r *http.Request) (string, error) { - return "hmac@gocardless.com", nil + return "test@draupnir", nil } type FakeOAuthClient struct { diff --git a/routes/fixtures.go b/routes/fixtures.go index d59744f0..add59945 100644 --- a/routes/fixtures.go +++ b/routes/fixtures.go @@ -61,10 +61,10 @@ var createInstanceFixture = jsonapi.OnePayload{ Type: "instances", ID: "1", Attributes: map[string]interface{}{ - "image_id": 1, + "image_id": float64(1), "created_at": "2016-01-01T12:33:44Z", "updated_at": "2016-01-01T12:33:44Z", - "port": 0, + "port": float64(0), }, }, } @@ -75,9 +75,9 @@ var listInstancesFixture = jsonapi.ManyPayload{ Type: "instances", ID: "1", Attributes: map[string]interface{}{ - "image_id": 1, + "image_id": float64(1), "created_at": "2016-01-01T12:33:44Z", - "port": 5432, + "port": float64(5432), "updated_at": "2016-01-01T12:33:44Z", }, }, @@ -89,9 +89,9 @@ var getInstanceFixture = jsonapi.OnePayload{ Type: "instances", ID: "1", Attributes: map[string]interface{}{ - "image_id": 1, + "image_id": float64(1), "created_at": "2016-01-01T12:33:44Z", - "port": 5432, + "port": float64(5432), "updated_at": "2016-01-01T12:33:44Z", }, }, diff --git a/routes/images_test.go b/routes/images_test.go index dccf3242..96e3dc9c 100644 --- a/routes/images_test.go +++ b/routes/images_test.go @@ -1,8 +1,11 @@ package routes import ( + "bytes" "encoding/json" "errors" + "io" + "log" "net/http" "net/http/httptest" "strings" @@ -11,13 +14,21 @@ import ( "github.com/gocardless/draupnir/auth" "github.com/gocardless/draupnir/models" + "github.com/google/jsonapi" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" ) +func decodeJSON(r io.Reader, out interface{}) { + err := json.NewDecoder(r).Decode(out) + if err != nil { + log.Panic(err) + } +} + func TestGetImage(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/images/1", nil) + req := httptest.NewRequest("GET", "/images/1", nil) store := FakeImageStore{ _Get: func(id int) (models.Image, error) { @@ -36,21 +47,16 @@ func TestGetImage(t *testing.T) { router.HandleFunc("/images/{id}", routeSet.Get) router.ServeHTTP(recorder, req) - expected, err := json.Marshal(getImageFixture) - if err != nil { - t.Fatal(err) - } + var response jsonapi.OnePayload + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusOK, recorder.Code) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, getImageFixture, response) } func TestGetImageWhenAuthenticationFails(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/images/1", nil) - if err != nil { - t.Fatal(err) - } + req := httptest.NewRequest("GET", "/images/1", nil) authenticator := FakeAuthenticator{ _AuthenticateRequest: func(r *http.Request) (string, error) { @@ -66,10 +72,7 @@ func TestGetImageWhenAuthenticationFails(t *testing.T) { func TestListImages(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/images", nil) - if err != nil { - t.Fatal(err) - } + req := httptest.NewRequest("GET", "/images", nil) store := FakeImageStore{ _List: func() ([]models.Image, error) { @@ -85,37 +88,37 @@ func TestListImages(t *testing.T) { }, } - handler := http.HandlerFunc(Images{ImageStore: store, Authenticator: AllowAll{}}.List) - handler.ServeHTTP(recorder, req) + handler := Images{ImageStore: store, Authenticator: AllowAll{}}.List + handler(recorder, req) - expected, err := json.Marshal(listImagesFixture) - if err != nil { - t.Fatal(err.Error()) - } + var response jsonapi.ManyPayload + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusOK, recorder.Code) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, response, listImagesFixture) } func TestCreateImage(t *testing.T) { recorder := httptest.NewRecorder() - body := `{"data":{"type":"images","attributes":{"backed_up_at": "2016-01-01T12:33:44.567Z","anonymisation_script":"SELECT * FROM foo;"}}}` - req, err := http.NewRequest("POST", "/images", strings.NewReader(body)) - if err != nil { - t.Fatal(err) + request := createImageRequest{ + BackedUpAt: timestamp(), + Anon: "SELECT * FROM foo;", } + body := bytes.NewBuffer([]byte{}) + jsonapi.MarshalOnePayload(body, &request) + + req := httptest.NewRequest("POST", "/images", body) executor := FakeExecutor{ - _CreateBtrfsSubvolume: func(id int) error { - return nil - }, + _CreateBtrfsSubvolume: func(id int) error { assert.Equal(t, id, 1); return nil }, } store := FakeImageStore{ _Create: func(image models.Image) (models.Image, error) { + assert.Equal(t, image.Anon, "SELECT * FROM foo;") return models.Image{ ID: 1, - BackedUpAt: timestamp(), + BackedUpAt: image.BackedUpAt, Ready: false, CreatedAt: timestamp(), UpdatedAt: timestamp(), @@ -124,45 +127,39 @@ func TestCreateImage(t *testing.T) { } routeSet := Images{ImageStore: store, Executor: executor, Authenticator: AllowAll{}} - handler := http.HandlerFunc(routeSet.Create) - handler.ServeHTTP(recorder, req) + routeSet.Create(recorder, req) - expected, err := json.Marshal(createImageFixture) - if err != nil { - t.Fatal(err) - } + var response jsonapi.OnePayload + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusCreated, recorder.Code) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, createImageFixture, response) } func TestImageCreateReturnsErrorWithInvalidPayload(t *testing.T) { recorder := httptest.NewRecorder() body := `{"this is": "not a valid JSON API request payload"}` - req, err := http.NewRequest("POST", "/images", strings.NewReader(body)) - if err != nil { - t.Fatal(err) - } + req := httptest.NewRequest("POST", "/images", strings.NewReader(body)) routeSet := Images{Authenticator: AllowAll{}} - handler := http.HandlerFunc(routeSet.Create) - handler.ServeHTTP(recorder, req) + routeSet.Create(recorder, req) + + var response APIError + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusBadRequest, recorder.Code) - expected, err := json.Marshal(invalidJSONError) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, invalidJSONError, response) } func TestImageCreateReturnsErrorWhenSubvolumeCreationFails(t *testing.T) { recorder := httptest.NewRecorder() - body := `{"data": { "type": "images", "attributes": { "backed_up_at": "2016-01-01T12:33:44.567Z"} } }` - req, err := http.NewRequest("POST", "/images", strings.NewReader(body)) - if err != nil { - t.Fatal(err) + request := createImageRequest{ + BackedUpAt: timestamp(), + Anon: "SELECT * FROM foo;", } + body := bytes.NewBuffer([]byte{}) + jsonapi.MarshalOnePayload(body, &request) + req := httptest.NewRequest("POST", "/images", body) store := FakeImageStore{ _Create: func(image models.Image) (models.Image, error) { @@ -182,42 +179,45 @@ func TestImageCreateReturnsErrorWhenSubvolumeCreationFails(t *testing.T) { }, } routeSet := Images{ImageStore: store, Executor: executor, Authenticator: AllowAll{}} - handler := http.HandlerFunc(routeSet.Create) - handler.ServeHTTP(recorder, req) + routeSet.Create(recorder, req) + + var response APIError + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusInternalServerError, recorder.Code) - expected, err := json.Marshal(internalServerError) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, internalServerError, response) } func TestImageDone(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("POST", "/images/1/done", nil) - if err != nil { - t.Fatal(err) + req := httptest.NewRequest("POST", "/images/1/done", nil) + + image := models.Image{ + ID: 1, + BackedUpAt: timestamp(), + Ready: false, + CreatedAt: timestamp(), + UpdatedAt: timestamp(), } store := FakeImageStore{ _Get: func(id int) (models.Image, error) { - return models.Image{ - ID: 1, - BackedUpAt: timestamp(), - Ready: false, - CreatedAt: timestamp(), - UpdatedAt: timestamp(), - }, nil - }, - _MarkAsReady: func(image models.Image) (models.Image, error) { - image.Ready = true + assert.Equal(t, 1, id) + return image, nil }, + _MarkAsReady: func(i models.Image) (models.Image, error) { + assert.Equal(t, image, i) + + i.Ready = true + return i, nil + }, } executor := FakeExecutor{ - _FinaliseImage: func(image models.Image) error { + _FinaliseImage: func(i models.Image) error { + assert.Equal(t, image, i) + return nil }, } @@ -227,66 +227,63 @@ func TestImageDone(t *testing.T) { router.HandleFunc("/images/{id}/done", routeSet.Done) router.ServeHTTP(recorder, req) - expected, err := json.Marshal(doneImageFixture) - if err != nil { - t.Fatal(err) - } + var response jsonapi.OnePayload + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusOK, recorder.Code) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, doneImageFixture, response) } func TestImageDoneWithNonNumericID(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("POST", "/images/bad_id/done", nil) - if err != nil { - t.Fatal(err) - } + req := httptest.NewRequest("POST", "/images/bad_id/done", nil) routeSet := Images{Authenticator: AllowAll{}} router := mux.NewRouter() router.HandleFunc("/images/{id}/done", routeSet.Done) router.ServeHTTP(recorder, req) - expected, err := json.Marshal(notFoundError) - if err != nil { - t.Fatal(err) - } + var response APIError + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusNotFound, recorder.Code) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, notFoundError, response) } func TestImageDestroy(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("DELETE", "/images/1", nil) - if err != nil { - t.Fatal(err) + req := httptest.NewRequest("DELETE", "/images/1", nil) + + image := models.Image{ + ID: 1, + BackedUpAt: timestamp(), + Ready: false, + CreatedAt: timestamp(), + UpdatedAt: timestamp(), } store := FakeImageStore{ _Get: func(id int) (models.Image, error) { - return models.Image{ - ID: 1, - BackedUpAt: timestamp(), - Ready: false, - CreatedAt: timestamp(), - UpdatedAt: timestamp(), - }, nil + assert.Equal(t, 1, id) + + return image, nil }, - _Destroy: func(image models.Image) error { + _Destroy: func(i models.Image) error { + assert.Equal(t, image, i) return nil }, } executor := FakeExecutor{ _DestroyImage: func(imageID int) error { + assert.Equal(t, 1, imageID) return nil }, } router := mux.NewRouter() - router.HandleFunc("/images/{id}", Images{ImageStore: store, Executor: executor, Authenticator: AllowAll{}}.Destroy).Methods("DELETE") + routeSet := Images{ImageStore: store, Executor: executor, Authenticator: AllowAll{}} + router.HandleFunc("/images/{id}", routeSet.Destroy).Methods("DELETE") router.ServeHTTP(recorder, req) assert.Equal(t, http.StatusNoContent, recorder.Code) @@ -295,22 +292,23 @@ func TestImageDestroy(t *testing.T) { func TestImageDestroyFromUploadUser(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("DELETE", "/images/1", nil) - if err != nil { - t.Fatal(err) + req := httptest.NewRequest("DELETE", "/images/1", nil) + + image := models.Image{ + ID: 1, + BackedUpAt: timestamp(), + Ready: false, + CreatedAt: timestamp(), + UpdatedAt: timestamp(), } imageStore := FakeImageStore{ _Get: func(id int) (models.Image, error) { - return models.Image{ - ID: 1, - BackedUpAt: timestamp(), - Ready: false, - CreatedAt: timestamp(), - UpdatedAt: timestamp(), - }, nil + assert.Equal(t, 1, id) + return image, nil }, - _Destroy: func(image models.Image) error { + _Destroy: func(i models.Image) error { + assert.Equal(t, image, i) return nil }, } @@ -333,6 +331,7 @@ func TestImageDestroyFromUploadUser(t *testing.T) { executor := FakeExecutor{ _DestroyImage: func(imageID int) error { + assert.Equal(t, 1, imageID) return nil }, _DestroyInstance: func(id int) error { @@ -347,10 +346,8 @@ func TestImageDestroyFromUploadUser(t *testing.T) { } router := mux.NewRouter() - router.HandleFunc( - "/images/{id}", - Images{ImageStore: imageStore, InstanceStore: instanceStore, Executor: executor, Authenticator: authenticator}.Destroy, - ).Methods("DELETE") + routeSet := Images{ImageStore: imageStore, InstanceStore: instanceStore, Executor: executor, Authenticator: authenticator} + router.HandleFunc("/images/{id}", routeSet.Destroy).Methods("DELETE") router.ServeHTTP(recorder, req) assert.Equal(t, http.StatusNoContent, recorder.Code) diff --git a/routes/instances_test.go b/routes/instances_test.go index 99043d36..ef0bde03 100644 --- a/routes/instances_test.go +++ b/routes/instances_test.go @@ -1,28 +1,31 @@ package routes import ( + "bytes" "encoding/json" "net/http" "net/http/httptest" - "strings" "testing" "github.com/gocardless/draupnir/models" + "github.com/google/jsonapi" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" ) func TestInstanceCreate(t *testing.T) { recorder := httptest.NewRecorder() - body := `{"data":{"type":"instances","attributes":{"image_id":"1"}}}` - req, err := http.NewRequest("POST", "/instances", strings.NewReader(body)) - if err != nil { - t.Fatal(err) - } + request := createInstanceRequest{ImageID: "1"} + body := bytes.NewBuffer([]byte{}) + jsonapi.MarshalOnePayload(body, &request) + req := httptest.NewRequest("POST", "/instances", body) instanceStore := FakeInstanceStore{ - _Create: func(image models.Instance) (models.Instance, error) { + _Create: func(instance models.Instance) (models.Instance, error) { + assert.Equal(t, 1, instance.ImageID) + assert.True(t, instance.Port > 5432, "port is greater than 5432") + assert.True(t, instance.Port < 6000, "port is less than 6000") return models.Instance{ ID: 1, ImageID: 1, @@ -34,12 +37,15 @@ func TestInstanceCreate(t *testing.T) { imageStore := FakeImageStore{ _Get: func(id int) (models.Image, error) { + assert.Equal(t, 1, id) return models.Image{ID: 1, Ready: true}, nil }, } executor := FakeExecutor{ _CreateInstance: func(instanceID int, imageID int, port int) error { + assert.Equal(t, 1, instanceID) + assert.Equal(t, 1, imageID) return nil }, } @@ -50,26 +56,23 @@ func TestInstanceCreate(t *testing.T) { Executor: executor, Authenticator: AllowAll{}, } - handler := http.HandlerFunc(routeSet.Create) - handler.ServeHTTP(recorder, req) + routeSet.Create(recorder, req) - expected, err := json.Marshal(createInstanceFixture) - if err != nil { - t.Fatal(err) - } + var response jsonapi.OnePayload + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusCreated, recorder.Code) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, createInstanceFixture, response) } func TestInstanceCreateReturnsErrorWithUnreadyImage(t *testing.T) { recorder := httptest.NewRecorder() - body := `{"data":{"type":"instances","attributes":{"image_id":"1"}}}` - req, err := http.NewRequest("POST", "/instances", strings.NewReader(body)) - if err != nil { - t.Fatal(err) - } + request := createInstanceRequest{ImageID: "1"} + body := bytes.NewBuffer([]byte{}) + jsonapi.MarshalOnePayload(body, &request) + + req := httptest.NewRequest("POST", "/instances", body) instanceStore := FakeInstanceStore{ _Create: func(image models.Instance) (models.Instance, error) { @@ -100,66 +103,52 @@ func TestInstanceCreateReturnsErrorWithUnreadyImage(t *testing.T) { Executor: executor, Authenticator: AllowAll{}, } - handler := http.HandlerFunc(routeSet.Create) - handler.ServeHTTP(recorder, req) + routeSet.Create(recorder, req) - assert.Equal(t, http.StatusUnprocessableEntity, recorder.Code) - expected, err := json.Marshal(unreadyImageError) - if err != nil { - t.Fatal(err) - } + var response APIError + decodeJSON(recorder.Body, &response) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, http.StatusUnprocessableEntity, recorder.Code) + assert.Equal(t, unreadyImageError, response) } func TestInstanceCreateReturnsErrorWithInvalidPayload(t *testing.T) { recorder := httptest.NewRecorder() - body := `{"this is": "not a valid JSON API request payload"}` - req, err := http.NewRequest("POST", "/instances", strings.NewReader(body)) - if err != nil { - t.Fatal(err) - } + request := map[string]string{"this is": "not a valid JSON API request payload"} + body := bytes.NewBuffer([]byte{}) + json.NewEncoder(body).Encode(&request) + req := httptest.NewRequest("POST", "/instances", body) - handler := http.HandlerFunc(Instances{Authenticator: AllowAll{}}.Create) - handler.ServeHTTP(recorder, req) + Instances{Authenticator: AllowAll{}}.Create(recorder, req) - assert.Equal(t, http.StatusBadRequest, recorder.Code) - expected, err := json.Marshal(invalidJSONError) - if err != nil { - t.Fatal(err) - } + var response APIError + decodeJSON(recorder.Body, &response) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, http.StatusBadRequest, recorder.Code) + assert.Equal(t, invalidJSONError, response) } func TestInstanceCreateWithInvalidImageID(t *testing.T) { recorder := httptest.NewRecorder() - body := `{"data":{"type":"instances","attributes":{"image_id":"garbage"}}}` + request := createInstanceRequest{ImageID: "garbage"} + body := bytes.NewBuffer([]byte{}) + jsonapi.MarshalOnePayload(body, &request) - req, err := http.NewRequest("POST", "/instances", strings.NewReader(body)) - if err != nil { - t.Fatal(err) - } + req := httptest.NewRequest("POST", "/instances", body) routeSet := Instances{Executor: FakeExecutor{}, Authenticator: AllowAll{}} - handler := http.HandlerFunc(routeSet.Create) - handler.ServeHTTP(recorder, req) + routeSet.Create(recorder, req) - expected, err := json.Marshal(badImageIDError) - if err != nil { - t.Fatal(err) - } + var response APIError + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusBadRequest, recorder.Code) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, badImageIDError, response) } func TestInstanceList(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/instances", nil) - if err != nil { - t.Fatal(err) - } + req := httptest.NewRequest("GET", "/instances", nil) store := FakeInstanceStore{ _List: func() ([]models.Instance, error) { @@ -170,7 +159,7 @@ func TestInstanceList(t *testing.T) { Port: 5432, CreatedAt: timestamp(), UpdatedAt: timestamp(), - UserEmail: "hmac@gocardless.com", + UserEmail: "test@draupnir", }, models.Instance{ ID: 2, @@ -178,31 +167,25 @@ func TestInstanceList(t *testing.T) { Port: 5433, CreatedAt: timestamp(), UpdatedAt: timestamp(), - UserEmail: "alice@gocardless.com", + UserEmail: "otheruser@draupnir", }, }, nil }, } - handler := http.HandlerFunc(Instances{InstanceStore: store, Authenticator: AllowAll{}}.List) - handler.ServeHTTP(recorder, req) + routeSet := Instances{InstanceStore: store, Authenticator: AllowAll{}} + routeSet.List(recorder, req) - assert.Equal(t, http.StatusOK, recorder.Code) - - expected, err := json.Marshal(listInstancesFixture) - if err != nil { - t.Fatal(err.Error()) - } + var response jsonapi.ManyPayload + decodeJSON(recorder.Body, &response) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, listInstancesFixture, response) } func TestInstanceGet(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/instances/1", nil) - if err != nil { - t.Fatal(err) - } + req := httptest.NewRequest("GET", "/instances/1", nil) store := FakeInstanceStore{ _Get: func(id int) (models.Instance, error) { @@ -212,63 +195,56 @@ func TestInstanceGet(t *testing.T) { Port: 5432, CreatedAt: timestamp(), UpdatedAt: timestamp(), - UserEmail: "hmac@gocardless.com", + UserEmail: "test@draupnir", }, nil }, } + routeSet := Instances{InstanceStore: store, Authenticator: AllowAll{}} router := mux.NewRouter() - router.HandleFunc("/instances/{id}", Instances{InstanceStore: store, Authenticator: AllowAll{}}.Get) + router.HandleFunc("/instances/{id}", routeSet.Get) router.ServeHTTP(recorder, req) - expected, err := json.Marshal(getInstanceFixture) - if err != nil { - t.Fatal(err.Error()) - } + var response jsonapi.OnePayload + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusOK, recorder.Code) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, getInstanceFixture, response) } func TestInstanceGetFromWrongUser(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/instances/1", nil) - if err != nil { - t.Fatal(err) - } + req := httptest.NewRequest("GET", "/instances/1", nil) store := FakeInstanceStore{ _Get: func(id int) (models.Instance, error) { + assert.Equal(t, 1, id) + return models.Instance{ ID: 1, ImageID: 1, Port: 5432, CreatedAt: timestamp(), UpdatedAt: timestamp(), - UserEmail: "alice@gocardless.com", + UserEmail: "otheruser@draupnir", }, nil }, } - router := mux.NewRouter() - router.HandleFunc("/instances/{id}", Instances{InstanceStore: store, Authenticator: AllowAll{}}.Get) - router.ServeHTTP(recorder, req) + // AllowAll will return a user email of test@draupnir + routeSet := Instances{InstanceStore: store, Authenticator: AllowAll{}} + routeSet.Get(recorder, req) - expected, err := json.Marshal(notFoundError) - if err != nil { - t.Fatal(err.Error()) - } + var response APIError + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusNotFound, recorder.Code) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, notFoundError, response) } func TestInstanceDestroy(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("DELETE", "/instances/1", nil) - if err != nil { - t.Fatal(err) - } + req := httptest.NewRequest("DELETE", "/instances/1", nil) store := FakeInstanceStore{ _Get: func(id int) (models.Instance, error) { @@ -278,7 +254,7 @@ func TestInstanceDestroy(t *testing.T) { Port: 5432, CreatedAt: timestamp(), UpdatedAt: timestamp(), - UserEmail: "hmac@gocardless.com", + UserEmail: "test@draupnir", }, nil }, _Destroy: func(instance models.Instance) error { @@ -292,11 +268,9 @@ func TestInstanceDestroy(t *testing.T) { }, } + routeSet := Instances{InstanceStore: store, Executor: executor, Authenticator: AllowAll{}} router := mux.NewRouter() - router.HandleFunc( - "/instances/{id}", - Instances{InstanceStore: store, Executor: executor, Authenticator: AllowAll{}}.Destroy, - ).Methods("DELETE") + router.HandleFunc("/instances/{id}", routeSet.Destroy).Methods("DELETE") router.ServeHTTP(recorder, req) assert.Equal(t, http.StatusNoContent, recorder.Code) @@ -305,10 +279,7 @@ func TestInstanceDestroy(t *testing.T) { func TestInstanceDestroyFromWrongUser(t *testing.T) { recorder := httptest.NewRecorder() - req, err := http.NewRequest("DELETE", "/instances/1", nil) - if err != nil { - t.Fatal(err) - } + req := httptest.NewRequest("DELETE", "/instances/1", nil) store := FakeInstanceStore{ _Get: func(id int) (models.Instance, error) { @@ -318,7 +289,7 @@ func TestInstanceDestroyFromWrongUser(t *testing.T) { Port: 5432, CreatedAt: timestamp(), UpdatedAt: timestamp(), - UserEmail: "alice@gocardless.com", + UserEmail: "otheruser@draupnir", }, nil }, _Destroy: func(instance models.Instance) error { @@ -332,19 +303,15 @@ func TestInstanceDestroyFromWrongUser(t *testing.T) { }, } + // AllowAll will return a user email of test@draupnir + routeSet := Instances{InstanceStore: store, Executor: executor, Authenticator: AllowAll{}} router := mux.NewRouter() - // AllowAll will return a user email of hmac@gocardless.com - router.HandleFunc( - "/instances/{id}", - Instances{InstanceStore: store, Executor: executor, Authenticator: AllowAll{}}.Destroy, - ).Methods("DELETE") + router.HandleFunc("/instances/{id}", routeSet.Destroy).Methods("DELETE") router.ServeHTTP(recorder, req) - expected, err := json.Marshal(notFoundError) - if err != nil { - t.Fatal(err.Error()) - } + var response APIError + decodeJSON(recorder.Body, &response) assert.Equal(t, http.StatusNotFound, recorder.Code) - assert.Equal(t, append(expected, byte('\n')), recorder.Body.Bytes()) + assert.Equal(t, notFoundError, response) }