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 }