From 39fb26677664335075c02f5d90bf1d7f74b2be72 Mon Sep 17 00:00:00 2001 From: Chris Shoemaker Date: Sat, 21 Dec 2013 12:50:46 -0500 Subject: [PATCH 1/4] fix(error.go): fix typo in comment --- error/error.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/error/error.go b/error/error.go index 3eb6c4561..f7372eba2 100644 --- a/error/error.go +++ b/error/error.go @@ -111,7 +111,7 @@ func (e Error) toJsonString() string { func (e Error) Write(w http.ResponseWriter) { w.Header().Add("X-Etcd-Index", fmt.Sprint(e.Index)) - // 3xx is reft internal error + // 3xx is raft internal error if e.ErrorCode/100 == 3 { http.Error(w, e.toJsonString(), http.StatusInternalServerError) } else { From 3cde996d219e74efc0df05e13a0b5d0cad2e6318 Mon Sep 17 00:00:00 2001 From: Chris Shoemaker Date: Sat, 21 Dec 2013 12:53:39 -0500 Subject: [PATCH 2/4] refactor(v2/tests): don't repeat construction of full test URL --- server/v2/tests/get_handler_test.go | 5 +++-- server/v2/tests/post_handler_test.go | 5 +++-- server/v2/tests/put_handler_test.go | 30 +++++++++++++++++----------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/server/v2/tests/get_handler_test.go b/server/v2/tests/get_handler_test.go index a8dd0e1d3..803fe4377 100644 --- a/server/v2/tests/get_handler_test.go +++ b/server/v2/tests/get_handler_test.go @@ -20,9 +20,10 @@ func TestV2GetKey(t *testing.T) { tests.RunServer(func(s *server.Server) { v := url.Values{} v.Set("value", "XXX") - resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") + resp, _ := tests.PutForm(fullURL, v) tests.ReadBody(resp) - resp, _ = tests.Get(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar")) + resp, _ = tests.Get(fullURL) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["action"], "get", "") node := body["node"].(map[string]interface{}) diff --git a/server/v2/tests/post_handler_test.go b/server/v2/tests/post_handler_test.go index a048d5cb5..bd6c900b7 100644 --- a/server/v2/tests/post_handler_test.go +++ b/server/v2/tests/post_handler_test.go @@ -18,7 +18,8 @@ import ( func TestV2CreateUnique(t *testing.T) { tests.RunServer(func(s *server.Server) { // POST should add index to list. - resp, _ := tests.PostForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), nil) + fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") + resp, _ := tests.PostForm(fullURL, nil) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["action"], "create", "") @@ -28,7 +29,7 @@ func TestV2CreateUnique(t *testing.T) { assert.Equal(t, node["modifiedIndex"], 2, "") // Second POST should add next index to list. - resp, _ = tests.PostForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), nil) + resp, _ = tests.PostForm(fullURL, nil) body = tests.ReadBodyJSON(resp) node = body["node"].(map[string]interface{}) diff --git a/server/v2/tests/put_handler_test.go b/server/v2/tests/put_handler_test.go index 7b51da4c2..7ece21aac 100644 --- a/server/v2/tests/put_handler_test.go +++ b/server/v2/tests/put_handler_test.go @@ -103,9 +103,10 @@ func TestV2CreateKeyFail(t *testing.T) { v := url.Values{} v.Set("value", "XXX") v.Set("prevExist", "false") - resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") + resp, _ := tests.PutForm(fullURL, v) tests.ReadBody(resp) - resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + resp, _ = tests.PutForm(fullURL, v) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 105, "") assert.Equal(t, body["message"], "Key already exists", "") @@ -123,12 +124,13 @@ func TestV2UpdateKeySuccess(t *testing.T) { v := url.Values{} v.Set("value", "XXX") - resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") + resp, _ := tests.PutForm(fullURL, v) tests.ReadBody(resp) v.Set("value", "YYY") v.Set("prevExist", "true") - resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + resp, _ = tests.PutForm(fullURL, v) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["action"], "update", "") }) @@ -181,11 +183,12 @@ func TestV2SetKeyCASOnIndexSuccess(t *testing.T) { tests.RunServer(func(s *server.Server) { v := url.Values{} v.Set("value", "XXX") - resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") + resp, _ := tests.PutForm(fullURL, v) tests.ReadBody(resp) v.Set("value", "YYY") v.Set("prevIndex", "2") - resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + resp, _ = tests.PutForm(fullURL, v) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["action"], "compareAndSwap", "") node := body["node"].(map[string]interface{}) @@ -203,11 +206,12 @@ func TestV2SetKeyCASOnIndexFail(t *testing.T) { tests.RunServer(func(s *server.Server) { v := url.Values{} v.Set("value", "XXX") - resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") + resp, _ := tests.PutForm(fullURL, v) tests.ReadBody(resp) v.Set("value", "YYY") v.Set("prevIndex", "10") - resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + resp, _ = tests.PutForm(fullURL, v) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 101, "") assert.Equal(t, body["message"], "Compare failed", "") @@ -242,11 +246,12 @@ func TestV2SetKeyCASOnValueSuccess(t *testing.T) { tests.RunServer(func(s *server.Server) { v := url.Values{} v.Set("value", "XXX") - resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") + resp, _ := tests.PutForm(fullURL, v) tests.ReadBody(resp) v.Set("value", "YYY") v.Set("prevValue", "XXX") - resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + resp, _ = tests.PutForm(fullURL, v) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["action"], "compareAndSwap", "") node := body["node"].(map[string]interface{}) @@ -264,11 +269,12 @@ func TestV2SetKeyCASOnValueFail(t *testing.T) { tests.RunServer(func(s *server.Server) { v := url.Values{} v.Set("value", "XXX") - resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") + resp, _ := tests.PutForm(fullURL, v) tests.ReadBody(resp) v.Set("value", "YYY") v.Set("prevValue", "AAA") - resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + resp, _ = tests.PutForm(fullURL, v) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 101, "") assert.Equal(t, body["message"], "Compare failed", "") From 3f85829e87a8769b6169bde947c7f8c6aa7690b9 Mon Sep 17 00:00:00 2001 From: Chris Shoemaker Date: Sat, 21 Dec 2013 14:21:44 -0500 Subject: [PATCH 3/4] fix(v2/tests): make comments and tests agree about what's being tested In cases where the comments were incorrect, this changes them to agree with the tests. In cases where the comments were correct, this extends the tests to cover the behavior described in the comment. --- server/v2/tests/delete_handler_test.go | 2 +- server/v2/tests/put_handler_test.go | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/server/v2/tests/delete_handler_test.go b/server/v2/tests/delete_handler_test.go index b6f7fa042..34021f846 100644 --- a/server/v2/tests/delete_handler_test.go +++ b/server/v2/tests/delete_handler_test.go @@ -31,7 +31,7 @@ func TestV2DeleteKey(t *testing.T) { // Ensures that an empty directory is deleted when dir is set. // // $ curl -X PUT localhost:4001/v2/keys/foo?dir=true -// $ curl -X PUT localhost:4001/v2/keys/foo ->fail +// $ curl -X DELETE localhost:4001/v2/keys/foo ->fail // $ curl -X DELETE localhost:4001/v2/keys/foo?dir=true // func TestV2DeleteEmptyDirectory(t *testing.T) { diff --git a/server/v2/tests/put_handler_test.go b/server/v2/tests/put_handler_test.go index 7ece21aac..20eddec31 100644 --- a/server/v2/tests/put_handler_test.go +++ b/server/v2/tests/put_handler_test.go @@ -77,7 +77,7 @@ func TestV2SetKeyWithBadTTL(t *testing.T) { }) } -// Ensures that a key is conditionally set only if it previously did not exist. +// Ensures that a key is conditionally set if it previously did not exist. // // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=false // @@ -93,10 +93,10 @@ func TestV2CreateKeySuccess(t *testing.T) { }) } -// Ensures that a key is not conditionally because it previously existed. +// Ensures that a key is not conditionally set because it previously existed. // -// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=false +// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=false -> fail // func TestV2CreateKeyFail(t *testing.T) { tests.RunServer(func(s *server.Server) { @@ -158,19 +158,25 @@ func TestV2UpdateKeyFailOnValue(t *testing.T) { // Ensures that a key is not conditionally set if it previously did not exist. // -// $ curl -X PUT localhost:4001/v2/keys/foo -d value=XXX -d prevExist=true -// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=true +// $ curl -X PUT localhost:4001/v2/keys/foo -d value=YYY -d prevExist=true -> fail +// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevExist=true -> fail // func TestV2UpdateKeyFailOnMissingDirectory(t *testing.T) { tests.RunServer(func(s *server.Server) { v := url.Values{} v.Set("value", "YYY") v.Set("prevExist", "true") - resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo"), v) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 100, "") assert.Equal(t, body["message"], "Key not found", "") assert.Equal(t, body["cause"], "/foo", "") + + resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + body = tests.ReadBodyJSON(resp) + assert.Equal(t, body["errorCode"], 100, "") + assert.Equal(t, body["message"], "Key not found", "") + assert.Equal(t, body["cause"], "/foo", "") }) } From d89fa131aba73cee7eb3c9ba777c06ebdba589de Mon Sep 17 00:00:00 2001 From: Chris Shoemaker Date: Sat, 21 Dec 2013 13:14:30 -0500 Subject: [PATCH 4/4] feat(v2/errors): Use more appropriate HTTP status codes for error cases. This commits adds test coverage for all the error and non-error cases described below, but only the behavior of the 403, 404 and 412 cases are changing in this commit. When setting a key results in a new resource, we asset an HTTP status code of 201 (aka "Created"). When attempting to get a resource that doesn't exist, we assert an HTTP status code of 404 (aka "Not Found"). When attempting to delete a directory without dir=true, or a non-empty directory without recursive=true, but the request is otherwise valid, we assert an HTTP status code of 403 (aka "Forbidden"). When a precondition (e.g. specified by prevIndex, or prevValue) is not met, but the request is otherwise syntactically valid, we assert an HTTP status code of 412 (aka "Precondition Failed"). However, prevExist is handled slightly differently. If prevExist=false fails, then this is treated like a failed precondition, so it should use PreconditionFailed. But, if prevExist=true fails, then this is treated like other requests that require the existence of the resource, and uses NotFound if the resource doesn't exist. We continue to assert an HTTP status code of 400 when the request is syntactically invalid (e.g. when prevIndex=bad_index). --- error/error.go | 17 +++++++++++++---- server/v2/tests/delete_handler_test.go | 7 +++++++ server/v2/tests/get_handler_test.go | 10 +++++++++- server/v2/tests/post_handler_test.go | 4 ++++ server/v2/tests/put_handler_test.go | 24 ++++++++++++++++++++++++ tests/functional/v1_migration_test.go | 3 ++- 6 files changed, 59 insertions(+), 6 deletions(-) diff --git a/error/error.go b/error/error.go index f7372eba2..dd06067ff 100644 --- a/error/error.go +++ b/error/error.go @@ -112,9 +112,18 @@ func (e Error) toJsonString() string { func (e Error) Write(w http.ResponseWriter) { w.Header().Add("X-Etcd-Index", fmt.Sprint(e.Index)) // 3xx is raft internal error - if e.ErrorCode/100 == 3 { - http.Error(w, e.toJsonString(), http.StatusInternalServerError) - } else { - http.Error(w, e.toJsonString(), http.StatusBadRequest) + status := http.StatusBadRequest + switch e.ErrorCode { + case EcodeKeyNotFound: + status = http.StatusNotFound + case EcodeNotFile, EcodeDirNotEmpty: + status = http.StatusForbidden + case EcodeTestFailed, EcodeNodeExist: + status = http.StatusPreconditionFailed + default: + if e.ErrorCode/100 == 3 { + status = http.StatusInternalServerError + } } + http.Error(w, e.toJsonString(), status) } diff --git a/server/v2/tests/delete_handler_test.go b/server/v2/tests/delete_handler_test.go index 34021f846..335564f0a 100644 --- a/server/v2/tests/delete_handler_test.go +++ b/server/v2/tests/delete_handler_test.go @@ -2,6 +2,7 @@ package v2 import ( "fmt" + "net/http" "net/url" "testing" @@ -22,6 +23,7 @@ func TestV2DeleteKey(t *testing.T) { resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) tests.ReadBody(resp) resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), url.Values{}) + assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBody(resp) assert.Nil(t, err, "") assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo/bar","modifiedIndex":3,"createdIndex":2}}`, "") @@ -39,9 +41,11 @@ func TestV2DeleteEmptyDirectory(t *testing.T) { resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{}) tests.ReadBody(resp) resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo"), url.Values{}) + assert.Equal(t, resp.StatusCode, http.StatusForbidden) bodyJson := tests.ReadBodyJSON(resp) assert.Equal(t, bodyJson["errorCode"], 102, "") resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{}) + assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBody(resp) assert.Nil(t, err, "") assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2}}`, "") @@ -59,9 +63,11 @@ func TestV2DeleteNonEmptyDirectory(t *testing.T) { resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?dir=true"), url.Values{}) tests.ReadBody(resp) resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{}) + assert.Equal(t, resp.StatusCode, http.StatusForbidden) bodyJson := tests.ReadBodyJSON(resp) assert.Equal(t, bodyJson["errorCode"], 108, "") resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true&recursive=true"), url.Values{}) + assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBody(resp) assert.Nil(t, err, "") assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2}}`, "") @@ -78,6 +84,7 @@ func TestV2DeleteDirectoryRecursiveImpliesDir(t *testing.T) { resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{}) tests.ReadBody(resp) resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?recursive=true"), url.Values{}) + assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBody(resp) assert.Nil(t, err, "") assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2}}`, "") diff --git a/server/v2/tests/get_handler_test.go b/server/v2/tests/get_handler_test.go index 803fe4377..97feaa314 100644 --- a/server/v2/tests/get_handler_test.go +++ b/server/v2/tests/get_handler_test.go @@ -2,6 +2,7 @@ package v2 import ( "fmt" + "net/http" "net/url" "testing" "time" @@ -13,6 +14,7 @@ import ( // Ensures that a value can be retrieve for a given key. // +// $ curl localhost:4001/v2/keys/foo/bar -> fail // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX // $ curl localhost:4001/v2/keys/foo/bar // @@ -21,9 +23,14 @@ func TestV2GetKey(t *testing.T) { v := url.Values{} v.Set("value", "XXX") fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") - resp, _ := tests.PutForm(fullURL, v) + resp, _ := tests.Get(fullURL) + assert.Equal(t, resp.StatusCode, http.StatusNotFound) + + resp, _ = tests.PutForm(fullURL, v) tests.ReadBody(resp) + resp, _ = tests.Get(fullURL) + assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["action"], "get", "") node := body["node"].(map[string]interface{}) @@ -52,6 +59,7 @@ func TestV2GetKeyRecursively(t *testing.T) { tests.ReadBody(resp) resp, _ = tests.Get(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?recursive=true")) + assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["action"], "get", "") node := body["node"].(map[string]interface{}) diff --git a/server/v2/tests/post_handler_test.go b/server/v2/tests/post_handler_test.go index bd6c900b7..92acfc90a 100644 --- a/server/v2/tests/post_handler_test.go +++ b/server/v2/tests/post_handler_test.go @@ -3,6 +3,7 @@ package v2 import ( "fmt" "testing" + "net/http" "github.com/coreos/etcd/server" "github.com/coreos/etcd/tests" @@ -20,6 +21,7 @@ func TestV2CreateUnique(t *testing.T) { // POST should add index to list. fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") resp, _ := tests.PostForm(fullURL, nil) + assert.Equal(t, resp.StatusCode, http.StatusCreated) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["action"], "create", "") @@ -30,6 +32,7 @@ func TestV2CreateUnique(t *testing.T) { // Second POST should add next index to list. resp, _ = tests.PostForm(fullURL, nil) + assert.Equal(t, resp.StatusCode, http.StatusCreated) body = tests.ReadBodyJSON(resp) node = body["node"].(map[string]interface{}) @@ -37,6 +40,7 @@ func TestV2CreateUnique(t *testing.T) { // POST to a different key should add index to that list. resp, _ = tests.PostForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/baz"), nil) + assert.Equal(t, resp.StatusCode, http.StatusCreated) body = tests.ReadBodyJSON(resp) node = body["node"].(map[string]interface{}) diff --git a/server/v2/tests/put_handler_test.go b/server/v2/tests/put_handler_test.go index 20eddec31..f507c0d3d 100644 --- a/server/v2/tests/put_handler_test.go +++ b/server/v2/tests/put_handler_test.go @@ -2,6 +2,7 @@ package v2 import ( "fmt" + "net/http" "net/url" "testing" "time" @@ -20,6 +21,7 @@ func TestV2SetKey(t *testing.T) { v := url.Values{} v.Set("value", "XXX") resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + assert.Equal(t, resp.StatusCode, http.StatusCreated) body := tests.ReadBody(resp) assert.Nil(t, err, "") assert.Equal(t, string(body), `{"action":"set","node":{"key":"/foo/bar","value":"XXX","modifiedIndex":2,"createdIndex":2}}`, "") @@ -33,6 +35,7 @@ func TestV2SetKey(t *testing.T) { func TestV2SetDirectory(t *testing.T) { tests.RunServer(func(s *server.Server) { resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{}) + assert.Equal(t, resp.StatusCode, http.StatusCreated) body := tests.ReadBody(resp) assert.Nil(t, err, "") assert.Equal(t, string(body), `{"action":"set","node":{"key":"/foo","dir":true,"modifiedIndex":2,"createdIndex":2}}`, "") @@ -50,6 +53,7 @@ func TestV2SetKeyWithTTL(t *testing.T) { v.Set("value", "XXX") v.Set("ttl", "20") resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + assert.Equal(t, resp.StatusCode, http.StatusCreated) body := tests.ReadBodyJSON(resp) node := body["node"].(map[string]interface{}) assert.Equal(t, node["ttl"], 20, "") @@ -70,6 +74,7 @@ func TestV2SetKeyWithBadTTL(t *testing.T) { v.Set("value", "XXX") v.Set("ttl", "bad_ttl") resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + assert.Equal(t, resp.StatusCode, http.StatusBadRequest) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 202, "") assert.Equal(t, body["message"], "The given TTL in POST form is not a number", "") @@ -87,6 +92,7 @@ func TestV2CreateKeySuccess(t *testing.T) { v.Set("value", "XXX") v.Set("prevExist", "false") resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + assert.Equal(t, resp.StatusCode, http.StatusCreated) body := tests.ReadBodyJSON(resp) node := body["node"].(map[string]interface{}) assert.Equal(t, node["value"], "XXX", "") @@ -105,8 +111,10 @@ func TestV2CreateKeyFail(t *testing.T) { v.Set("prevExist", "false") fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") resp, _ := tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusCreated) tests.ReadBody(resp) resp, _ = tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusPreconditionFailed) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 105, "") assert.Equal(t, body["message"], "Key already exists", "") @@ -126,11 +134,13 @@ func TestV2UpdateKeySuccess(t *testing.T) { v.Set("value", "XXX") fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") resp, _ := tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusCreated) tests.ReadBody(resp) v.Set("value", "YYY") v.Set("prevExist", "true") resp, _ = tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["action"], "update", "") }) @@ -146,9 +156,11 @@ func TestV2UpdateKeyFailOnValue(t *testing.T) { v := url.Values{} resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), v) + assert.Equal(t, resp.StatusCode, http.StatusCreated) v.Set("value", "YYY") v.Set("prevExist", "true") resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + assert.Equal(t, resp.StatusCode, http.StatusNotFound) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 100, "") assert.Equal(t, body["message"], "Key not found", "") @@ -167,12 +179,14 @@ func TestV2UpdateKeyFailOnMissingDirectory(t *testing.T) { v.Set("value", "YYY") v.Set("prevExist", "true") resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo"), v) + assert.Equal(t, resp.StatusCode, http.StatusNotFound) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 100, "") assert.Equal(t, body["message"], "Key not found", "") assert.Equal(t, body["cause"], "/foo", "") resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + assert.Equal(t, resp.StatusCode, http.StatusNotFound) body = tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 100, "") assert.Equal(t, body["message"], "Key not found", "") @@ -191,10 +205,12 @@ func TestV2SetKeyCASOnIndexSuccess(t *testing.T) { v.Set("value", "XXX") fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") resp, _ := tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusCreated) tests.ReadBody(resp) v.Set("value", "YYY") v.Set("prevIndex", "2") resp, _ = tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["action"], "compareAndSwap", "") node := body["node"].(map[string]interface{}) @@ -214,10 +230,12 @@ func TestV2SetKeyCASOnIndexFail(t *testing.T) { v.Set("value", "XXX") fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") resp, _ := tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusCreated) tests.ReadBody(resp) v.Set("value", "YYY") v.Set("prevIndex", "10") resp, _ = tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusPreconditionFailed) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 101, "") assert.Equal(t, body["message"], "Compare failed", "") @@ -236,6 +254,7 @@ func TestV2SetKeyCASWithInvalidIndex(t *testing.T) { v.Set("value", "YYY") v.Set("prevIndex", "bad_index") resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + assert.Equal(t, resp.StatusCode, http.StatusBadRequest) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 203, "") assert.Equal(t, body["message"], "The given index in POST form is not a number", "") @@ -254,10 +273,12 @@ func TestV2SetKeyCASOnValueSuccess(t *testing.T) { v.Set("value", "XXX") fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") resp, _ := tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusCreated) tests.ReadBody(resp) v.Set("value", "YYY") v.Set("prevValue", "XXX") resp, _ = tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusOK) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["action"], "compareAndSwap", "") node := body["node"].(map[string]interface{}) @@ -277,10 +298,12 @@ func TestV2SetKeyCASOnValueFail(t *testing.T) { v.Set("value", "XXX") fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") resp, _ := tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusCreated) tests.ReadBody(resp) v.Set("value", "YYY") v.Set("prevValue", "AAA") resp, _ = tests.PutForm(fullURL, v) + assert.Equal(t, resp.StatusCode, http.StatusPreconditionFailed) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 101, "") assert.Equal(t, body["message"], "Compare failed", "") @@ -299,6 +322,7 @@ func TestV2SetKeyCASWithMissingValueFails(t *testing.T) { v.Set("value", "XXX") v.Set("prevValue", "") resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) + assert.Equal(t, resp.StatusCode, http.StatusBadRequest) body := tests.ReadBodyJSON(resp) assert.Equal(t, body["errorCode"], 201, "") assert.Equal(t, body["message"], "PrevValue is Required in POST form", "") diff --git a/tests/functional/v1_migration_test.go b/tests/functional/v1_migration_test.go index c8906a915..3a0512101 100644 --- a/tests/functional/v1_migration_test.go +++ b/tests/functional/v1_migration_test.go @@ -3,6 +3,7 @@ package test import ( "fmt" "io/ioutil" + "net/http" "os" "os/exec" "path/filepath" @@ -91,7 +92,7 @@ func TestV1ClusterMigration(t *testing.T) { resp, err := tests.Get("http://localhost:4001/v2/keys/message") body := tests.ReadBody(resp) assert.Nil(t, err, "") - assert.Equal(t, resp.StatusCode, 400) + assert.Equal(t, resp.StatusCode, http.StatusNotFound) assert.Equal(t, string(body), `{"errorCode":100,"message":"Key not found","cause":"/message","index":11}`+"\n") // Ensure TTL'd message is removed.