put_handler_test.go 9.7 KB

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