keys_test.go 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393
  1. // Copyright 2015 CoreOS, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package client
  15. import (
  16. "errors"
  17. "fmt"
  18. "io/ioutil"
  19. "net/http"
  20. "net/url"
  21. "reflect"
  22. "testing"
  23. "time"
  24. "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
  25. )
  26. func TestV2KeysURLHelper(t *testing.T) {
  27. tests := []struct {
  28. endpoint url.URL
  29. prefix string
  30. key string
  31. want url.URL
  32. }{
  33. // key is empty, no problem
  34. {
  35. endpoint: url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys"},
  36. prefix: "",
  37. key: "",
  38. want: url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys"},
  39. },
  40. // key is joined to path
  41. {
  42. endpoint: url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys"},
  43. prefix: "",
  44. key: "/foo/bar",
  45. want: url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys/foo/bar"},
  46. },
  47. // key is joined to path when path is empty
  48. {
  49. endpoint: url.URL{Scheme: "http", Host: "example.com", Path: ""},
  50. prefix: "",
  51. key: "/foo/bar",
  52. want: url.URL{Scheme: "http", Host: "example.com", Path: "/foo/bar"},
  53. },
  54. // Host field carries through with port
  55. {
  56. endpoint: url.URL{Scheme: "http", Host: "example.com:8080", Path: "/v2/keys"},
  57. prefix: "",
  58. key: "",
  59. want: url.URL{Scheme: "http", Host: "example.com:8080", Path: "/v2/keys"},
  60. },
  61. // Scheme carries through
  62. {
  63. endpoint: url.URL{Scheme: "https", Host: "example.com", Path: "/v2/keys"},
  64. prefix: "",
  65. key: "",
  66. want: url.URL{Scheme: "https", Host: "example.com", Path: "/v2/keys"},
  67. },
  68. // Prefix is applied
  69. {
  70. endpoint: url.URL{Scheme: "https", Host: "example.com", Path: "/foo"},
  71. prefix: "/bar",
  72. key: "/baz",
  73. want: url.URL{Scheme: "https", Host: "example.com", Path: "/foo/bar/baz"},
  74. },
  75. }
  76. for i, tt := range tests {
  77. got := v2KeysURL(tt.endpoint, tt.prefix, tt.key)
  78. if tt.want != *got {
  79. t.Errorf("#%d: want=%#v, got=%#v", i, tt.want, *got)
  80. }
  81. }
  82. }
  83. func TestGetAction(t *testing.T) {
  84. ep := url.URL{Scheme: "http", Host: "example.com/v2/keys"}
  85. baseWantURL := &url.URL{
  86. Scheme: "http",
  87. Host: "example.com",
  88. Path: "/v2/keys/foo/bar",
  89. }
  90. wantHeader := http.Header{}
  91. tests := []struct {
  92. recursive bool
  93. sorted bool
  94. quorum bool
  95. wantQuery string
  96. }{
  97. {
  98. recursive: false,
  99. sorted: false,
  100. quorum: false,
  101. wantQuery: "quorum=false&recursive=false&sorted=false",
  102. },
  103. {
  104. recursive: true,
  105. sorted: false,
  106. quorum: false,
  107. wantQuery: "quorum=false&recursive=true&sorted=false",
  108. },
  109. {
  110. recursive: false,
  111. sorted: true,
  112. quorum: false,
  113. wantQuery: "quorum=false&recursive=false&sorted=true",
  114. },
  115. {
  116. recursive: true,
  117. sorted: true,
  118. quorum: false,
  119. wantQuery: "quorum=false&recursive=true&sorted=true",
  120. },
  121. {
  122. recursive: false,
  123. sorted: false,
  124. quorum: true,
  125. wantQuery: "quorum=true&recursive=false&sorted=false",
  126. },
  127. }
  128. for i, tt := range tests {
  129. f := getAction{
  130. Key: "/foo/bar",
  131. Recursive: tt.recursive,
  132. Sorted: tt.sorted,
  133. Quorum: tt.quorum,
  134. }
  135. got := *f.HTTPRequest(ep)
  136. wantURL := baseWantURL
  137. wantURL.RawQuery = tt.wantQuery
  138. err := assertRequest(got, "GET", wantURL, wantHeader, nil)
  139. if err != nil {
  140. t.Errorf("#%d: %v", i, err)
  141. }
  142. }
  143. }
  144. func TestWaitAction(t *testing.T) {
  145. ep := url.URL{Scheme: "http", Host: "example.com/v2/keys"}
  146. baseWantURL := &url.URL{
  147. Scheme: "http",
  148. Host: "example.com",
  149. Path: "/v2/keys/foo/bar",
  150. }
  151. wantHeader := http.Header{}
  152. tests := []struct {
  153. waitIndex uint64
  154. recursive bool
  155. wantQuery string
  156. }{
  157. {
  158. recursive: false,
  159. waitIndex: uint64(0),
  160. wantQuery: "recursive=false&wait=true&waitIndex=0",
  161. },
  162. {
  163. recursive: false,
  164. waitIndex: uint64(12),
  165. wantQuery: "recursive=false&wait=true&waitIndex=12",
  166. },
  167. {
  168. recursive: true,
  169. waitIndex: uint64(12),
  170. wantQuery: "recursive=true&wait=true&waitIndex=12",
  171. },
  172. }
  173. for i, tt := range tests {
  174. f := waitAction{
  175. Key: "/foo/bar",
  176. WaitIndex: tt.waitIndex,
  177. Recursive: tt.recursive,
  178. }
  179. got := *f.HTTPRequest(ep)
  180. wantURL := baseWantURL
  181. wantURL.RawQuery = tt.wantQuery
  182. err := assertRequest(got, "GET", wantURL, wantHeader, nil)
  183. if err != nil {
  184. t.Errorf("#%d: unexpected error: %#v", i, err)
  185. }
  186. }
  187. }
  188. func TestSetAction(t *testing.T) {
  189. wantHeader := http.Header(map[string][]string{
  190. "Content-Type": []string{"application/x-www-form-urlencoded"},
  191. })
  192. tests := []struct {
  193. act setAction
  194. wantURL string
  195. wantBody string
  196. }{
  197. // default prefix
  198. {
  199. act: setAction{
  200. Prefix: defaultV2KeysPrefix,
  201. Key: "foo",
  202. },
  203. wantURL: "http://example.com/v2/keys/foo",
  204. wantBody: "value=",
  205. },
  206. // non-default prefix
  207. {
  208. act: setAction{
  209. Prefix: "/pfx",
  210. Key: "foo",
  211. },
  212. wantURL: "http://example.com/pfx/foo",
  213. wantBody: "value=",
  214. },
  215. // no prefix
  216. {
  217. act: setAction{
  218. Key: "foo",
  219. },
  220. wantURL: "http://example.com/foo",
  221. wantBody: "value=",
  222. },
  223. // Key with path separators
  224. {
  225. act: setAction{
  226. Prefix: defaultV2KeysPrefix,
  227. Key: "foo/bar/baz",
  228. },
  229. wantURL: "http://example.com/v2/keys/foo/bar/baz",
  230. wantBody: "value=",
  231. },
  232. // Key with leading slash, Prefix with trailing slash
  233. {
  234. act: setAction{
  235. Prefix: "/foo/",
  236. Key: "/bar",
  237. },
  238. wantURL: "http://example.com/foo/bar",
  239. wantBody: "value=",
  240. },
  241. // Key with trailing slash
  242. {
  243. act: setAction{
  244. Key: "/foo/",
  245. },
  246. wantURL: "http://example.com/foo",
  247. wantBody: "value=",
  248. },
  249. // Value is set
  250. {
  251. act: setAction{
  252. Key: "foo",
  253. Value: "baz",
  254. },
  255. wantURL: "http://example.com/foo",
  256. wantBody: "value=baz",
  257. },
  258. // PrevExist set, but still ignored
  259. {
  260. act: setAction{
  261. Key: "foo",
  262. PrevExist: PrevIgnore,
  263. },
  264. wantURL: "http://example.com/foo",
  265. wantBody: "value=",
  266. },
  267. // PrevExist set to true
  268. {
  269. act: setAction{
  270. Key: "foo",
  271. PrevExist: PrevExist,
  272. },
  273. wantURL: "http://example.com/foo?prevExist=true",
  274. wantBody: "value=",
  275. },
  276. // PrevExist set to false
  277. {
  278. act: setAction{
  279. Key: "foo",
  280. PrevExist: PrevNoExist,
  281. },
  282. wantURL: "http://example.com/foo?prevExist=false",
  283. wantBody: "value=",
  284. },
  285. // PrevValue is urlencoded
  286. {
  287. act: setAction{
  288. Key: "foo",
  289. PrevValue: "bar baz",
  290. },
  291. wantURL: "http://example.com/foo?prevValue=bar+baz",
  292. wantBody: "value=",
  293. },
  294. // PrevIndex is set
  295. {
  296. act: setAction{
  297. Key: "foo",
  298. PrevIndex: uint64(12),
  299. },
  300. wantURL: "http://example.com/foo?prevIndex=12",
  301. wantBody: "value=",
  302. },
  303. // TTL is set
  304. {
  305. act: setAction{
  306. Key: "foo",
  307. TTL: 3 * time.Minute,
  308. },
  309. wantURL: "http://example.com/foo",
  310. wantBody: "ttl=180&value=",
  311. },
  312. // Dir is set
  313. {
  314. act: setAction{
  315. Key: "foo",
  316. Dir: true,
  317. },
  318. wantURL: "http://example.com/foo?dir=true",
  319. wantBody: "",
  320. },
  321. // Dir is set with a value
  322. {
  323. act: setAction{
  324. Key: "foo",
  325. Value: "bar",
  326. Dir: true,
  327. },
  328. wantURL: "http://example.com/foo?dir=true",
  329. wantBody: "",
  330. },
  331. // Dir is set with PrevExist set to true
  332. {
  333. act: setAction{
  334. Key: "foo",
  335. PrevExist: PrevExist,
  336. Dir: true,
  337. },
  338. wantURL: "http://example.com/foo?dir=true&prevExist=true",
  339. wantBody: "",
  340. },
  341. // Dir is set with PrevValue
  342. {
  343. act: setAction{
  344. Key: "foo",
  345. PrevValue: "bar",
  346. Dir: true,
  347. },
  348. wantURL: "http://example.com/foo?dir=true",
  349. wantBody: "",
  350. },
  351. }
  352. for i, tt := range tests {
  353. u, err := url.Parse(tt.wantURL)
  354. if err != nil {
  355. t.Errorf("#%d: unable to use wantURL fixture: %v", i, err)
  356. }
  357. got := tt.act.HTTPRequest(url.URL{Scheme: "http", Host: "example.com"})
  358. if err := assertRequest(*got, "PUT", u, wantHeader, []byte(tt.wantBody)); err != nil {
  359. t.Errorf("#%d: %v", i, err)
  360. }
  361. }
  362. }
  363. func TestCreateInOrderAction(t *testing.T) {
  364. wantHeader := http.Header(map[string][]string{
  365. "Content-Type": []string{"application/x-www-form-urlencoded"},
  366. })
  367. tests := []struct {
  368. act createInOrderAction
  369. wantURL string
  370. wantBody string
  371. }{
  372. // default prefix
  373. {
  374. act: createInOrderAction{
  375. Prefix: defaultV2KeysPrefix,
  376. Dir: "foo",
  377. },
  378. wantURL: "http://example.com/v2/keys/foo",
  379. wantBody: "value=",
  380. },
  381. // non-default prefix
  382. {
  383. act: createInOrderAction{
  384. Prefix: "/pfx",
  385. Dir: "foo",
  386. },
  387. wantURL: "http://example.com/pfx/foo",
  388. wantBody: "value=",
  389. },
  390. // no prefix
  391. {
  392. act: createInOrderAction{
  393. Dir: "foo",
  394. },
  395. wantURL: "http://example.com/foo",
  396. wantBody: "value=",
  397. },
  398. // Key with path separators
  399. {
  400. act: createInOrderAction{
  401. Prefix: defaultV2KeysPrefix,
  402. Dir: "foo/bar/baz",
  403. },
  404. wantURL: "http://example.com/v2/keys/foo/bar/baz",
  405. wantBody: "value=",
  406. },
  407. // Key with leading slash, Prefix with trailing slash
  408. {
  409. act: createInOrderAction{
  410. Prefix: "/foo/",
  411. Dir: "/bar",
  412. },
  413. wantURL: "http://example.com/foo/bar",
  414. wantBody: "value=",
  415. },
  416. // Key with trailing slash
  417. {
  418. act: createInOrderAction{
  419. Dir: "/foo/",
  420. },
  421. wantURL: "http://example.com/foo",
  422. wantBody: "value=",
  423. },
  424. // Value is set
  425. {
  426. act: createInOrderAction{
  427. Dir: "foo",
  428. Value: "baz",
  429. },
  430. wantURL: "http://example.com/foo",
  431. wantBody: "value=baz",
  432. },
  433. // TTL is set
  434. {
  435. act: createInOrderAction{
  436. Dir: "foo",
  437. TTL: 3 * time.Minute,
  438. },
  439. wantURL: "http://example.com/foo",
  440. wantBody: "ttl=180&value=",
  441. },
  442. }
  443. for i, tt := range tests {
  444. u, err := url.Parse(tt.wantURL)
  445. if err != nil {
  446. t.Errorf("#%d: unable to use wantURL fixture: %v", i, err)
  447. }
  448. got := tt.act.HTTPRequest(url.URL{Scheme: "http", Host: "example.com"})
  449. if err := assertRequest(*got, "POST", u, wantHeader, []byte(tt.wantBody)); err != nil {
  450. t.Errorf("#%d: %v", i, err)
  451. }
  452. }
  453. }
  454. func TestDeleteAction(t *testing.T) {
  455. wantHeader := http.Header(map[string][]string{
  456. "Content-Type": []string{"application/x-www-form-urlencoded"},
  457. })
  458. tests := []struct {
  459. act deleteAction
  460. wantURL string
  461. }{
  462. // default prefix
  463. {
  464. act: deleteAction{
  465. Prefix: defaultV2KeysPrefix,
  466. Key: "foo",
  467. },
  468. wantURL: "http://example.com/v2/keys/foo",
  469. },
  470. // non-default prefix
  471. {
  472. act: deleteAction{
  473. Prefix: "/pfx",
  474. Key: "foo",
  475. },
  476. wantURL: "http://example.com/pfx/foo",
  477. },
  478. // no prefix
  479. {
  480. act: deleteAction{
  481. Key: "foo",
  482. },
  483. wantURL: "http://example.com/foo",
  484. },
  485. // Key with path separators
  486. {
  487. act: deleteAction{
  488. Prefix: defaultV2KeysPrefix,
  489. Key: "foo/bar/baz",
  490. },
  491. wantURL: "http://example.com/v2/keys/foo/bar/baz",
  492. },
  493. // Key with leading slash, Prefix with trailing slash
  494. {
  495. act: deleteAction{
  496. Prefix: "/foo/",
  497. Key: "/bar",
  498. },
  499. wantURL: "http://example.com/foo/bar",
  500. },
  501. // Key with trailing slash
  502. {
  503. act: deleteAction{
  504. Key: "/foo/",
  505. },
  506. wantURL: "http://example.com/foo",
  507. },
  508. // Recursive set to true
  509. {
  510. act: deleteAction{
  511. Key: "foo",
  512. Recursive: true,
  513. },
  514. wantURL: "http://example.com/foo?recursive=true",
  515. },
  516. // PrevValue is urlencoded
  517. {
  518. act: deleteAction{
  519. Key: "foo",
  520. PrevValue: "bar baz",
  521. },
  522. wantURL: "http://example.com/foo?prevValue=bar+baz",
  523. },
  524. // PrevIndex is set
  525. {
  526. act: deleteAction{
  527. Key: "foo",
  528. PrevIndex: uint64(12),
  529. },
  530. wantURL: "http://example.com/foo?prevIndex=12",
  531. },
  532. }
  533. for i, tt := range tests {
  534. u, err := url.Parse(tt.wantURL)
  535. if err != nil {
  536. t.Errorf("#%d: unable to use wantURL fixture: %v", i, err)
  537. }
  538. got := tt.act.HTTPRequest(url.URL{Scheme: "http", Host: "example.com"})
  539. if err := assertRequest(*got, "DELETE", u, wantHeader, nil); err != nil {
  540. t.Errorf("#%d: %v", i, err)
  541. }
  542. }
  543. }
  544. func assertRequest(got http.Request, wantMethod string, wantURL *url.URL, wantHeader http.Header, wantBody []byte) error {
  545. if wantMethod != got.Method {
  546. return fmt.Errorf("want.Method=%#v got.Method=%#v", wantMethod, got.Method)
  547. }
  548. if !reflect.DeepEqual(wantURL, got.URL) {
  549. return fmt.Errorf("want.URL=%#v got.URL=%#v", wantURL, got.URL)
  550. }
  551. if !reflect.DeepEqual(wantHeader, got.Header) {
  552. return fmt.Errorf("want.Header=%#v got.Header=%#v", wantHeader, got.Header)
  553. }
  554. if got.Body == nil {
  555. if wantBody != nil {
  556. return fmt.Errorf("want.Body=%v got.Body=%v", wantBody, got.Body)
  557. }
  558. } else {
  559. if wantBody == nil {
  560. return fmt.Errorf("want.Body=%v got.Body=%s", wantBody, got.Body)
  561. } else {
  562. gotBytes, err := ioutil.ReadAll(got.Body)
  563. if err != nil {
  564. return err
  565. }
  566. if !reflect.DeepEqual(wantBody, gotBytes) {
  567. return fmt.Errorf("want.Body=%s got.Body=%s", wantBody, gotBytes)
  568. }
  569. }
  570. }
  571. return nil
  572. }
  573. func TestUnmarshalSuccessfulResponse(t *testing.T) {
  574. var expiration time.Time
  575. expiration.UnmarshalText([]byte("2015-04-07T04:40:23.044979686Z"))
  576. tests := []struct {
  577. hdr string
  578. body string
  579. wantRes *Response
  580. wantErr bool
  581. }{
  582. // Neither PrevNode or Node
  583. {
  584. hdr: "1",
  585. body: `{"action":"delete"}`,
  586. wantRes: &Response{Action: "delete", Index: 1},
  587. wantErr: false,
  588. },
  589. // PrevNode
  590. {
  591. hdr: "15",
  592. body: `{"action":"delete", "prevNode": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10}}`,
  593. wantRes: &Response{
  594. Action: "delete",
  595. Index: 15,
  596. Node: nil,
  597. PrevNode: &Node{
  598. Key: "/foo",
  599. Value: "bar",
  600. ModifiedIndex: 12,
  601. CreatedIndex: 10,
  602. },
  603. },
  604. wantErr: false,
  605. },
  606. // Node
  607. {
  608. hdr: "15",
  609. body: `{"action":"get", "node": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10, "ttl": 10, "expiration": "2015-04-07T04:40:23.044979686Z"}}`,
  610. wantRes: &Response{
  611. Action: "get",
  612. Index: 15,
  613. Node: &Node{
  614. Key: "/foo",
  615. Value: "bar",
  616. ModifiedIndex: 12,
  617. CreatedIndex: 10,
  618. TTL: 10,
  619. Expiration: &expiration,
  620. },
  621. PrevNode: nil,
  622. },
  623. wantErr: false,
  624. },
  625. // Node Dir
  626. {
  627. hdr: "15",
  628. body: `{"action":"get", "node": {"key": "/foo", "dir": true, "modifiedIndex": 12, "createdIndex": 10}}`,
  629. wantRes: &Response{
  630. Action: "get",
  631. Index: 15,
  632. Node: &Node{
  633. Key: "/foo",
  634. Dir: true,
  635. ModifiedIndex: 12,
  636. CreatedIndex: 10,
  637. },
  638. PrevNode: nil,
  639. },
  640. wantErr: false,
  641. },
  642. // PrevNode and Node
  643. {
  644. hdr: "15",
  645. body: `{"action":"update", "prevNode": {"key": "/foo", "value": "baz", "modifiedIndex": 10, "createdIndex": 10}, "node": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10}}`,
  646. wantRes: &Response{
  647. Action: "update",
  648. Index: 15,
  649. PrevNode: &Node{
  650. Key: "/foo",
  651. Value: "baz",
  652. ModifiedIndex: 10,
  653. CreatedIndex: 10,
  654. },
  655. Node: &Node{
  656. Key: "/foo",
  657. Value: "bar",
  658. ModifiedIndex: 12,
  659. CreatedIndex: 10,
  660. },
  661. },
  662. wantErr: false,
  663. },
  664. // Garbage in body
  665. {
  666. hdr: "",
  667. body: `garbage`,
  668. wantRes: nil,
  669. wantErr: true,
  670. },
  671. // non-integer index
  672. {
  673. hdr: "poo",
  674. body: `{}`,
  675. wantRes: nil,
  676. wantErr: true,
  677. },
  678. }
  679. for i, tt := range tests {
  680. h := make(http.Header)
  681. h.Add("X-Etcd-Index", tt.hdr)
  682. res, err := unmarshalSuccessfulKeysResponse(h, []byte(tt.body))
  683. if tt.wantErr != (err != nil) {
  684. t.Errorf("#%d: wantErr=%t, err=%v", i, tt.wantErr, err)
  685. }
  686. if (res == nil) != (tt.wantRes == nil) {
  687. t.Errorf("#%d: received res=%#v, but expected res=%#v", i, res, tt.wantRes)
  688. continue
  689. } else if tt.wantRes == nil {
  690. // expected and successfully got nil response
  691. continue
  692. }
  693. if res.Action != tt.wantRes.Action {
  694. t.Errorf("#%d: Action=%s, expected %s", i, res.Action, tt.wantRes.Action)
  695. }
  696. if res.Index != tt.wantRes.Index {
  697. t.Errorf("#%d: Index=%d, expected %d", i, res.Index, tt.wantRes.Index)
  698. }
  699. if !reflect.DeepEqual(res.Node, tt.wantRes.Node) {
  700. t.Errorf("#%d: Node=%v, expected %v", i, res.Node, tt.wantRes.Node)
  701. }
  702. }
  703. }
  704. func TestUnmarshalFailedKeysResponse(t *testing.T) {
  705. body := []byte(`{"errorCode":100,"message":"Key not found","cause":"/foo","index":18}`)
  706. wantErr := Error{
  707. Code: 100,
  708. Message: "Key not found",
  709. Cause: "/foo",
  710. Index: uint64(18),
  711. }
  712. gotErr := unmarshalFailedKeysResponse(body)
  713. if !reflect.DeepEqual(wantErr, gotErr) {
  714. t.Errorf("unexpected error: want=%#v got=%#v", wantErr, gotErr)
  715. }
  716. }
  717. func TestUnmarshalFailedKeysResponseBadJSON(t *testing.T) {
  718. err := unmarshalFailedKeysResponse([]byte(`{"er`))
  719. if err == nil {
  720. t.Errorf("got nil error")
  721. } else if _, ok := err.(Error); ok {
  722. t.Errorf("error is of incorrect type *Error: %#v", err)
  723. }
  724. }
  725. func TestHTTPWatcherNextWaitAction(t *testing.T) {
  726. initAction := waitAction{
  727. Prefix: "/pants",
  728. Key: "/foo/bar",
  729. Recursive: true,
  730. WaitIndex: 19,
  731. }
  732. client := &actionAssertingHTTPClient{
  733. t: t,
  734. act: &initAction,
  735. resp: http.Response{
  736. StatusCode: http.StatusOK,
  737. Header: http.Header{"X-Etcd-Index": []string{"42"}},
  738. },
  739. body: []byte(`{"action":"update","node":{"key":"/pants/foo/bar/baz","value":"snarf","modifiedIndex":21,"createdIndex":19},"prevNode":{"key":"/pants/foo/bar/baz","value":"snazz","modifiedIndex":20,"createdIndex":19}}`),
  740. }
  741. wantResponse := &Response{
  742. Action: "update",
  743. Node: &Node{Key: "/pants/foo/bar/baz", Value: "snarf", CreatedIndex: uint64(19), ModifiedIndex: uint64(21)},
  744. PrevNode: &Node{Key: "/pants/foo/bar/baz", Value: "snazz", CreatedIndex: uint64(19), ModifiedIndex: uint64(20)},
  745. Index: uint64(42),
  746. }
  747. wantNextWait := waitAction{
  748. Prefix: "/pants",
  749. Key: "/foo/bar",
  750. Recursive: true,
  751. WaitIndex: 22,
  752. }
  753. watcher := &httpWatcher{
  754. client: client,
  755. nextWait: initAction,
  756. }
  757. resp, err := watcher.Next(context.Background())
  758. if err != nil {
  759. t.Errorf("non-nil error: %#v", err)
  760. }
  761. if !reflect.DeepEqual(wantResponse, resp) {
  762. t.Errorf("received incorrect Response: want=%#v got=%#v", wantResponse, resp)
  763. }
  764. if !reflect.DeepEqual(wantNextWait, watcher.nextWait) {
  765. t.Errorf("nextWait incorrect: want=%#v got=%#v", wantNextWait, watcher.nextWait)
  766. }
  767. }
  768. func TestHTTPWatcherNextFail(t *testing.T) {
  769. tests := []httpClient{
  770. // generic HTTP client failure
  771. &staticHTTPClient{
  772. err: errors.New("fail!"),
  773. },
  774. // unusable status code
  775. &staticHTTPClient{
  776. resp: http.Response{
  777. StatusCode: http.StatusTeapot,
  778. },
  779. },
  780. // etcd Error response
  781. &staticHTTPClient{
  782. resp: http.Response{
  783. StatusCode: http.StatusNotFound,
  784. },
  785. body: []byte(`{"errorCode":100,"message":"Key not found","cause":"/foo","index":18}`),
  786. },
  787. }
  788. for i, tt := range tests {
  789. act := waitAction{
  790. Prefix: "/pants",
  791. Key: "/foo/bar",
  792. Recursive: true,
  793. WaitIndex: 19,
  794. }
  795. watcher := &httpWatcher{
  796. client: tt,
  797. nextWait: act,
  798. }
  799. resp, err := watcher.Next(context.Background())
  800. if err == nil {
  801. t.Errorf("#%d: expected non-nil error", i)
  802. }
  803. if resp != nil {
  804. t.Errorf("#%d: expected nil Response, got %#v", i, resp)
  805. }
  806. if !reflect.DeepEqual(act, watcher.nextWait) {
  807. t.Errorf("#%d: nextWait changed: want=%#v got=%#v", i, act, watcher.nextWait)
  808. }
  809. }
  810. }
  811. func TestHTTPKeysAPIWatcherAction(t *testing.T) {
  812. tests := []struct {
  813. key string
  814. opts *WatcherOptions
  815. want waitAction
  816. }{
  817. {
  818. key: "/foo",
  819. opts: nil,
  820. want: waitAction{
  821. Key: "/foo",
  822. Recursive: false,
  823. WaitIndex: 0,
  824. },
  825. },
  826. {
  827. key: "/foo",
  828. opts: &WatcherOptions{
  829. Recursive: false,
  830. AfterIndex: 0,
  831. },
  832. want: waitAction{
  833. Key: "/foo",
  834. Recursive: false,
  835. WaitIndex: 0,
  836. },
  837. },
  838. {
  839. key: "/foo",
  840. opts: &WatcherOptions{
  841. Recursive: true,
  842. AfterIndex: 0,
  843. },
  844. want: waitAction{
  845. Key: "/foo",
  846. Recursive: true,
  847. WaitIndex: 0,
  848. },
  849. },
  850. {
  851. key: "/foo",
  852. opts: &WatcherOptions{
  853. Recursive: false,
  854. AfterIndex: 19,
  855. },
  856. want: waitAction{
  857. Key: "/foo",
  858. Recursive: false,
  859. WaitIndex: 20,
  860. },
  861. },
  862. }
  863. for i, tt := range tests {
  864. kAPI := &httpKeysAPI{
  865. client: &staticHTTPClient{err: errors.New("fail!")},
  866. }
  867. want := &httpWatcher{
  868. client: &staticHTTPClient{err: errors.New("fail!")},
  869. nextWait: tt.want,
  870. }
  871. got := kAPI.Watcher(tt.key, tt.opts)
  872. if !reflect.DeepEqual(want, got) {
  873. t.Errorf("#%d: incorrect watcher: want=%#v got=%#v", i, want, got)
  874. }
  875. }
  876. }
  877. func TestHTTPKeysAPISetAction(t *testing.T) {
  878. tests := []struct {
  879. key string
  880. value string
  881. opts *SetOptions
  882. wantAction httpAction
  883. }{
  884. // nil SetOptions
  885. {
  886. key: "/foo",
  887. value: "bar",
  888. opts: nil,
  889. wantAction: &setAction{
  890. Key: "/foo",
  891. Value: "bar",
  892. PrevValue: "",
  893. PrevIndex: 0,
  894. PrevExist: PrevIgnore,
  895. TTL: 0,
  896. },
  897. },
  898. // empty SetOptions
  899. {
  900. key: "/foo",
  901. value: "bar",
  902. opts: &SetOptions{},
  903. wantAction: &setAction{
  904. Key: "/foo",
  905. Value: "bar",
  906. PrevValue: "",
  907. PrevIndex: 0,
  908. PrevExist: PrevIgnore,
  909. TTL: 0,
  910. },
  911. },
  912. // populated SetOptions
  913. {
  914. key: "/foo",
  915. value: "bar",
  916. opts: &SetOptions{
  917. PrevValue: "baz",
  918. PrevIndex: 13,
  919. PrevExist: PrevExist,
  920. TTL: time.Minute,
  921. Dir: true,
  922. },
  923. wantAction: &setAction{
  924. Key: "/foo",
  925. Value: "bar",
  926. PrevValue: "baz",
  927. PrevIndex: 13,
  928. PrevExist: PrevExist,
  929. TTL: time.Minute,
  930. Dir: true,
  931. },
  932. },
  933. }
  934. for i, tt := range tests {
  935. client := &actionAssertingHTTPClient{t: t, num: i, act: tt.wantAction}
  936. kAPI := httpKeysAPI{client: client}
  937. kAPI.Set(context.Background(), tt.key, tt.value, tt.opts)
  938. }
  939. }
  940. func TestHTTPKeysAPISetError(t *testing.T) {
  941. tests := []httpClient{
  942. // generic HTTP client failure
  943. &staticHTTPClient{
  944. err: errors.New("fail!"),
  945. },
  946. // unusable status code
  947. &staticHTTPClient{
  948. resp: http.Response{
  949. StatusCode: http.StatusTeapot,
  950. },
  951. },
  952. // etcd Error response
  953. &staticHTTPClient{
  954. resp: http.Response{
  955. StatusCode: http.StatusInternalServerError,
  956. },
  957. body: []byte(`{"errorCode":300,"message":"Raft internal error","cause":"/foo","index":18}`),
  958. },
  959. }
  960. for i, tt := range tests {
  961. kAPI := httpKeysAPI{client: tt}
  962. resp, err := kAPI.Set(context.Background(), "/foo", "bar", nil)
  963. if err == nil {
  964. t.Errorf("#%d: received nil error", i)
  965. }
  966. if resp != nil {
  967. t.Errorf("#%d: received non-nil Response: %#v", i, resp)
  968. }
  969. }
  970. }
  971. func TestHTTPKeysAPISetResponse(t *testing.T) {
  972. client := &staticHTTPClient{
  973. resp: http.Response{
  974. StatusCode: http.StatusOK,
  975. Header: http.Header{"X-Etcd-Index": []string{"21"}},
  976. },
  977. body: []byte(`{"action":"set","node":{"key":"/pants/foo/bar/baz","value":"snarf","modifiedIndex":21,"createdIndex":21},"prevNode":{"key":"/pants/foo/bar/baz","value":"snazz","modifiedIndex":20,"createdIndex":19}}`),
  978. }
  979. wantResponse := &Response{
  980. Action: "set",
  981. Node: &Node{Key: "/pants/foo/bar/baz", Value: "snarf", CreatedIndex: uint64(21), ModifiedIndex: uint64(21)},
  982. PrevNode: &Node{Key: "/pants/foo/bar/baz", Value: "snazz", CreatedIndex: uint64(19), ModifiedIndex: uint64(20)},
  983. Index: uint64(21),
  984. }
  985. kAPI := &httpKeysAPI{client: client, prefix: "/pants"}
  986. resp, err := kAPI.Set(context.Background(), "/foo/bar/baz", "snarf", nil)
  987. if err != nil {
  988. t.Errorf("non-nil error: %#v", err)
  989. }
  990. if !reflect.DeepEqual(wantResponse, resp) {
  991. t.Errorf("incorrect Response: want=%#v got=%#v", wantResponse, resp)
  992. }
  993. }
  994. func TestHTTPKeysAPIGetAction(t *testing.T) {
  995. tests := []struct {
  996. key string
  997. opts *GetOptions
  998. wantAction httpAction
  999. }{
  1000. // nil GetOptions
  1001. {
  1002. key: "/foo",
  1003. opts: nil,
  1004. wantAction: &getAction{
  1005. Key: "/foo",
  1006. Sorted: false,
  1007. Recursive: false,
  1008. },
  1009. },
  1010. // empty GetOptions
  1011. {
  1012. key: "/foo",
  1013. opts: &GetOptions{},
  1014. wantAction: &getAction{
  1015. Key: "/foo",
  1016. Sorted: false,
  1017. Recursive: false,
  1018. },
  1019. },
  1020. // populated GetOptions
  1021. {
  1022. key: "/foo",
  1023. opts: &GetOptions{
  1024. Sort: true,
  1025. Recursive: true,
  1026. Quorum: true,
  1027. },
  1028. wantAction: &getAction{
  1029. Key: "/foo",
  1030. Sorted: true,
  1031. Recursive: true,
  1032. Quorum: true,
  1033. },
  1034. },
  1035. }
  1036. for i, tt := range tests {
  1037. client := &actionAssertingHTTPClient{t: t, num: i, act: tt.wantAction}
  1038. kAPI := httpKeysAPI{client: client}
  1039. kAPI.Get(context.Background(), tt.key, tt.opts)
  1040. }
  1041. }
  1042. func TestHTTPKeysAPIGetError(t *testing.T) {
  1043. tests := []httpClient{
  1044. // generic HTTP client failure
  1045. &staticHTTPClient{
  1046. err: errors.New("fail!"),
  1047. },
  1048. // unusable status code
  1049. &staticHTTPClient{
  1050. resp: http.Response{
  1051. StatusCode: http.StatusTeapot,
  1052. },
  1053. },
  1054. // etcd Error response
  1055. &staticHTTPClient{
  1056. resp: http.Response{
  1057. StatusCode: http.StatusInternalServerError,
  1058. },
  1059. body: []byte(`{"errorCode":300,"message":"Raft internal error","cause":"/foo","index":18}`),
  1060. },
  1061. }
  1062. for i, tt := range tests {
  1063. kAPI := httpKeysAPI{client: tt}
  1064. resp, err := kAPI.Get(context.Background(), "/foo", nil)
  1065. if err == nil {
  1066. t.Errorf("#%d: received nil error", i)
  1067. }
  1068. if resp != nil {
  1069. t.Errorf("#%d: received non-nil Response: %#v", i, resp)
  1070. }
  1071. }
  1072. }
  1073. func TestHTTPKeysAPIGetResponse(t *testing.T) {
  1074. client := &staticHTTPClient{
  1075. resp: http.Response{
  1076. StatusCode: http.StatusOK,
  1077. Header: http.Header{"X-Etcd-Index": []string{"42"}},
  1078. },
  1079. body: []byte(`{"action":"get","node":{"key":"/pants/foo/bar","modifiedIndex":25,"createdIndex":19,"nodes":[{"key":"/pants/foo/bar/baz","value":"snarf","createdIndex":21,"modifiedIndex":25}]}}`),
  1080. }
  1081. wantResponse := &Response{
  1082. Action: "get",
  1083. Node: &Node{
  1084. Key: "/pants/foo/bar",
  1085. Nodes: []*Node{
  1086. &Node{Key: "/pants/foo/bar/baz", Value: "snarf", CreatedIndex: 21, ModifiedIndex: 25},
  1087. },
  1088. CreatedIndex: uint64(19),
  1089. ModifiedIndex: uint64(25),
  1090. },
  1091. Index: uint64(42),
  1092. }
  1093. kAPI := &httpKeysAPI{client: client, prefix: "/pants"}
  1094. resp, err := kAPI.Get(context.Background(), "/foo/bar", &GetOptions{Recursive: true})
  1095. if err != nil {
  1096. t.Errorf("non-nil error: %#v", err)
  1097. }
  1098. if !reflect.DeepEqual(wantResponse, resp) {
  1099. t.Errorf("incorrect Response: want=%#v got=%#v", wantResponse, resp)
  1100. }
  1101. }
  1102. func TestHTTPKeysAPIDeleteAction(t *testing.T) {
  1103. tests := []struct {
  1104. key string
  1105. value string
  1106. opts *DeleteOptions
  1107. wantAction httpAction
  1108. }{
  1109. // nil DeleteOptions
  1110. {
  1111. key: "/foo",
  1112. opts: nil,
  1113. wantAction: &deleteAction{
  1114. Key: "/foo",
  1115. PrevValue: "",
  1116. PrevIndex: 0,
  1117. Recursive: false,
  1118. },
  1119. },
  1120. // empty DeleteOptions
  1121. {
  1122. key: "/foo",
  1123. opts: &DeleteOptions{},
  1124. wantAction: &deleteAction{
  1125. Key: "/foo",
  1126. PrevValue: "",
  1127. PrevIndex: 0,
  1128. Recursive: false,
  1129. },
  1130. },
  1131. // populated DeleteOptions
  1132. {
  1133. key: "/foo",
  1134. opts: &DeleteOptions{
  1135. PrevValue: "baz",
  1136. PrevIndex: 13,
  1137. Recursive: true,
  1138. },
  1139. wantAction: &deleteAction{
  1140. Key: "/foo",
  1141. PrevValue: "baz",
  1142. PrevIndex: 13,
  1143. Recursive: true,
  1144. },
  1145. },
  1146. }
  1147. for i, tt := range tests {
  1148. client := &actionAssertingHTTPClient{t: t, num: i, act: tt.wantAction}
  1149. kAPI := httpKeysAPI{client: client}
  1150. kAPI.Delete(context.Background(), tt.key, tt.opts)
  1151. }
  1152. }
  1153. func TestHTTPKeysAPIDeleteError(t *testing.T) {
  1154. tests := []httpClient{
  1155. // generic HTTP client failure
  1156. &staticHTTPClient{
  1157. err: errors.New("fail!"),
  1158. },
  1159. // unusable status code
  1160. &staticHTTPClient{
  1161. resp: http.Response{
  1162. StatusCode: http.StatusTeapot,
  1163. },
  1164. },
  1165. // etcd Error response
  1166. &staticHTTPClient{
  1167. resp: http.Response{
  1168. StatusCode: http.StatusInternalServerError,
  1169. },
  1170. body: []byte(`{"errorCode":300,"message":"Raft internal error","cause":"/foo","index":18}`),
  1171. },
  1172. }
  1173. for i, tt := range tests {
  1174. kAPI := httpKeysAPI{client: tt}
  1175. resp, err := kAPI.Delete(context.Background(), "/foo", nil)
  1176. if err == nil {
  1177. t.Errorf("#%d: received nil error", i)
  1178. }
  1179. if resp != nil {
  1180. t.Errorf("#%d: received non-nil Response: %#v", i, resp)
  1181. }
  1182. }
  1183. }
  1184. func TestHTTPKeysAPIDeleteResponse(t *testing.T) {
  1185. client := &staticHTTPClient{
  1186. resp: http.Response{
  1187. StatusCode: http.StatusOK,
  1188. Header: http.Header{"X-Etcd-Index": []string{"22"}},
  1189. },
  1190. body: []byte(`{"action":"delete","node":{"key":"/pants/foo/bar/baz","value":"snarf","modifiedIndex":22,"createdIndex":19},"prevNode":{"key":"/pants/foo/bar/baz","value":"snazz","modifiedIndex":20,"createdIndex":19}}`),
  1191. }
  1192. wantResponse := &Response{
  1193. Action: "delete",
  1194. Node: &Node{Key: "/pants/foo/bar/baz", Value: "snarf", CreatedIndex: uint64(19), ModifiedIndex: uint64(22)},
  1195. PrevNode: &Node{Key: "/pants/foo/bar/baz", Value: "snazz", CreatedIndex: uint64(19), ModifiedIndex: uint64(20)},
  1196. Index: uint64(22),
  1197. }
  1198. kAPI := &httpKeysAPI{client: client, prefix: "/pants"}
  1199. resp, err := kAPI.Delete(context.Background(), "/foo/bar/baz", nil)
  1200. if err != nil {
  1201. t.Errorf("non-nil error: %#v", err)
  1202. }
  1203. if !reflect.DeepEqual(wantResponse, resp) {
  1204. t.Errorf("incorrect Response: want=%#v got=%#v", wantResponse, resp)
  1205. }
  1206. }
  1207. func TestHTTPKeysAPICreateAction(t *testing.T) {
  1208. act := &setAction{
  1209. Key: "/foo",
  1210. Value: "bar",
  1211. PrevExist: PrevNoExist,
  1212. PrevIndex: 0,
  1213. PrevValue: "",
  1214. TTL: 0,
  1215. }
  1216. kAPI := httpKeysAPI{client: &actionAssertingHTTPClient{t: t, act: act}}
  1217. kAPI.Create(context.Background(), "/foo", "bar")
  1218. }
  1219. func TestHTTPKeysAPICreateInOrderAction(t *testing.T) {
  1220. act := &createInOrderAction{
  1221. Dir: "/foo",
  1222. Value: "bar",
  1223. TTL: 0,
  1224. }
  1225. kAPI := httpKeysAPI{client: &actionAssertingHTTPClient{t: t, act: act}}
  1226. kAPI.CreateInOrder(context.Background(), "/foo", "bar", nil)
  1227. }
  1228. func TestHTTPKeysAPIUpdateAction(t *testing.T) {
  1229. act := &setAction{
  1230. Key: "/foo",
  1231. Value: "bar",
  1232. PrevExist: PrevExist,
  1233. PrevIndex: 0,
  1234. PrevValue: "",
  1235. TTL: 0,
  1236. }
  1237. kAPI := httpKeysAPI{client: &actionAssertingHTTPClient{t: t, act: act}}
  1238. kAPI.Update(context.Background(), "/foo", "bar")
  1239. }
  1240. func TestNodeTTLDuration(t *testing.T) {
  1241. tests := []struct {
  1242. node *Node
  1243. want time.Duration
  1244. }{
  1245. {
  1246. node: &Node{TTL: 0},
  1247. want: 0,
  1248. },
  1249. {
  1250. node: &Node{TTL: 97},
  1251. want: 97 * time.Second,
  1252. },
  1253. }
  1254. for i, tt := range tests {
  1255. got := tt.node.TTLDuration()
  1256. if tt.want != got {
  1257. t.Errorf("#%d: incorrect duration: want=%v got=%v", i, tt.want, got)
  1258. }
  1259. }
  1260. }