http_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. package etcdhttp
  2. import (
  3. "bytes"
  4. "errors"
  5. "io"
  6. "net/http"
  7. "net/http/httptest"
  8. "net/url"
  9. "path"
  10. "reflect"
  11. "strings"
  12. "sync"
  13. "testing"
  14. "time"
  15. etcdErr "github.com/coreos/etcd/error"
  16. "github.com/coreos/etcd/etcdserver"
  17. "github.com/coreos/etcd/etcdserver/etcdserverpb"
  18. "github.com/coreos/etcd/raft/raftpb"
  19. "github.com/coreos/etcd/store"
  20. "github.com/coreos/etcd/third_party/code.google.com/p/go.net/context"
  21. )
  22. func boolp(b bool) *bool { return &b }
  23. func mustNewURL(t *testing.T, s string) *url.URL {
  24. u, err := url.Parse(s)
  25. if err != nil {
  26. t.Fatalf("error creating URL from %q: %v", s, err)
  27. }
  28. return u
  29. }
  30. // mustNewRequest takes a path, appends it to the standard keysPrefix, and constructs
  31. // an *http.Request referencing the resulting URL
  32. func mustNewRequest(t *testing.T, p string) *http.Request {
  33. return &http.Request{
  34. URL: mustNewURL(t, path.Join(keysPrefix, p)),
  35. }
  36. }
  37. // mustNewForm takes a set of Values and constructs a PUT *http.Request,
  38. // with a URL constructed from appending the given path to the standard keysPrefix
  39. func mustNewForm(t *testing.T, p string, vals url.Values) *http.Request {
  40. u := mustNewURL(t, path.Join(keysPrefix, p))
  41. req, err := http.NewRequest("PUT", u.String(), strings.NewReader(vals.Encode()))
  42. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  43. if err != nil {
  44. t.Fatalf("error creating new request: %v", err)
  45. }
  46. return req
  47. }
  48. func TestBadParseRequest(t *testing.T) {
  49. tests := []struct {
  50. in *http.Request
  51. wcode int
  52. }{
  53. {
  54. // parseForm failure
  55. &http.Request{
  56. Body: nil,
  57. Method: "PUT",
  58. },
  59. etcdErr.EcodeInvalidForm,
  60. },
  61. {
  62. // bad key prefix
  63. &http.Request{
  64. URL: mustNewURL(t, "/badprefix/"),
  65. },
  66. etcdErr.EcodeInvalidForm,
  67. },
  68. // bad values for prevIndex, waitIndex, ttl
  69. {
  70. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"garbage"}}),
  71. etcdErr.EcodeIndexNaN,
  72. },
  73. {
  74. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"1.5"}}),
  75. etcdErr.EcodeIndexNaN,
  76. },
  77. {
  78. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"-1"}}),
  79. etcdErr.EcodeIndexNaN,
  80. },
  81. {
  82. mustNewForm(t, "foo", url.Values{"waitIndex": []string{"garbage"}}),
  83. etcdErr.EcodeIndexNaN,
  84. },
  85. {
  86. mustNewForm(t, "foo", url.Values{"waitIndex": []string{"??"}}),
  87. etcdErr.EcodeIndexNaN,
  88. },
  89. {
  90. mustNewForm(t, "foo", url.Values{"ttl": []string{"-1"}}),
  91. etcdErr.EcodeTTLNaN,
  92. },
  93. // bad values for recursive, sorted, wait, prevExists
  94. {
  95. mustNewForm(t, "foo", url.Values{"recursive": []string{"hahaha"}}),
  96. etcdErr.EcodeInvalidField,
  97. },
  98. {
  99. mustNewForm(t, "foo", url.Values{"recursive": []string{"1234"}}),
  100. etcdErr.EcodeInvalidField,
  101. },
  102. {
  103. mustNewForm(t, "foo", url.Values{"recursive": []string{"?"}}),
  104. etcdErr.EcodeInvalidField,
  105. },
  106. {
  107. mustNewForm(t, "foo", url.Values{"sorted": []string{"?"}}),
  108. etcdErr.EcodeInvalidField,
  109. },
  110. {
  111. mustNewForm(t, "foo", url.Values{"sorted": []string{"x"}}),
  112. etcdErr.EcodeInvalidField,
  113. },
  114. {
  115. mustNewForm(t, "foo", url.Values{"wait": []string{"?!"}}),
  116. etcdErr.EcodeInvalidField,
  117. },
  118. {
  119. mustNewForm(t, "foo", url.Values{"wait": []string{"yes"}}),
  120. etcdErr.EcodeInvalidField,
  121. },
  122. {
  123. mustNewForm(t, "foo", url.Values{"prevExists": []string{"yes"}}),
  124. etcdErr.EcodeInvalidField,
  125. },
  126. {
  127. mustNewForm(t, "foo", url.Values{"prevExists": []string{"#2"}}),
  128. etcdErr.EcodeInvalidField,
  129. },
  130. // query values are considered
  131. {
  132. mustNewRequest(t, "foo?prevExists=wrong"),
  133. etcdErr.EcodeInvalidField,
  134. },
  135. {
  136. mustNewRequest(t, "foo?ttl=wrong"),
  137. etcdErr.EcodeTTLNaN,
  138. },
  139. // but body takes precedence if both are specified
  140. {
  141. mustNewForm(
  142. t,
  143. "foo?ttl=12",
  144. url.Values{"ttl": []string{"garbage"}},
  145. ),
  146. etcdErr.EcodeTTLNaN,
  147. },
  148. {
  149. mustNewForm(
  150. t,
  151. "foo?prevExists=false",
  152. url.Values{"prevExists": []string{"yes"}},
  153. ),
  154. etcdErr.EcodeInvalidField,
  155. },
  156. }
  157. for i, tt := range tests {
  158. got, err := parseRequest(tt.in, 1234)
  159. if err == nil {
  160. t.Errorf("#%d: unexpected nil error!", i)
  161. continue
  162. }
  163. ee, ok := err.(*etcdErr.Error)
  164. if !ok {
  165. t.Errorf("#%d: err is not etcd.Error!", i)
  166. continue
  167. }
  168. if ee.ErrorCode != tt.wcode {
  169. t.Errorf("#%d: code=%d, want %v", i, ee.ErrorCode, tt.wcode)
  170. t.Logf("cause: %#v", ee.Cause)
  171. }
  172. if !reflect.DeepEqual(got, etcdserverpb.Request{}) {
  173. t.Errorf("#%d: unexpected non-empty Request: %#v", i, got)
  174. }
  175. }
  176. }
  177. func TestGoodParseRequest(t *testing.T) {
  178. tests := []struct {
  179. in *http.Request
  180. w etcdserverpb.Request
  181. }{
  182. {
  183. // good prefix, all other values default
  184. mustNewRequest(t, "foo"),
  185. etcdserverpb.Request{
  186. Id: 1234,
  187. Path: "/foo",
  188. },
  189. },
  190. {
  191. // value specified
  192. mustNewForm(
  193. t,
  194. "foo",
  195. url.Values{"value": []string{"some_value"}},
  196. ),
  197. etcdserverpb.Request{
  198. Id: 1234,
  199. Method: "PUT",
  200. Val: "some_value",
  201. Path: "/foo",
  202. },
  203. },
  204. {
  205. // prevIndex specified
  206. mustNewForm(
  207. t,
  208. "foo",
  209. url.Values{"prevIndex": []string{"98765"}},
  210. ),
  211. etcdserverpb.Request{
  212. Id: 1234,
  213. Method: "PUT",
  214. PrevIndex: 98765,
  215. Path: "/foo",
  216. },
  217. },
  218. {
  219. // recursive specified
  220. mustNewForm(
  221. t,
  222. "foo",
  223. url.Values{"recursive": []string{"true"}},
  224. ),
  225. etcdserverpb.Request{
  226. Id: 1234,
  227. Method: "PUT",
  228. Recursive: true,
  229. Path: "/foo",
  230. },
  231. },
  232. {
  233. // sorted specified
  234. mustNewForm(
  235. t,
  236. "foo",
  237. url.Values{"sorted": []string{"true"}},
  238. ),
  239. etcdserverpb.Request{
  240. Id: 1234,
  241. Method: "PUT",
  242. Sorted: true,
  243. Path: "/foo",
  244. },
  245. },
  246. {
  247. // wait specified
  248. mustNewForm(
  249. t,
  250. "foo",
  251. url.Values{"wait": []string{"true"}},
  252. ),
  253. etcdserverpb.Request{
  254. Id: 1234,
  255. Method: "PUT",
  256. Wait: true,
  257. Path: "/foo",
  258. },
  259. },
  260. {
  261. // prevExists should be non-null if specified
  262. mustNewForm(
  263. t,
  264. "foo",
  265. url.Values{"prevExists": []string{"true"}},
  266. ),
  267. etcdserverpb.Request{
  268. Id: 1234,
  269. Method: "PUT",
  270. PrevExists: boolp(true),
  271. Path: "/foo",
  272. },
  273. },
  274. {
  275. // prevExists should be non-null if specified
  276. mustNewForm(
  277. t,
  278. "foo",
  279. url.Values{"prevExists": []string{"false"}},
  280. ),
  281. etcdserverpb.Request{
  282. Id: 1234,
  283. Method: "PUT",
  284. PrevExists: boolp(false),
  285. Path: "/foo",
  286. },
  287. },
  288. // mix various fields
  289. {
  290. mustNewForm(
  291. t,
  292. "foo",
  293. url.Values{
  294. "value": []string{"some value"},
  295. "prevExists": []string{"true"},
  296. "prevValue": []string{"previous value"},
  297. },
  298. ),
  299. etcdserverpb.Request{
  300. Id: 1234,
  301. Method: "PUT",
  302. PrevExists: boolp(true),
  303. PrevValue: "previous value",
  304. Val: "some value",
  305. Path: "/foo",
  306. },
  307. },
  308. // query parameters should be used if given
  309. {
  310. mustNewForm(
  311. t,
  312. "foo?prevValue=woof",
  313. url.Values{},
  314. ),
  315. etcdserverpb.Request{
  316. Id: 1234,
  317. Method: "PUT",
  318. PrevValue: "woof",
  319. Path: "/foo",
  320. },
  321. },
  322. // but form values should take precedence over query parameters
  323. {
  324. mustNewForm(
  325. t,
  326. "foo?prevValue=woof",
  327. url.Values{
  328. "prevValue": []string{"miaow"},
  329. },
  330. ),
  331. etcdserverpb.Request{
  332. Id: 1234,
  333. Method: "PUT",
  334. PrevValue: "miaow",
  335. Path: "/foo",
  336. },
  337. },
  338. }
  339. for i, tt := range tests {
  340. got, err := parseRequest(tt.in, 1234)
  341. if err != nil {
  342. t.Errorf("#%d: err = %v, want %v", i, err, nil)
  343. }
  344. if !reflect.DeepEqual(got, tt.w) {
  345. t.Errorf("#%d: request=%#v, want %#v", i, got, tt.w)
  346. }
  347. }
  348. }
  349. // eventingWatcher immediately returns a simple event of the given action on its channel
  350. type eventingWatcher struct {
  351. action string
  352. }
  353. func (w *eventingWatcher) EventChan() chan *store.Event {
  354. ch := make(chan *store.Event)
  355. go func() {
  356. ch <- &store.Event{
  357. Action: w.action,
  358. Node: &store.NodeExtern{},
  359. }
  360. }()
  361. return ch
  362. }
  363. func (w *eventingWatcher) Remove() {}
  364. func TestWriteError(t *testing.T) {
  365. // nil error should not panic
  366. rw := httptest.NewRecorder()
  367. writeError(rw, nil)
  368. h := rw.Header()
  369. if len(h) > 0 {
  370. t.Fatalf("unexpected non-empty headers: %#v", h)
  371. }
  372. b := rw.Body.String()
  373. if len(b) > 0 {
  374. t.Fatalf("unexpected non-empty body: %q", b)
  375. }
  376. tests := []struct {
  377. err error
  378. wcode int
  379. wi string
  380. }{
  381. {
  382. etcdErr.NewError(etcdErr.EcodeKeyNotFound, "/foo/bar", 123),
  383. http.StatusNotFound,
  384. "123",
  385. },
  386. {
  387. etcdErr.NewError(etcdErr.EcodeTestFailed, "/foo/bar", 456),
  388. http.StatusPreconditionFailed,
  389. "456",
  390. },
  391. {
  392. err: errors.New("something went wrong"),
  393. wcode: http.StatusInternalServerError,
  394. },
  395. }
  396. for i, tt := range tests {
  397. rw := httptest.NewRecorder()
  398. writeError(rw, tt.err)
  399. if code := rw.Code; code != tt.wcode {
  400. t.Errorf("#%d: code=%d, want %d", i, code, tt.wcode)
  401. }
  402. if idx := rw.Header().Get("X-Etcd-Index"); idx != tt.wi {
  403. t.Errorf("#%d: X-Etcd-Index=%q, want %q", i, idx, tt.wi)
  404. }
  405. }
  406. }
  407. func TestWriteEvent(t *testing.T) {
  408. // nil event should not panic
  409. rw := httptest.NewRecorder()
  410. writeEvent(rw, nil)
  411. h := rw.Header()
  412. if len(h) > 0 {
  413. t.Fatalf("unexpected non-empty headers: %#v", h)
  414. }
  415. b := rw.Body.String()
  416. if len(b) > 0 {
  417. t.Fatalf("unexpected non-empty body: %q", b)
  418. }
  419. tests := []struct {
  420. ev *store.Event
  421. idx string
  422. code int
  423. err error
  424. }{
  425. // standard case, standard 200 response
  426. {
  427. &store.Event{
  428. Action: store.Get,
  429. Node: &store.NodeExtern{},
  430. PrevNode: &store.NodeExtern{},
  431. },
  432. "0",
  433. http.StatusOK,
  434. nil,
  435. },
  436. // check new nodes return StatusCreated
  437. {
  438. &store.Event{
  439. Action: store.Create,
  440. Node: &store.NodeExtern{},
  441. PrevNode: &store.NodeExtern{},
  442. },
  443. "0",
  444. http.StatusCreated,
  445. nil,
  446. },
  447. }
  448. for i, tt := range tests {
  449. rw := httptest.NewRecorder()
  450. writeEvent(rw, tt.ev)
  451. if gct := rw.Header().Get("Content-Type"); gct != "application/json" {
  452. t.Errorf("case %d: bad Content-Type: got %q, want application/json", i, gct)
  453. }
  454. if gei := rw.Header().Get("X-Etcd-Index"); gei != tt.idx {
  455. t.Errorf("case %d: bad X-Etcd-Index header: got %s, want %s", i, gei, tt.idx)
  456. }
  457. if rw.Code != tt.code {
  458. t.Errorf("case %d: bad response code: got %d, want %v", i, rw.Code, tt.code)
  459. }
  460. }
  461. }
  462. type dummyWatcher struct {
  463. echan chan *store.Event
  464. }
  465. func (w *dummyWatcher) EventChan() chan *store.Event {
  466. return w.echan
  467. }
  468. func (w *dummyWatcher) Remove() {}
  469. type dummyResponseWriter struct {
  470. cnchan chan bool
  471. http.ResponseWriter
  472. }
  473. func (rw *dummyResponseWriter) CloseNotify() <-chan bool {
  474. return rw.cnchan
  475. }
  476. func TestWaitForEventChan(t *testing.T) {
  477. ctx := context.Background()
  478. ec := make(chan *store.Event)
  479. dw := &dummyWatcher{
  480. echan: ec,
  481. }
  482. w := httptest.NewRecorder()
  483. var wg sync.WaitGroup
  484. var ev *store.Event
  485. var err error
  486. wg.Add(1)
  487. go func() {
  488. ev, err = waitForEvent(ctx, w, dw)
  489. wg.Done()
  490. }()
  491. ec <- &store.Event{
  492. Action: store.Get,
  493. Node: &store.NodeExtern{
  494. Key: "/foo/bar",
  495. ModifiedIndex: 12345,
  496. },
  497. }
  498. wg.Wait()
  499. want := &store.Event{
  500. Action: store.Get,
  501. Node: &store.NodeExtern{
  502. Key: "/foo/bar",
  503. ModifiedIndex: 12345,
  504. },
  505. }
  506. if !reflect.DeepEqual(ev, want) {
  507. t.Fatalf("bad event: got %#v, want %#v", ev, want)
  508. }
  509. if err != nil {
  510. t.Fatalf("unexpected error: %v", err)
  511. }
  512. }
  513. func TestWaitForEventCloseNotify(t *testing.T) {
  514. ctx := context.Background()
  515. dw := &dummyWatcher{}
  516. cnchan := make(chan bool)
  517. w := &dummyResponseWriter{
  518. cnchan: cnchan,
  519. }
  520. var wg sync.WaitGroup
  521. var ev *store.Event
  522. var err error
  523. wg.Add(1)
  524. go func() {
  525. ev, err = waitForEvent(ctx, w, dw)
  526. wg.Done()
  527. }()
  528. close(cnchan)
  529. wg.Wait()
  530. if ev != nil {
  531. t.Fatalf("non-nil Event returned with CloseNotifier: %v", ev)
  532. }
  533. if err == nil {
  534. t.Fatalf("nil err returned with CloseNotifier!")
  535. }
  536. }
  537. func TestWaitForEventCancelledContext(t *testing.T) {
  538. cctx, cancel := context.WithCancel(context.Background())
  539. dw := &dummyWatcher{}
  540. w := httptest.NewRecorder()
  541. var wg sync.WaitGroup
  542. var ev *store.Event
  543. var err error
  544. wg.Add(1)
  545. go func() {
  546. ev, err = waitForEvent(cctx, w, dw)
  547. wg.Done()
  548. }()
  549. cancel()
  550. wg.Wait()
  551. if ev != nil {
  552. t.Fatalf("non-nil Event returned with cancelled context: %v", ev)
  553. }
  554. if err == nil {
  555. t.Fatalf("nil err returned with cancelled context!")
  556. }
  557. }
  558. func TestV2MachinesEndpoint(t *testing.T) {
  559. tests := []struct {
  560. method string
  561. wcode int
  562. }{
  563. {"GET", http.StatusOK},
  564. {"HEAD", http.StatusOK},
  565. {"POST", http.StatusMethodNotAllowed},
  566. }
  567. m := NewHandler(nil, Peers{}, time.Hour)
  568. s := httptest.NewServer(m)
  569. defer s.Close()
  570. for _, tt := range tests {
  571. req, err := http.NewRequest(tt.method, s.URL+machinesPrefix, nil)
  572. if err != nil {
  573. t.Fatal(err)
  574. }
  575. resp, err := http.DefaultClient.Do(req)
  576. if err != nil {
  577. t.Fatal(err)
  578. }
  579. if resp.StatusCode != tt.wcode {
  580. t.Errorf("StatusCode = %d, expected %d", resp.StatusCode, tt.wcode)
  581. }
  582. }
  583. }
  584. func TestServeMachines(t *testing.T) {
  585. peers := Peers{}
  586. peers.Set("0xBEEF0=localhost:8080&0xBEEF1=localhost:8081&0xBEEF2=localhost:8082")
  587. writer := httptest.NewRecorder()
  588. req, err := http.NewRequest("GET", "", nil)
  589. if err != nil {
  590. t.Fatal(err)
  591. }
  592. h := &serverHandler{peers: peers}
  593. h.serveMachines(writer, req)
  594. w := "http://localhost:8080, http://localhost:8081, http://localhost:8082"
  595. if g := writer.Body.String(); g != w {
  596. t.Errorf("body = %s, want %s", g, w)
  597. }
  598. if writer.Code != http.StatusOK {
  599. t.Errorf("header = %d, want %d", writer.Code, http.StatusOK)
  600. }
  601. }
  602. func TestPeersEndpoints(t *testing.T) {
  603. tests := []struct {
  604. peers Peers
  605. endpoints []string
  606. }{
  607. // single peer with a single address
  608. {
  609. peers: Peers(map[int64][]string{
  610. 1: []string{"192.0.2.1"},
  611. }),
  612. endpoints: []string{"http://192.0.2.1"},
  613. },
  614. // single peer with a single address with a port
  615. {
  616. peers: Peers(map[int64][]string{
  617. 1: []string{"192.0.2.1:8001"},
  618. }),
  619. endpoints: []string{"http://192.0.2.1:8001"},
  620. },
  621. // several peers explicitly unsorted
  622. {
  623. peers: Peers(map[int64][]string{
  624. 2: []string{"192.0.2.3", "192.0.2.4"},
  625. 3: []string{"192.0.2.5", "192.0.2.6"},
  626. 1: []string{"192.0.2.1", "192.0.2.2"},
  627. }),
  628. endpoints: []string{"http://192.0.2.1", "http://192.0.2.2", "http://192.0.2.3", "http://192.0.2.4", "http://192.0.2.5", "http://192.0.2.6"},
  629. },
  630. // no peers
  631. {
  632. peers: Peers(map[int64][]string{}),
  633. endpoints: []string{},
  634. },
  635. // peer with no endpoints
  636. {
  637. peers: Peers(map[int64][]string{
  638. 3: []string{},
  639. }),
  640. endpoints: []string{},
  641. },
  642. }
  643. for i, tt := range tests {
  644. endpoints := tt.peers.Endpoints()
  645. if !reflect.DeepEqual(tt.endpoints, endpoints) {
  646. t.Errorf("#%d: peers.Endpoints() incorrect: want=%#v got=%#v", i, tt.endpoints, endpoints)
  647. }
  648. }
  649. }
  650. func TestAllowMethod(t *testing.T) {
  651. tests := []struct {
  652. m string
  653. ms []string
  654. w bool
  655. wh string
  656. }{
  657. // Accepted methods
  658. {
  659. m: "GET",
  660. ms: []string{"GET", "POST", "PUT"},
  661. w: true,
  662. },
  663. {
  664. m: "POST",
  665. ms: []string{"POST"},
  666. w: true,
  667. },
  668. // Made-up methods no good
  669. {
  670. m: "FAKE",
  671. ms: []string{"GET", "POST", "PUT"},
  672. w: false,
  673. wh: "GET,POST,PUT",
  674. },
  675. // Empty methods no good
  676. {
  677. m: "",
  678. ms: []string{"GET", "POST"},
  679. w: false,
  680. wh: "GET,POST",
  681. },
  682. // Empty accepted methods no good
  683. {
  684. m: "GET",
  685. ms: []string{""},
  686. w: false,
  687. wh: "",
  688. },
  689. // No methods accepted
  690. {
  691. m: "GET",
  692. ms: []string{},
  693. w: false,
  694. wh: "",
  695. },
  696. }
  697. for i, tt := range tests {
  698. rw := httptest.NewRecorder()
  699. g := allowMethod(rw, tt.m, tt.ms...)
  700. if g != tt.w {
  701. t.Errorf("#%d: got allowMethod()=%t, want %t", i, g, tt.w)
  702. }
  703. if !tt.w {
  704. if rw.Code != http.StatusMethodNotAllowed {
  705. t.Errorf("#%d: code=%d, want %d", i, rw.Code, http.StatusMethodNotAllowed)
  706. }
  707. gh := rw.Header().Get("Allow")
  708. if gh != tt.wh {
  709. t.Errorf("#%d: Allow header=%q, want %q", i, gh, tt.wh)
  710. }
  711. }
  712. }
  713. }
  714. // errServer implements the etcd.Server interface for testing.
  715. // It returns the given error from any Do/Process calls.
  716. type errServer struct {
  717. err error
  718. }
  719. func (fs *errServer) Do(ctx context.Context, r etcdserverpb.Request) (etcdserver.Response, error) {
  720. return etcdserver.Response{}, fs.err
  721. }
  722. func (fs *errServer) Process(ctx context.Context, m raftpb.Message) error {
  723. return fs.err
  724. }
  725. func (fs *errServer) Start() {}
  726. func (fs *errServer) Stop() {}
  727. // errReader implements io.Reader to facilitate a broken request.
  728. type errReader struct{}
  729. func (er *errReader) Read(_ []byte) (int, error) { return 0, errors.New("some error") }
  730. func mustMarshalMsg(t *testing.T, m raftpb.Message) []byte {
  731. json, err := m.Marshal()
  732. if err != nil {
  733. t.Fatalf("error marshalling raft Message: %#v", err)
  734. }
  735. return json
  736. }
  737. func TestServeRaft(t *testing.T) {
  738. testCases := []struct {
  739. reqBody io.Reader
  740. serverErr error
  741. wcode int
  742. }{
  743. {
  744. &errReader{},
  745. nil,
  746. http.StatusBadRequest,
  747. },
  748. {
  749. strings.NewReader("malformed garbage"),
  750. nil,
  751. http.StatusBadRequest,
  752. },
  753. {
  754. bytes.NewReader(
  755. mustMarshalMsg(
  756. t,
  757. raftpb.Message{},
  758. ),
  759. ),
  760. errors.New("some error"),
  761. http.StatusInternalServerError,
  762. },
  763. {
  764. bytes.NewReader(
  765. mustMarshalMsg(
  766. t,
  767. raftpb.Message{},
  768. ),
  769. ),
  770. nil,
  771. http.StatusNoContent,
  772. },
  773. }
  774. for i, tt := range testCases {
  775. req, err := http.NewRequest("POST", "foo", tt.reqBody)
  776. if err != nil {
  777. t.Fatalf("#%d: could not create request: %#v", i, err)
  778. }
  779. h := &serverHandler{
  780. timeout: time.Hour,
  781. server: &errServer{tt.serverErr},
  782. peers: nil,
  783. }
  784. rw := httptest.NewRecorder()
  785. h.serveRaft(rw, req)
  786. if rw.Code != tt.wcode {
  787. t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
  788. }
  789. }
  790. }