keys_test.go 30 KB

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