github.com/ghodss/etcd@v0.3.1-0.20140417172404-cc329bfa55cb/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 could update TTL. 198 // 199 // $ curl -X PUT localhost:4001/v2/keys/foo -d value=XXX 200 // $ curl -X PUT localhost:4001/v2/keys/foo -d value=XXX -d ttl=1000 -d prevExist=true 201 // $ curl -X PUT localhost:4001/v2/keys/foo -d value=XXX -d ttl= -d prevExist=true 202 // 203 func TestV2UpdateKeySuccessWithTTL(t *testing.T) { 204 tests.RunServer(func(s *server.Server) { 205 v := url.Values{} 206 v.Set("value", "XXX") 207 resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo"), v) 208 assert.Equal(t, resp.StatusCode, http.StatusCreated) 209 node := (tests.ReadBodyJSON(resp)["node"]).(map[string]interface{}) 210 createdIndex := node["createdIndex"] 211 212 v.Set("ttl", "1000") 213 v.Set("prevExist", "true") 214 resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo"), v) 215 assert.Equal(t, resp.StatusCode, http.StatusOK) 216 node = (tests.ReadBodyJSON(resp)["node"]).(map[string]interface{}) 217 assert.Equal(t, node["value"], "XXX", "") 218 assert.Equal(t, node["ttl"], 1000, "") 219 assert.NotEqual(t, node["expiration"], "", "") 220 assert.Equal(t, node["createdIndex"], createdIndex, "") 221 222 v.Del("ttl") 223 resp, _ = tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo"), v) 224 assert.Equal(t, resp.StatusCode, http.StatusOK) 225 node = (tests.ReadBodyJSON(resp)["node"]).(map[string]interface{}) 226 assert.Equal(t, node["value"], "XXX", "") 227 assert.Equal(t, node["ttl"], nil, "") 228 assert.Equal(t, node["expiration"], nil, "") 229 assert.Equal(t, node["createdIndex"], createdIndex, "") 230 }) 231 } 232 233 // Ensures that a key is set only if the previous index matches. 234 // 235 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX 236 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=1 237 // 238 func TestV2SetKeyCASOnIndexSuccess(t *testing.T) { 239 tests.RunServer(func(s *server.Server) { 240 v := url.Values{} 241 v.Set("value", "XXX") 242 fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") 243 resp, _ := tests.PutForm(fullURL, v) 244 assert.Equal(t, resp.StatusCode, http.StatusCreated) 245 tests.ReadBody(resp) 246 v.Set("value", "YYY") 247 v.Set("prevIndex", "2") 248 resp, _ = tests.PutForm(fullURL, v) 249 assert.Equal(t, resp.StatusCode, http.StatusOK) 250 body := tests.ReadBodyJSON(resp) 251 assert.Equal(t, body["action"], "compareAndSwap", "") 252 node := body["node"].(map[string]interface{}) 253 assert.Equal(t, node["value"], "YYY", "") 254 assert.Equal(t, node["modifiedIndex"], 3, "") 255 }) 256 } 257 258 // Ensures that a key is not set if the previous index does not match. 259 // 260 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX 261 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=10 262 // 263 func TestV2SetKeyCASOnIndexFail(t *testing.T) { 264 tests.RunServer(func(s *server.Server) { 265 v := url.Values{} 266 v.Set("value", "XXX") 267 fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") 268 resp, _ := tests.PutForm(fullURL, v) 269 assert.Equal(t, resp.StatusCode, http.StatusCreated) 270 tests.ReadBody(resp) 271 v.Set("value", "YYY") 272 v.Set("prevIndex", "10") 273 resp, _ = tests.PutForm(fullURL, v) 274 assert.Equal(t, resp.StatusCode, http.StatusPreconditionFailed) 275 body := tests.ReadBodyJSON(resp) 276 assert.Equal(t, body["errorCode"], 101, "") 277 assert.Equal(t, body["message"], "Compare failed", "") 278 assert.Equal(t, body["cause"], "[10 != 2]", "") 279 assert.Equal(t, body["index"], 2, "") 280 }) 281 } 282 283 // Ensures that an error is thrown if an invalid previous index is provided. 284 // 285 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=bad_index 286 // 287 func TestV2SetKeyCASWithInvalidIndex(t *testing.T) { 288 tests.RunServer(func(s *server.Server) { 289 v := url.Values{} 290 v.Set("value", "YYY") 291 v.Set("prevIndex", "bad_index") 292 resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) 293 assert.Equal(t, resp.StatusCode, http.StatusBadRequest) 294 body := tests.ReadBodyJSON(resp) 295 assert.Equal(t, body["errorCode"], 203, "") 296 assert.Equal(t, body["message"], "The given index in POST form is not a number", "") 297 assert.Equal(t, body["cause"], "CompareAndSwap", "") 298 }) 299 } 300 301 // Ensures that a key is set only if the previous value matches. 302 // 303 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX 304 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=XXX 305 // 306 func TestV2SetKeyCASOnValueSuccess(t *testing.T) { 307 tests.RunServer(func(s *server.Server) { 308 v := url.Values{} 309 v.Set("value", "XXX") 310 fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") 311 resp, _ := tests.PutForm(fullURL, v) 312 assert.Equal(t, resp.StatusCode, http.StatusCreated) 313 tests.ReadBody(resp) 314 v.Set("value", "YYY") 315 v.Set("prevValue", "XXX") 316 resp, _ = tests.PutForm(fullURL, v) 317 assert.Equal(t, resp.StatusCode, http.StatusOK) 318 body := tests.ReadBodyJSON(resp) 319 assert.Equal(t, body["action"], "compareAndSwap", "") 320 node := body["node"].(map[string]interface{}) 321 assert.Equal(t, node["value"], "YYY", "") 322 assert.Equal(t, node["modifiedIndex"], 3, "") 323 }) 324 } 325 326 // Ensures that a key is not set if the previous value does not match. 327 // 328 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX 329 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=AAA 330 // 331 func TestV2SetKeyCASOnValueFail(t *testing.T) { 332 tests.RunServer(func(s *server.Server) { 333 v := url.Values{} 334 v.Set("value", "XXX") 335 fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") 336 resp, _ := tests.PutForm(fullURL, v) 337 assert.Equal(t, resp.StatusCode, http.StatusCreated) 338 tests.ReadBody(resp) 339 v.Set("value", "YYY") 340 v.Set("prevValue", "AAA") 341 resp, _ = tests.PutForm(fullURL, v) 342 assert.Equal(t, resp.StatusCode, http.StatusPreconditionFailed) 343 body := tests.ReadBodyJSON(resp) 344 assert.Equal(t, body["errorCode"], 101, "") 345 assert.Equal(t, body["message"], "Compare failed", "") 346 assert.Equal(t, body["cause"], "[AAA != XXX]", "") 347 assert.Equal(t, body["index"], 2, "") 348 }) 349 } 350 351 // Ensures that an error is returned if a blank prevValue is set. 352 // 353 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevValue= 354 // 355 func TestV2SetKeyCASWithMissingValueFails(t *testing.T) { 356 tests.RunServer(func(s *server.Server) { 357 v := url.Values{} 358 v.Set("value", "XXX") 359 v.Set("prevValue", "") 360 resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) 361 assert.Equal(t, resp.StatusCode, http.StatusBadRequest) 362 body := tests.ReadBodyJSON(resp) 363 assert.Equal(t, body["errorCode"], 201, "") 364 assert.Equal(t, body["message"], "PrevValue is Required in POST form", "") 365 assert.Equal(t, body["cause"], "CompareAndSwap", "") 366 }) 367 } 368 369 // Ensures that a key is not set if both previous value and index do not match. 370 // 371 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX 372 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=AAA -d prevIndex=3 373 // 374 func TestV2SetKeyCASOnValueAndIndexFail(t *testing.T) { 375 tests.RunServer(func(s *server.Server) { 376 v := url.Values{} 377 v.Set("value", "XXX") 378 fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") 379 resp, _ := tests.PutForm(fullURL, v) 380 assert.Equal(t, resp.StatusCode, http.StatusCreated) 381 tests.ReadBody(resp) 382 v.Set("value", "YYY") 383 v.Set("prevValue", "AAA") 384 v.Set("prevIndex", "3") 385 resp, _ = tests.PutForm(fullURL, v) 386 assert.Equal(t, resp.StatusCode, http.StatusPreconditionFailed) 387 body := tests.ReadBodyJSON(resp) 388 assert.Equal(t, body["errorCode"], 101, "") 389 assert.Equal(t, body["message"], "Compare failed", "") 390 assert.Equal(t, body["cause"], "[AAA != XXX] [3 != 2]", "") 391 assert.Equal(t, body["index"], 2, "") 392 }) 393 } 394 395 // Ensures that a key is not set if previous value match but index does not. 396 // 397 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX 398 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=XXX -d prevIndex=3 399 // 400 func TestV2SetKeyCASOnValueMatchAndIndexFail(t *testing.T) { 401 tests.RunServer(func(s *server.Server) { 402 v := url.Values{} 403 v.Set("value", "XXX") 404 fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") 405 resp, _ := tests.PutForm(fullURL, v) 406 assert.Equal(t, resp.StatusCode, http.StatusCreated) 407 tests.ReadBody(resp) 408 v.Set("value", "YYY") 409 v.Set("prevValue", "XXX") 410 v.Set("prevIndex", "3") 411 resp, _ = tests.PutForm(fullURL, v) 412 assert.Equal(t, resp.StatusCode, http.StatusPreconditionFailed) 413 body := tests.ReadBodyJSON(resp) 414 assert.Equal(t, body["errorCode"], 101, "") 415 assert.Equal(t, body["message"], "Compare failed", "") 416 assert.Equal(t, body["cause"], "[3 != 2]", "") 417 assert.Equal(t, body["index"], 2, "") 418 }) 419 } 420 421 // Ensures that a key is not set if previous index matches but value does not. 422 // 423 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX 424 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=AAA -d prevIndex=2 425 // 426 func TestV2SetKeyCASOnIndexMatchAndValueFail(t *testing.T) { 427 tests.RunServer(func(s *server.Server) { 428 v := url.Values{} 429 v.Set("value", "XXX") 430 fullURL := fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar") 431 resp, _ := tests.PutForm(fullURL, v) 432 assert.Equal(t, resp.StatusCode, http.StatusCreated) 433 tests.ReadBody(resp) 434 v.Set("value", "YYY") 435 v.Set("prevValue", "AAA") 436 v.Set("prevIndex", "2") 437 resp, _ = tests.PutForm(fullURL, v) 438 assert.Equal(t, resp.StatusCode, http.StatusPreconditionFailed) 439 body := tests.ReadBodyJSON(resp) 440 assert.Equal(t, body["errorCode"], 101, "") 441 assert.Equal(t, body["message"], "Compare failed", "") 442 assert.Equal(t, body["cause"], "[AAA != XXX]", "") 443 assert.Equal(t, body["index"], 2, "") 444 }) 445 } 446 447 // Ensure that we can set an empty value 448 // 449 // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value= 450 // 451 func TestV2SetKeyCASWithEmptyValueSuccess(t *testing.T) { 452 tests.RunServer(func(s *server.Server) { 453 v := url.Values{} 454 v.Set("value", "") 455 resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v) 456 assert.Equal(t, resp.StatusCode, http.StatusCreated) 457 body := tests.ReadBody(resp) 458 assert.Equal(t, string(body), `{"action":"set","node":{"key":"/foo/bar","value":"","modifiedIndex":2,"createdIndex":2}}`) 459 }) 460 }