delete_handler_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. package v2
  2. import (
  3. "fmt"
  4. "net/http"
  5. "net/url"
  6. "testing"
  7. "github.com/coreos/etcd/server"
  8. "github.com/coreos/etcd/tests"
  9. "github.com/coreos/etcd/third_party/github.com/stretchr/testify/assert"
  10. )
  11. // Ensures that a key is deleted.
  12. //
  13. // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
  14. // $ curl -X DELETE localhost:4001/v2/keys/foo/bar
  15. //
  16. func TestV2DeleteKey(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. tests.ReadBody(resp)
  22. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), url.Values{})
  23. assert.Equal(t, resp.StatusCode, http.StatusOK)
  24. body := tests.ReadBody(resp)
  25. assert.Nil(t, err, "")
  26. assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo/bar","modifiedIndex":3,"createdIndex":2},"prevNode":{"key":"/foo/bar","value":"XXX","modifiedIndex":2,"createdIndex":2}}`, "")
  27. })
  28. }
  29. // Ensures that a key is deleted when 'dir' is not specified or false
  30. //
  31. // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
  32. // $ curl -X DELETE localhost:4001/v2/keys/foo/bar?dir=true -> fail
  33. // $ curl -X DELETE localhost:4001/v2/keys/foo/bar?dir=false
  34. //
  35. func TestV2DeleteKeyAsDirectory(t *testing.T) {
  36. tests.RunServer(func(s *server.Server) {
  37. v := url.Values{}
  38. v.Set("value", "XXX")
  39. resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
  40. tests.ReadBody(resp)
  41. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?dir=true"), url.Values{})
  42. assert.Equal(t, resp.StatusCode, http.StatusForbidden)
  43. bodyJson := tests.ReadBodyJSON(resp)
  44. assert.Equal(t, bodyJson["errorCode"], 104, "")
  45. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?dir=false"), url.Values{})
  46. assert.Equal(t, resp.StatusCode, http.StatusOK)
  47. body := tests.ReadBody(resp)
  48. assert.Nil(t, err, "")
  49. assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo/bar","modifiedIndex":3,"createdIndex":2},"prevNode":{"key":"/foo/bar","value":"XXX","modifiedIndex":2,"createdIndex":2}}`, "")
  50. })
  51. }
  52. // Ensures that an empty directory is deleted when dir is set.
  53. //
  54. // $ curl -X PUT localhost:4001/v2/keys/foo?dir=true
  55. // $ curl -X DELETE localhost:4001/v2/keys/foo ->fail
  56. // $ curl -X DELETE localhost:4001/v2/keys/foo?dir=true
  57. //
  58. func TestV2DeleteEmptyDirectory(t *testing.T) {
  59. tests.RunServer(func(s *server.Server) {
  60. resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{})
  61. tests.ReadBody(resp)
  62. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo"), url.Values{})
  63. assert.Equal(t, resp.StatusCode, http.StatusForbidden)
  64. bodyJson := tests.ReadBodyJSON(resp)
  65. assert.Equal(t, bodyJson["errorCode"], 102, "")
  66. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{})
  67. assert.Equal(t, resp.StatusCode, http.StatusOK)
  68. body := tests.ReadBody(resp)
  69. assert.Nil(t, err, "")
  70. assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2},"prevNode":{"key":"/foo","dir":true,"modifiedIndex":2,"createdIndex":2}}`, "")
  71. })
  72. }
  73. // Ensures that a not-empty directory is deleted when dir and recursive is set.
  74. //
  75. // $ curl -X PUT localhost:4001/v2/keys/foo/bar?dir=true
  76. // $ curl -X DELETE localhost:4001/v2/keys/foo?dir=true ->fail
  77. // $ curl -X DELETE localhost:4001/v2/keys/foo?dir=true&recursive=true
  78. //
  79. func TestV2DeleteNonEmptyDirectory(t *testing.T) {
  80. tests.RunServer(func(s *server.Server) {
  81. resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?dir=true"), url.Values{})
  82. tests.ReadBody(resp)
  83. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{})
  84. assert.Equal(t, resp.StatusCode, http.StatusForbidden)
  85. bodyJson := tests.ReadBodyJSON(resp)
  86. assert.Equal(t, bodyJson["errorCode"], 108, "")
  87. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true&recursive=true"), url.Values{})
  88. assert.Equal(t, resp.StatusCode, http.StatusOK)
  89. body := tests.ReadBody(resp)
  90. assert.Nil(t, err, "")
  91. assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2},"prevNode":{"key":"/foo","dir":true,"modifiedIndex":2,"createdIndex":2}}`, "")
  92. })
  93. }
  94. // Ensures that a directory is deleted when recursive is set.
  95. //
  96. // $ curl -X PUT localhost:4001/v2/keys/foo?dir=true
  97. // $ curl -X DELETE localhost:4001/v2/keys/foo?recursive=true
  98. //
  99. func TestV2DeleteDirectoryRecursiveImpliesDir(t *testing.T) {
  100. tests.RunServer(func(s *server.Server) {
  101. resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{})
  102. tests.ReadBody(resp)
  103. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?recursive=true"), url.Values{})
  104. assert.Equal(t, resp.StatusCode, http.StatusOK)
  105. body := tests.ReadBody(resp)
  106. assert.Nil(t, err, "")
  107. assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2},"prevNode":{"key":"/foo","dir":true,"modifiedIndex":2,"createdIndex":2}}`, "")
  108. })
  109. }
  110. // Ensures that prevIndex / prevValue are invalid for for directory deletion
  111. //
  112. // $ curl -X PUT localhost:4001/v2/keys/foo?dir=true
  113. // $ curl -X DELETE localhost:4001/v2/keys/foo?prevValue=X -> fail
  114. // $ curl -X DELETE localhost:4001/v2/keys/foo?prevIndex=0 -> fail
  115. // $ curl -X DELETE localhost:4001/v2/keys/foo?dir=true
  116. //
  117. func TestV2DeleteDirectoryRejectPrevArguments(t *testing.T) {
  118. tests.RunServer(func(s *server.Server) {
  119. resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{})
  120. tests.ReadBody(resp)
  121. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?prevValue=X"), url.Values{})
  122. assert.Equal(t, resp.StatusCode, http.StatusForbidden)
  123. bodyJson := tests.ReadBodyJSON(resp)
  124. assert.Equal(t, bodyJson["errorCode"], 102, "")
  125. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?prevIndex=0"), url.Values{})
  126. assert.Equal(t, resp.StatusCode, http.StatusForbidden)
  127. bodyJson = tests.ReadBodyJSON(resp)
  128. assert.Equal(t, bodyJson["errorCode"], 102, "")
  129. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?dir=true"), url.Values{})
  130. assert.Equal(t, resp.StatusCode, http.StatusOK)
  131. body := tests.ReadBody(resp)
  132. assert.Nil(t, err, "")
  133. assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo","dir":true,"modifiedIndex":3,"createdIndex":2},"prevNode":{"key":"/foo","dir":true,"modifiedIndex":2,"createdIndex":2}}`, "")
  134. })
  135. }
  136. // Ensures that a key is deleted if the previous index matches
  137. //
  138. // $ curl -X PUT localhost:4001/v2/keys/foo -d value=XXX
  139. // $ curl -X DELETE localhost:4001/v2/keys/foo?prevIndex=2
  140. //
  141. func TestV2DeleteKeyCADOnIndexSuccess(t *testing.T) {
  142. tests.RunServer(func(s *server.Server) {
  143. v := url.Values{}
  144. v.Set("value", "XXX")
  145. resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo"), v)
  146. tests.ReadBody(resp)
  147. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?prevIndex=2"), url.Values{})
  148. assert.Nil(t, err, "")
  149. body := tests.ReadBodyJSON(resp)
  150. assert.Equal(t, body["action"], "compareAndDelete", "")
  151. node := body["node"].(map[string]interface{})
  152. assert.Equal(t, node["key"], "/foo", "")
  153. assert.Equal(t, node["modifiedIndex"], 3, "")
  154. })
  155. }
  156. // Ensures that a key is not deleted if the previous index does not match
  157. //
  158. // $ curl -X PUT localhost:4001/v2/keys/foo -d value=XXX
  159. // $ curl -X DELETE localhost:4001/v2/keys/foo?prevIndex=100
  160. //
  161. func TestV2DeleteKeyCADOnIndexFail(t *testing.T) {
  162. tests.RunServer(func(s *server.Server) {
  163. v := url.Values{}
  164. v.Set("value", "XXX")
  165. resp, err := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo"), v)
  166. tests.ReadBody(resp)
  167. resp, err = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo?prevIndex=100"), url.Values{})
  168. assert.Nil(t, err, "")
  169. body := tests.ReadBodyJSON(resp)
  170. assert.Equal(t, body["errorCode"], 101)
  171. })
  172. }
  173. // Ensures that an error is thrown if an invalid previous index is provided.
  174. //
  175. // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
  176. // $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevIndex=bad_index
  177. //
  178. func TestV2DeleteKeyCADWithInvalidIndex(t *testing.T) {
  179. tests.RunServer(func(s *server.Server) {
  180. v := url.Values{}
  181. v.Set("value", "XXX")
  182. resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
  183. tests.ReadBody(resp)
  184. resp, _ = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?prevIndex=bad_index"), v)
  185. body := tests.ReadBodyJSON(resp)
  186. assert.Equal(t, body["errorCode"], 203)
  187. })
  188. }
  189. // Ensures that a key is deleted only if the previous value matches.
  190. //
  191. // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
  192. // $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevValue=XXX
  193. //
  194. func TestV2DeleteKeyCADOnValueSuccess(t *testing.T) {
  195. tests.RunServer(func(s *server.Server) {
  196. v := url.Values{}
  197. v.Set("value", "XXX")
  198. resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
  199. tests.ReadBody(resp)
  200. resp, _ = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?prevValue=XXX"), v)
  201. body := tests.ReadBodyJSON(resp)
  202. assert.Equal(t, body["action"], "compareAndDelete", "")
  203. node := body["node"].(map[string]interface{})
  204. assert.Equal(t, node["modifiedIndex"], 3, "")
  205. })
  206. }
  207. // Ensures that a key is not deleted if the previous value does not match.
  208. //
  209. // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
  210. // $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevValue=YYY
  211. //
  212. func TestV2DeleteKeyCADOnValueFail(t *testing.T) {
  213. tests.RunServer(func(s *server.Server) {
  214. v := url.Values{}
  215. v.Set("value", "XXX")
  216. resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
  217. tests.ReadBody(resp)
  218. resp, _ = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?prevValue=YYY"), v)
  219. body := tests.ReadBodyJSON(resp)
  220. assert.Equal(t, body["errorCode"], 101)
  221. })
  222. }
  223. // Ensures that an error is thrown if an invalid previous value is provided.
  224. //
  225. // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX
  226. // $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevIndex=
  227. //
  228. func TestV2DeleteKeyCADWithInvalidValue(t *testing.T) {
  229. tests.RunServer(func(s *server.Server) {
  230. v := url.Values{}
  231. v.Set("value", "XXX")
  232. resp, _ := tests.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar"), v)
  233. tests.ReadBody(resp)
  234. resp, _ = tests.DeleteForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/foo/bar?prevValue="), v)
  235. body := tests.ReadBodyJSON(resp)
  236. assert.Equal(t, body["errorCode"], 201)
  237. })
  238. }