mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00

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).
233 lines
6.7 KiB
Go
233 lines
6.7 KiB
Go
package v2
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/coreos/etcd/server"
|
|
"github.com/coreos/etcd/tests"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// 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
|
|
//
|
|
func TestV2GetKey(t *testing.T) {
|
|
tests.RunServer(func(s *server.Server) {
|
|
v := url.Values{}
|
|
v.Set("value", "XXX")
|
|
fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar")
|
|
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{})
|
|
assert.Equal(t, node["key"], "/foo/bar", "")
|
|
assert.Equal(t, node["value"], "XXX", "")
|
|
assert.Equal(t, node["modifiedIndex"], 2, "")
|
|
})
|
|
}
|
|
|
|
// Ensures that a directory of values can be recursively retrieved for a given key.
|
|
//
|
|
// $ curl -X PUT localhost:4001/v2/keys/foo/x -d value=XXX
|
|
// $ curl -X PUT localhost:4001/v2/keys/foo/y/z -d value=YYY
|
|
// $ curl localhost:4001/v2/keys/foo -d recursive=true
|
|
//
|
|
func TestV2GetKeyRecursively(t *testing.T) {
|
|
tests.RunServer(func(s *server.Server) {
|
|
v := url.Values{}
|
|
v.Set("value", "XXX")
|
|
v.Set("ttl", "10")
|
|
resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/x"), v)
|
|
tests.ReadBody(resp)
|
|
|
|
v.Set("value", "YYY")
|
|
resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/y/z"), v)
|
|
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{})
|
|
assert.Equal(t, node["key"], "/foo", "")
|
|
assert.Equal(t, node["dir"], true, "")
|
|
assert.Equal(t, node["modifiedIndex"], 2, "")
|
|
assert.Equal(t, len(node["nodes"].([]interface{})), 2, "")
|
|
|
|
node0 := node["nodes"].([]interface{})[0].(map[string]interface{})
|
|
assert.Equal(t, node0["key"], "/foo/x", "")
|
|
assert.Equal(t, node0["value"], "XXX", "")
|
|
assert.Equal(t, node0["ttl"], 10, "")
|
|
|
|
node1 := node["nodes"].([]interface{})[1].(map[string]interface{})
|
|
assert.Equal(t, node1["key"], "/foo/y", "")
|
|
assert.Equal(t, node1["dir"], true, "")
|
|
|
|
node2 := node1["nodes"].([]interface{})[0].(map[string]interface{})
|
|
assert.Equal(t, node2["key"], "/foo/y/z", "")
|
|
assert.Equal(t, node2["value"], "YYY", "")
|
|
})
|
|
}
|
|
|
|
// Ensures that a watcher can wait for a value to be set and return it to the client.
|
|
//
|
|
// $ curl localhost:4001/v2/keys/foo/bar?wait=true
|
|
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
|
//
|
|
func TestV2WatchKey(t *testing.T) {
|
|
tests.RunServer(func(s *server.Server) {
|
|
var body map[string]interface{}
|
|
c := make(chan bool)
|
|
go func() {
|
|
resp, _ := tests.Get(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?wait=true"))
|
|
body = tests.ReadBodyJSON(resp)
|
|
c <- true
|
|
}()
|
|
|
|
// Make sure response didn't fire early.
|
|
time.Sleep(1 * time.Millisecond)
|
|
assert.Nil(t, body, "")
|
|
|
|
// Set a value.
|
|
v := url.Values{}
|
|
v.Set("value", "XXX")
|
|
resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
|
tests.ReadBody(resp)
|
|
|
|
// A response should follow from the GET above.
|
|
time.Sleep(1 * time.Millisecond)
|
|
|
|
select {
|
|
case <-c:
|
|
|
|
default:
|
|
t.Fatal("cannot get watch result")
|
|
}
|
|
|
|
assert.NotNil(t, body, "")
|
|
assert.Equal(t, body["action"], "set", "")
|
|
|
|
node := body["node"].(map[string]interface{})
|
|
assert.Equal(t, node["key"], "/foo/bar", "")
|
|
assert.Equal(t, node["value"], "XXX", "")
|
|
assert.Equal(t, node["modifiedIndex"], 2, "")
|
|
})
|
|
}
|
|
|
|
// Ensures that a watcher can wait for a value to be set after a given index.
|
|
//
|
|
// $ curl localhost:4001/v2/keys/foo/bar?wait=true&waitIndex=4
|
|
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
|
|
// $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY
|
|
//
|
|
func TestV2WatchKeyWithIndex(t *testing.T) {
|
|
tests.RunServer(func(s *server.Server) {
|
|
var body map[string]interface{}
|
|
c := make(chan bool)
|
|
go func() {
|
|
resp, _ := tests.Get(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?wait=true&waitIndex=3"))
|
|
body = tests.ReadBodyJSON(resp)
|
|
c <- true
|
|
}()
|
|
|
|
// Make sure response didn't fire early.
|
|
time.Sleep(1 * time.Millisecond)
|
|
assert.Nil(t, body, "")
|
|
|
|
// Set a value (before given index).
|
|
v := url.Values{}
|
|
v.Set("value", "XXX")
|
|
resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
|
tests.ReadBody(resp)
|
|
|
|
// Make sure response didn't fire early.
|
|
time.Sleep(1 * time.Millisecond)
|
|
assert.Nil(t, body, "")
|
|
|
|
// Set a value (before given index).
|
|
v.Set("value", "YYY")
|
|
resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
|
|
tests.ReadBody(resp)
|
|
|
|
// A response should follow from the GET above.
|
|
time.Sleep(1 * time.Millisecond)
|
|
|
|
select {
|
|
case <-c:
|
|
|
|
default:
|
|
t.Fatal("cannot get watch result")
|
|
}
|
|
|
|
assert.NotNil(t, body, "")
|
|
assert.Equal(t, body["action"], "set", "")
|
|
|
|
node := body["node"].(map[string]interface{})
|
|
assert.Equal(t, node["key"], "/foo/bar", "")
|
|
assert.Equal(t, node["value"], "YYY", "")
|
|
assert.Equal(t, node["modifiedIndex"], 3, "")
|
|
})
|
|
}
|
|
|
|
// Ensures that a watcher can wait for a value to be set after a given index.
|
|
//
|
|
// $ curl localhost:4001/v2/keys/keyindir/bar?wait=true
|
|
// $ curl -X PUT localhost:4001/v2/keys/keyindir -d dir=true -d ttl=1
|
|
// $ curl -X PUT localhost:4001/v2/keys/keyindir/bar -d value=YYY
|
|
//
|
|
func TestV2WatchKeyInDir(t *testing.T) {
|
|
tests.RunServer(func(s *server.Server) {
|
|
var body map[string]interface{}
|
|
c := make(chan bool)
|
|
|
|
// Set a value (before given index).
|
|
v := url.Values{}
|
|
v.Set("dir", "true")
|
|
v.Set("ttl", "1")
|
|
resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/keyindir"), v)
|
|
tests.ReadBody(resp)
|
|
|
|
// Set a value (before given index).
|
|
v = url.Values{}
|
|
v.Set("value", "XXX")
|
|
resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/keyindir/bar"), v)
|
|
tests.ReadBody(resp)
|
|
|
|
go func() {
|
|
resp, _ := tests.Get(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/keyindir/bar?wait=true"))
|
|
body = tests.ReadBodyJSON(resp)
|
|
c <- true
|
|
}()
|
|
|
|
// wait for expiration, we do have a up to 500 millisecond delay
|
|
time.Sleep(1500 * time.Millisecond)
|
|
|
|
select {
|
|
case <-c:
|
|
|
|
default:
|
|
t.Fatal("cannot get watch result")
|
|
}
|
|
|
|
assert.NotNil(t, body, "")
|
|
assert.Equal(t, body["action"], "expire", "")
|
|
|
|
node := body["node"].(map[string]interface{})
|
|
assert.Equal(t, node["key"], "/keyindir", "")
|
|
})
|
|
}
|