github.com/macb/etcd@v0.3.1-0.20140227003422-a60481c6b1a0/server/v2/tests/put_handler_test.go (about)

     1  package v2
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/url"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/coreos/etcd/server"
    11  	"github.com/coreos/etcd/tests"
    12  	"github.com/coreos/etcd/third_party/github.com/stretchr/testify/assert"
    13  )
    14  
    15  // Ensures that a key is set to a given value.
    16  //
    17  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
    18  //
    19  func TestV2SetKey(t *testing.T) {
    20  	tests.RunServer(func(s *server.Server) {
    21  		v := url.Values{}
    22  		v.Set("value", "XXX")
    23  		resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
    24  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
    25  		body := tests.ReadBody(resp)
    26  		assert.Nil(t, err, "")
    27  		assert.Equal(t, string(body), `{"action":"set","node":{"key":"/foo/bar","value":"XXX","modifiedIndex":2,"createdIndex":2}}`, "")
    28  	})
    29  }
    30  
    31  // Ensures that a directory is created
    32  //
    33  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar?dir=true
    34  //
    35  func TestV2SetDirectory(t *testing.T) {
    36  	tests.RunServer(func(s *server.Server) {
    37  		resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{})
    38  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
    39  		body := tests.ReadBody(resp)
    40  		assert.Nil(t, err, "")
    41  		assert.Equal(t, string(body), `{"action":"set","node":{"key":"/foo","dir":true,"modifiedIndex":2,"createdIndex":2}}`, "")
    42  	})
    43  }
    44  
    45  // Ensures that a time-to-live is added to a key.
    46  //
    47  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d ttl=20
    48  //
    49  func TestV2SetKeyWithTTL(t *testing.T) {
    50  	tests.RunServer(func(s *server.Server) {
    51  		t0 := time.Now()
    52  		v := url.Values{}
    53  		v.Set("value", "XXX")
    54  		v.Set("ttl", "20")
    55  		resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
    56  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
    57  		body := tests.ReadBodyJSON(resp)
    58  		node := body["node"].(map[string]interface{})
    59  		assert.Equal(t, node["ttl"], 20, "")
    60  
    61  		// Make sure the expiration date is correct.
    62  		expiration, _ := time.Parse(time.RFC3339Nano, node["expiration"].(string))
    63  		assert.Equal(t, expiration.Sub(t0)/time.Second, 20, "")
    64  	})
    65  }
    66  
    67  // Ensures that an invalid time-to-live is returned as an error.
    68  //
    69  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d ttl=bad_ttl
    70  //
    71  func TestV2SetKeyWithBadTTL(t *testing.T) {
    72  	tests.RunServer(func(s *server.Server) {
    73  		v := url.Values{}
    74  		v.Set("value", "XXX")
    75  		v.Set("ttl", "bad_ttl")
    76  		resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
    77  		assert.Equal(t, resp.StatusCode, http.StatusBadRequest)
    78  		body := tests.ReadBodyJSON(resp)
    79  		assert.Equal(t, body["errorCode"], 202, "")
    80  		assert.Equal(t, body["message"], "The given TTL in POST form is not a number", "")
    81  		assert.Equal(t, body["cause"], "Update", "")
    82  	})
    83  }
    84  
    85  // Ensures that a key is conditionally set if it previously did not exist.
    86  //
    87  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=false
    88  //
    89  func TestV2CreateKeySuccess(t *testing.T) {
    90  	tests.RunServer(func(s *server.Server) {
    91  		v := url.Values{}
    92  		v.Set("value", "XXX")
    93  		v.Set("prevExist", "false")
    94  		resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
    95  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
    96  		body := tests.ReadBodyJSON(resp)
    97  		node := body["node"].(map[string]interface{})
    98  		assert.Equal(t, node["value"], "XXX", "")
    99  	})
   100  }
   101  
   102  // Ensures that a key is not conditionally set because it previously existed.
   103  //
   104  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=false
   105  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=false -> fail
   106  //
   107  func TestV2CreateKeyFail(t *testing.T) {
   108  	tests.RunServer(func(s *server.Server) {
   109  		v := url.Values{}
   110  		v.Set("value", "XXX")
   111  		v.Set("prevExist", "false")
   112  		fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar")
   113  		resp, _ := tests.PutForm(fullURL, v)
   114  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   115  		tests.ReadBody(resp)
   116  		resp, _ = tests.PutForm(fullURL, v)
   117  		assert.Equal(t, resp.StatusCode, http.StatusPreconditionFailed)
   118  		body := tests.ReadBodyJSON(resp)
   119  		assert.Equal(t, body["errorCode"], 105, "")
   120  		assert.Equal(t, body["message"], "Key already exists", "")
   121  		assert.Equal(t, body["cause"], "/foo/bar", "")
   122  	})
   123  }
   124  
   125  // Ensures that a key is conditionally set only if it previously did exist.
   126  //
   127  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
   128  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevExist=true
   129  //
   130  func TestV2UpdateKeySuccess(t *testing.T) {
   131  	tests.RunServer(func(s *server.Server) {
   132  		v := url.Values{}
   133  
   134  		v.Set("value", "XXX")
   135  		fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar")
   136  		resp, _ := tests.PutForm(fullURL, v)
   137  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   138  		tests.ReadBody(resp)
   139  
   140  		v.Set("value", "YYY")
   141  		v.Set("prevExist", "true")
   142  		resp, _ = tests.PutForm(fullURL, v)
   143  		assert.Equal(t, resp.StatusCode, http.StatusOK)
   144  		body := tests.ReadBodyJSON(resp)
   145  		assert.Equal(t, body["action"], "update", "")
   146  	})
   147  }
   148  
   149  // Ensures that a key is not conditionally set if it previously did not exist.
   150  //
   151  //   $ curl -X PUT localhost:4001/v2/keys/foo?dir=true
   152  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=true
   153  //
   154  func TestV2UpdateKeyFailOnValue(t *testing.T) {
   155  	tests.RunServer(func(s *server.Server) {
   156  		v := url.Values{}
   157  		resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), v)
   158  
   159  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   160  		v.Set("value", "YYY")
   161  		v.Set("prevExist", "true")
   162  		resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
   163  		assert.Equal(t, resp.StatusCode, http.StatusNotFound)
   164  		body := tests.ReadBodyJSON(resp)
   165  		assert.Equal(t, body["errorCode"], 100, "")
   166  		assert.Equal(t, body["message"], "Key not found", "")
   167  		assert.Equal(t, body["cause"], "/foo/bar", "")
   168  	})
   169  }
   170  
   171  // Ensures that a key is not conditionally set if it previously did not exist.
   172  //
   173  //   $ curl -X PUT localhost:4001/v2/keys/foo -d value=YYY -d prevExist=true -> fail
   174  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevExist=true -> fail
   175  //
   176  func TestV2UpdateKeyFailOnMissingDirectory(t *testing.T) {
   177  	tests.RunServer(func(s *server.Server) {
   178  		v := url.Values{}
   179  		v.Set("value", "YYY")
   180  		v.Set("prevExist", "true")
   181  		resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo"), v)
   182  		assert.Equal(t, resp.StatusCode, http.StatusNotFound)
   183  		body := tests.ReadBodyJSON(resp)
   184  		assert.Equal(t, body["errorCode"], 100, "")
   185  		assert.Equal(t, body["message"], "Key not found", "")
   186  		assert.Equal(t, body["cause"], "/foo", "")
   187  
   188  		resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
   189  		assert.Equal(t, resp.StatusCode, http.StatusNotFound)
   190  		body = tests.ReadBodyJSON(resp)
   191  		assert.Equal(t, body["errorCode"], 100, "")
   192  		assert.Equal(t, body["message"], "Key not found", "")
   193  		assert.Equal(t, body["cause"], "/foo", "")
   194  	})
   195  }
   196  
   197  // Ensures that a key is set only if the previous index matches.
   198  //
   199  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
   200  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=1
   201  //
   202  func TestV2SetKeyCASOnIndexSuccess(t *testing.T) {
   203  	tests.RunServer(func(s *server.Server) {
   204  		v := url.Values{}
   205  		v.Set("value", "XXX")
   206  		fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar")
   207  		resp, _ := tests.PutForm(fullURL, v)
   208  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   209  		tests.ReadBody(resp)
   210  		v.Set("value", "YYY")
   211  		v.Set("prevIndex", "2")
   212  		resp, _ = tests.PutForm(fullURL, v)
   213  		assert.Equal(t, resp.StatusCode, http.StatusOK)
   214  		body := tests.ReadBodyJSON(resp)
   215  		assert.Equal(t, body["action"], "compareAndSwap", "")
   216  		node := body["node"].(map[string]interface{})
   217  		assert.Equal(t, node["value"], "YYY", "")
   218  		assert.Equal(t, node["modifiedIndex"], 3, "")
   219  	})
   220  }
   221  
   222  // Ensures that a key is not set if the previous index does not match.
   223  //
   224  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
   225  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=10
   226  //
   227  func TestV2SetKeyCASOnIndexFail(t *testing.T) {
   228  	tests.RunServer(func(s *server.Server) {
   229  		v := url.Values{}
   230  		v.Set("value", "XXX")
   231  		fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar")
   232  		resp, _ := tests.PutForm(fullURL, v)
   233  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   234  		tests.ReadBody(resp)
   235  		v.Set("value", "YYY")
   236  		v.Set("prevIndex", "10")
   237  		resp, _ = tests.PutForm(fullURL, v)
   238  		assert.Equal(t, resp.StatusCode, http.StatusPreconditionFailed)
   239  		body := tests.ReadBodyJSON(resp)
   240  		assert.Equal(t, body["errorCode"], 101, "")
   241  		assert.Equal(t, body["message"], "Compare failed", "")
   242  		assert.Equal(t, body["cause"], "[ != XXX] [10 != 2]", "")
   243  		assert.Equal(t, body["index"], 2, "")
   244  	})
   245  }
   246  
   247  // Ensures that an error is thrown if an invalid previous index is provided.
   248  //
   249  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=bad_index
   250  //
   251  func TestV2SetKeyCASWithInvalidIndex(t *testing.T) {
   252  	tests.RunServer(func(s *server.Server) {
   253  		v := url.Values{}
   254  		v.Set("value", "YYY")
   255  		v.Set("prevIndex", "bad_index")
   256  		resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
   257  		assert.Equal(t, resp.StatusCode, http.StatusBadRequest)
   258  		body := tests.ReadBodyJSON(resp)
   259  		assert.Equal(t, body["errorCode"], 203, "")
   260  		assert.Equal(t, body["message"], "The given index in POST form is not a number", "")
   261  		assert.Equal(t, body["cause"], "CompareAndSwap", "")
   262  	})
   263  }
   264  
   265  // Ensures that a key is set only if the previous value matches.
   266  //
   267  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
   268  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=XXX
   269  //
   270  func TestV2SetKeyCASOnValueSuccess(t *testing.T) {
   271  	tests.RunServer(func(s *server.Server) {
   272  		v := url.Values{}
   273  		v.Set("value", "XXX")
   274  		fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar")
   275  		resp, _ := tests.PutForm(fullURL, v)
   276  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   277  		tests.ReadBody(resp)
   278  		v.Set("value", "YYY")
   279  		v.Set("prevValue", "XXX")
   280  		resp, _ = tests.PutForm(fullURL, v)
   281  		assert.Equal(t, resp.StatusCode, http.StatusOK)
   282  		body := tests.ReadBodyJSON(resp)
   283  		assert.Equal(t, body["action"], "compareAndSwap", "")
   284  		node := body["node"].(map[string]interface{})
   285  		assert.Equal(t, node["value"], "YYY", "")
   286  		assert.Equal(t, node["modifiedIndex"], 3, "")
   287  	})
   288  }
   289  
   290  // Ensures that a key is not set if the previous value does not match.
   291  //
   292  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
   293  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=AAA
   294  //
   295  func TestV2SetKeyCASOnValueFail(t *testing.T) {
   296  	tests.RunServer(func(s *server.Server) {
   297  		v := url.Values{}
   298  		v.Set("value", "XXX")
   299  		fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar")
   300  		resp, _ := tests.PutForm(fullURL, v)
   301  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   302  		tests.ReadBody(resp)
   303  		v.Set("value", "YYY")
   304  		v.Set("prevValue", "AAA")
   305  		resp, _ = tests.PutForm(fullURL, v)
   306  		assert.Equal(t, resp.StatusCode, http.StatusPreconditionFailed)
   307  		body := tests.ReadBodyJSON(resp)
   308  		assert.Equal(t, body["errorCode"], 101, "")
   309  		assert.Equal(t, body["message"], "Compare failed", "")
   310  		assert.Equal(t, body["cause"], "[AAA != XXX] [0 != 2]", "")
   311  		assert.Equal(t, body["index"], 2, "")
   312  	})
   313  }
   314  
   315  // Ensures that an error is returned if a blank prevValue is set.
   316  //
   317  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevValue=
   318  //
   319  func TestV2SetKeyCASWithMissingValueFails(t *testing.T) {
   320  	tests.RunServer(func(s *server.Server) {
   321  		v := url.Values{}
   322  		v.Set("value", "XXX")
   323  		v.Set("prevValue", "")
   324  		resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
   325  		assert.Equal(t, resp.StatusCode, http.StatusBadRequest)
   326  		body := tests.ReadBodyJSON(resp)
   327  		assert.Equal(t, body["errorCode"], 201, "")
   328  		assert.Equal(t, body["message"], "PrevValue is Required in POST form", "")
   329  		assert.Equal(t, body["cause"], "CompareAndSwap", "")
   330  	})
   331  }
   332  
   333  // Ensure that we can set an empty value
   334  //
   335  //   $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=
   336  //
   337  func TestV2SetKeyCASWithEmptyValueSuccess(t *testing.T) {
   338  	tests.RunServer(func(s *server.Server) {
   339  		v := url.Values{}
   340  		v.Set("value", "")
   341  		resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
   342  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   343  		body := tests.ReadBody(resp)
   344  		assert.Equal(t, string(body), `{"action":"set","node":{"key":"/foo/bar","value":"","modifiedIndex":2,"createdIndex":2}}`)
   345  	})
   346  }