put_handler_test.go 12 KB

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