1 package httpmock_test
2
3 import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "errors"
8 "fmt"
9 "net"
10 "net/http"
11 "net/url"
12 "regexp"
13 "testing"
14 "time"
15
16 "github.com/maxatome/go-testdeep/td"
17
18 . "github.com/jarcoal/httpmock"
19 "github.com/jarcoal/httpmock/internal"
20 )
21
22 const testURL = "http://www.example.com/"
23
24 func TestMockTransport(t *testing.T) {
25 Activate()
26 defer Deactivate()
27
28 url := "https://github.com/foo/bar"
29 body := `["hello world"]` + "\n"
30
31 RegisterResponder("GET", url, NewStringResponder(200, body))
32 RegisterResponder("GET", `=~/xxx\z`, NewStringResponder(200, body))
33
34 assert := td.Assert(t)
35
36
37
38 assert.RunAssertRequire("simple", func(assert, require *td.T) {
39 resp, err := http.Get(url)
40 require.CmpNoError(err)
41 assertBody(assert, resp, body)
42
43
44 _, err = http.Get(testURL)
45 assert.HasSuffix(err, NoResponderFound.Error())
46
47
48 req, err := http.NewRequest("Get", url, nil)
49 require.CmpNoError(err)
50
51 c := http.Client{}
52 _, err = c.Do(req)
53 assert.HasSuffix(err, NoResponderFound.Error()+` for method "Get", but one matches method "GET"`)
54
55
56 req, err = http.NewRequest("POST", url, nil)
57 require.CmpNoError(err)
58
59 _, err = c.Do(req)
60 assert.HasSuffix(err, NoResponderFound.Error()+` for method "POST", but one matches method "GET"`)
61
62
63 req, err = http.NewRequest("POST", "http://pipo.com/xxx", nil)
64 require.CmpNoError(err)
65
66 _, err = c.Do(req)
67 assert.HasSuffix(err, NoResponderFound.Error()+` for method "POST", but one matches method "GET"`)
68
69
70 _, err = http.Get("https://github.com////foo//bar")
71 assert.HasSuffix(err, NoResponderFound.Error()+` for URL "https://github.com////foo//bar", but one matches URL "https://github.com/foo/bar"`)
72
73
74 _, err = http.Get("https://github.com/foo/bar/")
75 assert.HasSuffix(err, NoResponderFound.Error()+` for URL "https://github.com/foo/bar/", but one matches URL "https://github.com/foo/bar"`)
76 })
77
78
79
80 for i := 1; i <= 2; i++ {
81 assert.RunAssertRequire(fmt.Sprintf("try #%d", i), func(assert, require *td.T) {
82 resp, err := http.Get(url)
83 require.CmpNoError(err)
84 defer resp.Body.Close()
85
86 var res []string
87 err = json.NewDecoder(resp.Body).Decode(&res)
88 require.CmpNoError(err)
89
90 assert.Cmp(res, []string{"hello world"})
91 })
92 }
93 }
94
95
96
97 func TestMockTransportDefaultMethod(t *testing.T) {
98 assert, require := td.AssertRequire(t)
99
100 Activate()
101 defer Deactivate()
102
103 const urlString = "https://github.com/"
104 url, err := url.Parse(urlString)
105 require.CmpNoError(err)
106 body := "hello world"
107
108 RegisterResponder("GET", urlString, NewStringResponder(200, body))
109
110 req := &http.Request{
111 URL: url,
112
113 }
114
115 client := &http.Client{}
116 resp, err := client.Do(req)
117 require.CmpNoError(err)
118
119 assertBody(assert, resp, body)
120 }
121
122 func TestMockTransportReset(t *testing.T) {
123 DeactivateAndReset()
124
125 td.CmpZero(t, DefaultTransport.NumResponders(),
126 "expected no responders at this point")
127 td.Cmp(t, DefaultTransport.Responders(), []string{})
128
129 r := NewStringResponder(200, "hey")
130
131 RegisterResponder("GET", testURL, r)
132 RegisterResponder("POST", testURL, r)
133 RegisterResponder("PATCH", testURL, r)
134 RegisterResponder("GET", "/pipo/bingo", r)
135
136 RegisterResponder("GET", "=~/pipo/bingo", r)
137 RegisterResponder("GET", "=~/bingo/pipo", r)
138
139 td.Cmp(t, DefaultTransport.NumResponders(), 6, "expected one responder")
140 td.Cmp(t, DefaultTransport.Responders(), []string{
141
142 "GET /pipo/bingo",
143 "GET " + testURL,
144 "PATCH " + testURL,
145 "POST " + testURL,
146
147 "GET =~/pipo/bingo",
148 "GET =~/bingo/pipo",
149 })
150
151 Reset()
152
153 td.CmpZero(t, DefaultTransport.NumResponders(),
154 "expected no responders as they were just reset")
155 td.Cmp(t, DefaultTransport.Responders(), []string{})
156 }
157
158 func TestMockTransportNoResponder(t *testing.T) {
159 Activate()
160 defer DeactivateAndReset()
161
162 Reset()
163
164 _, err := http.Get(testURL)
165 td.CmpError(t, err, "expected to receive a connection error due to lack of responders")
166
167 RegisterNoResponder(NewStringResponder(200, "hello world"))
168
169 resp, err := http.Get(testURL)
170 if td.CmpNoError(t, err, "expected request to succeed") {
171 assertBody(t, resp, "hello world")
172 }
173
174
175 RegisterNoResponder(NewNotFoundResponder(nil))
176 _, err = http.Get(testURL)
177 td.CmpHasSuffix(t, err, "Responder not found for GET http://www.example.com/")
178
179 const url = "http://www.example.com/foo/bar"
180 RegisterResponder("POST", url, NewStringResponder(200, "hello world"))
181
182
183 _, err = http.Get(url)
184 td.CmpHasSuffix(t, err, `Responder not found for GET `+url+`, but one matches method "POST"`)
185
186
187 _, err = http.Post(url+"/", "", nil)
188 td.CmpHasSuffix(t, err, `Responder not found for POST `+url+`/, but one matches URL "`+url+`"`)
189
190
191 _, err = http.Post("http://www.example.com///foo//bar", "", nil)
192 td.CmpHasSuffix(t, err, `Responder not found for POST http://www.example.com///foo//bar, but one matches URL "`+url+`"`)
193 }
194
195 func TestMockTransportQuerystringFallback(t *testing.T) {
196 assert := td.Assert(t)
197
198 Activate()
199 defer DeactivateAndReset()
200
201
202 RegisterResponder("GET", testURL, NewStringResponder(200, "hello world"))
203
204 for _, suffix := range []string{"?", "?hello=world", "?hello=world#foo", "?hello=world&hello=all", "#foo"} {
205 assert.RunAssertRequire(suffix, func(assert, require *td.T) {
206 reqURL := testURL + suffix
207
208
209 resp, err := http.Get(reqURL)
210 require.CmpNoError(err)
211
212 assertBody(assert, resp, "hello world")
213 })
214 }
215 }
216
217 func TestMockTransportPathOnlyFallback(t *testing.T) {
218
219 defer DeactivateAndReset()
220
221 for _, test := range []struct {
222 Responder string
223 Paths []string
224 }{
225 {
226
227 Responder: "/hello/world?query=string&abc=zz#fragment",
228 Paths: []string{
229 testURL + "hello/world?query=string&abc=zz#fragment",
230 },
231 },
232 {
233
234 Responder: "/hello/world?abc=zz&query=string#fragment",
235 Paths: []string{
236 testURL + "hello/world?query=string&abc=zz#fragment",
237 testURL + "hello/world?abc=zz&query=string#fragment",
238 },
239 },
240 {
241
242 Responder: "/hello/world?query=string&abc=zz",
243 Paths: []string{
244 testURL + "hello/world?query=string&abc=zz",
245 },
246 },
247 {
248
249 Responder: "/hello/world?abc=zz&query=string",
250 Paths: []string{
251 testURL + "hello/world?query=string&abc=zz",
252 testURL + "hello/world?abc=zz&query=string",
253 },
254 },
255 {
256
257 Responder: "/hello/world?query=string&query=string2&abc=zz",
258 Paths: []string{
259 testURL + "hello/world?query=string&query=string2&abc=zz",
260 },
261 },
262
263 {
264 Responder: "/hello/world?abc=zz&query=string&query=string2",
265 Paths: []string{
266 testURL + "hello/world?query=string&query=string2&abc=zz",
267 testURL + "hello/world?query=string2&query=string&abc=zz",
268 testURL + "hello/world?abc=zz&query=string2&query=string",
269 },
270 },
271 {
272 Responder: "/hello/world?query",
273 Paths: []string{
274 testURL + "hello/world?query",
275 },
276 },
277 {
278 Responder: "/hello/world?query&abc",
279 Paths: []string{
280 testURL + "hello/world?query&abc",
281
282 },
283 },
284 {
285
286
287 Responder: "/hello/world?abc=&query=",
288 Paths: []string{
289 testURL + "hello/world?query&abc",
290 testURL + "hello/world?abc&query",
291 },
292 },
293 {
294 Responder: "/hello/world#fragment",
295 Paths: []string{
296 testURL + "hello/world#fragment",
297 },
298 },
299 {
300 Responder: "/hello/world",
301 Paths: []string{
302 testURL + "hello/world?query=string&abc=zz#fragment",
303 testURL + "hello/world?query=string&abc=zz",
304 testURL + "hello/world#fragment",
305 testURL + "hello/world",
306 },
307 },
308 {
309 Responder: "/hello%2fworl%64",
310 Paths: []string{
311 testURL + "hello%2fworl%64?query=string&abc=zz#fragment",
312 testURL + "hello%2fworl%64?query=string&abc=zz",
313 testURL + "hello%2fworl%64#fragment",
314 testURL + "hello%2fworl%64",
315 },
316 },
317
318 {
319 Responder: `=~^http://.*/hello/.*ld\z`,
320 Paths: []string{
321 testURL + "hello/world?query=string&abc=zz#fragment",
322 testURL + "hello/world?query=string&abc=zz",
323 testURL + "hello/world#fragment",
324 testURL + "hello/world",
325 },
326 },
327 {
328 Responder: `=~^http://.*/hello/.*ld(\z|[?#])`,
329 Paths: []string{
330 testURL + "hello/world?query=string&abc=zz#fragment",
331 testURL + "hello/world?query=string&abc=zz",
332 testURL + "hello/world#fragment",
333 testURL + "hello/world",
334 },
335 },
336 {
337 Responder: `=~^/hello/.*ld\z`,
338 Paths: []string{
339 testURL + "hello/world?query=string&abc=zz#fragment",
340 testURL + "hello/world?query=string&abc=zz",
341 testURL + "hello/world#fragment",
342 testURL + "hello/world",
343 },
344 },
345 {
346 Responder: `=~^/hello/.*ld(\z|[?#])`,
347 Paths: []string{
348 testURL + "hello/world?query=string&abc=zz#fragment",
349 testURL + "hello/world?query=string&abc=zz",
350 testURL + "hello/world#fragment",
351 testURL + "hello/world",
352 },
353 },
354 {
355 Responder: `=~abc=zz`,
356 Paths: []string{
357 testURL + "hello/world?query=string&abc=zz#fragment",
358 testURL + "hello/world?query=string&abc=zz",
359 },
360 },
361 } {
362 Activate()
363
364
365 RegisterResponder("GET", test.Responder, NewStringResponder(200, "hello world"))
366
367 for _, reqURL := range test.Paths {
368 t.Logf("%s: %s", test.Responder, reqURL)
369
370
371 resp, err := http.Get(reqURL)
372 if td.CmpNoError(t, err) {
373 assertBody(t, resp, "hello world")
374 }
375 }
376
377 DeactivateAndReset()
378 }
379 }
380
381 type dummyTripper struct{}
382
383 func (d *dummyTripper) RoundTrip(*http.Request) (*http.Response, error) {
384 return nil, nil
385 }
386
387 func TestMockTransportInitialTransport(t *testing.T) {
388 DeactivateAndReset()
389
390 tripper := &dummyTripper{}
391 http.DefaultTransport = tripper
392
393 Activate()
394
395 td.CmpNot(t, http.DefaultTransport, td.Shallow(tripper),
396 "expected http.DefaultTransport to be a mock transport")
397
398 Deactivate()
399
400 td.Cmp(t, http.DefaultTransport, td.Shallow(tripper),
401 "expected http.DefaultTransport to be dummy")
402 }
403
404 func TestMockTransportNonDefault(t *testing.T) {
405 assert, require := td.AssertRequire(t)
406
407
408 client := &http.Client{
409 Transport: &http.Transport{
410 Proxy: http.ProxyFromEnvironment,
411 Dial: (&net.Dialer{
412 Timeout: 60 * time.Second,
413 KeepAlive: 30 * time.Second,
414 }).Dial,
415 TLSHandshakeTimeout: 60 * time.Second,
416 },
417 }
418
419
420 ActivateNonDefault(client)
421 defer DeactivateAndReset()
422
423 body := "hello world!"
424
425 RegisterResponder("GET", testURL, NewStringResponder(200, body))
426
427 req, err := http.NewRequest("GET", testURL, nil)
428 require.CmpNoError(err)
429
430 resp, err := client.Do(req)
431 require.CmpNoError(err)
432
433 assertBody(assert, resp, body)
434 }
435
436 func TestMockTransportRespectsCancel(t *testing.T) {
437 assert := td.Assert(t)
438
439 Activate()
440 defer DeactivateAndReset()
441
442 const (
443 cancelNone = iota
444 cancelReq
445 cancelCtx
446 )
447
448 cases := []struct {
449 withCancel int
450 cancelNow bool
451 withPanic bool
452 expectedBody string
453 expectedErr error
454 }{
455
456 {cancelNone, false, false, "hello world", nil},
457
458
459 {cancelReq, true, false, "", errors.New("request canceled")},
460
461
462 {cancelCtx, true, false, "", errors.New("context canceled")},
463
464
465 {cancelReq, false, false, "hello world", nil},
466
467
468 {cancelCtx, false, false, "hello world", nil},
469
470
471 {cancelReq, false, true, "", errors.New(`panic in responder: got "oh no"`)},
472
473
474 {cancelCtx, false, true, "", errors.New(`panic in responder: got "oh no"`)},
475 }
476
477 for ic, c := range cases {
478 assert.RunAssertRequire(fmt.Sprintf("case #%d", ic), func(assert, require *td.T) {
479 Reset()
480 if c.withPanic {
481 RegisterResponder("GET", testURL, func(r *http.Request) (*http.Response, error) {
482 time.Sleep(10 * time.Millisecond)
483 panic("oh no")
484 })
485 } else {
486 RegisterResponder("GET", testURL, func(r *http.Request) (*http.Response, error) {
487 time.Sleep(10 * time.Millisecond)
488 return NewStringResponse(http.StatusOK, "hello world"), nil
489 })
490 }
491
492 req, err := http.NewRequest("GET", testURL, nil)
493 require.CmpNoError(err)
494
495 switch c.withCancel {
496 case cancelReq:
497 cancel := make(chan struct{}, 1)
498 req.Cancel = cancel
499 if c.cancelNow {
500 cancel <- struct{}{}
501 }
502 case cancelCtx:
503 ctx, cancel := context.WithCancel(req.Context())
504 req = req.WithContext(ctx)
505 if c.cancelNow {
506 cancel()
507 } else {
508 defer cancel()
509 }
510 }
511
512 resp, err := http.DefaultClient.Do(req)
513
514 if c.expectedErr != nil {
515
516 assert.Cmp(err, td.Smuggle("Err", td.String(c.expectedErr.Error())))
517 } else {
518 assert.CmpNoError(err)
519 }
520
521 if c.expectedBody != "" {
522 assertBody(assert, resp, c.expectedBody)
523 }
524 })
525 }
526 }
527
528 func TestMockTransportRespectsTimeout(t *testing.T) {
529 timeout := time.Millisecond
530 client := &http.Client{
531 Timeout: timeout,
532 }
533
534 ActivateNonDefault(client)
535 defer DeactivateAndReset()
536
537 RegisterResponder(
538 "GET", testURL,
539 func(r *http.Request) (*http.Response, error) {
540 time.Sleep(100 * timeout)
541 return NewStringResponse(http.StatusOK, ""), nil
542 },
543 )
544
545 _, err := client.Get(testURL)
546 td.CmpError(t, err)
547 }
548
549 func TestMockTransportCallCountReset(t *testing.T) {
550 assert, require := td.AssertRequire(t)
551
552 Reset()
553 Activate()
554 defer Deactivate()
555
556 const (
557 url = "https://github.com/path?b=1&a=2"
558 url2 = "https://gitlab.com/"
559 )
560
561 RegisterResponder("GET", url, NewStringResponder(200, "body"))
562 RegisterResponder("POST", "=~gitlab", NewStringResponder(200, "body"))
563
564 _, err := http.Get(url)
565 require.CmpNoError(err)
566
567 buff := new(bytes.Buffer)
568 json.NewEncoder(buff).Encode("{}")
569 _, err = http.Post(url2, "application/json", buff)
570 require.CmpNoError(err)
571
572 _, err = http.Get(url)
573 require.CmpNoError(err)
574
575 assert.Cmp(GetTotalCallCount(), 3)
576 assert.Cmp(GetCallCountInfo(), map[string]int{
577 "GET " + url: 2,
578
579 "POST " + url2: 1,
580 "POST =~gitlab": 1,
581 })
582
583 Reset()
584
585 assert.Zero(GetTotalCallCount())
586 assert.Empty(GetCallCountInfo())
587 }
588
589 func TestMockTransportCallCountZero(t *testing.T) {
590 assert, require := td.AssertRequire(t)
591
592 Reset()
593 Activate()
594 defer Deactivate()
595
596 const (
597 url = "https://github.com/path?b=1&a=2"
598 url2 = "https://gitlab.com/"
599 )
600
601 RegisterResponder("GET", url, NewStringResponder(200, "body"))
602 RegisterResponder("POST", "=~gitlab", NewStringResponder(200, "body"))
603
604 _, err := http.Get(url)
605 require.CmpNoError(err)
606
607 buff := new(bytes.Buffer)
608 json.NewEncoder(buff).Encode("{}")
609 _, err = http.Post(url2, "application/json", buff)
610 require.CmpNoError(err)
611
612 _, err = http.Get(url)
613 require.CmpNoError(err)
614
615 assert.Cmp(GetTotalCallCount(), 3)
616 assert.Cmp(GetCallCountInfo(), map[string]int{
617 "GET " + url: 2,
618
619 "POST " + url2: 1,
620 "POST =~gitlab": 1,
621 })
622
623 ZeroCallCounters()
624
625 assert.Zero(GetTotalCallCount())
626 assert.Cmp(GetCallCountInfo(), map[string]int{
627 "GET " + url: 0,
628
629 "POST " + url2: 0,
630 "POST =~gitlab": 0,
631 })
632
633
634 RegisterResponder("GET", url, nil)
635 RegisterResponder("POST", "=~gitlab", nil)
636
637 assert.Cmp(GetCallCountInfo(), map[string]int{
638
639
640 "POST " + url2: 0,
641 })
642 }
643
644 func TestRegisterResponderWithQuery(t *testing.T) {
645 assert, require := td.AssertRequire(t)
646
647 Reset()
648
649
650 defer DeactivateAndReset()
651
652
653 client := &http.Client{
654 Transport: &http.Transport{
655 Proxy: http.ProxyFromEnvironment,
656 Dial: (&net.Dialer{
657 Timeout: 60 * time.Second,
658 KeepAlive: 30 * time.Second,
659 }).Dial,
660 TLSHandshakeTimeout: 60 * time.Second,
661 },
662 }
663
664 body := "hello world!"
665 testURLPath := "http://acme.test/api"
666
667 for _, test := range []struct {
668 URL string
669 Queries []interface{}
670 URLs []string
671 }{
672 {
673 Queries: []interface{}{
674 map[string]string{"a": "1", "b": "2"},
675 "a=1&b=2",
676 "b=2&a=1",
677 url.Values{"a": []string{"1"}, "b": []string{"2"}},
678 },
679 URLs: []string{
680 "http://acme.test/api?a=1&b=2",
681 "http://acme.test/api?b=2&a=1",
682 },
683 },
684 {
685 Queries: []interface{}{
686 url.Values{
687 "a": []string{"3", "2", "1"},
688 "b": []string{"4", "2"},
689 "c": []string{""},
690
691
692
693
694 },
695 "a=1&b=2&a=3&c&b=4&a=2",
696 "b=2&a=1&c=&b=4&a=2&a=3",
697 nil,
698 },
699 URLs: []string{
700 testURLPath + "?a=1&b=2&a=3&c&b=4&a=2",
701 testURLPath + "?a=1&b=2&a=3&c=&b=4&a=2",
702 testURLPath + "?b=2&a=1&c=&b=4&a=2&a=3",
703 testURLPath + "?b=2&a=1&c&b=4&a=2&a=3",
704 },
705 },
706 } {
707 for _, query := range test.Queries {
708 ActivateNonDefault(client)
709 RegisterResponderWithQuery("GET", testURLPath, query, NewStringResponder(200, body))
710
711 for _, url := range test.URLs {
712 assert.Logf("query=%v URL=%s", query, url)
713
714 req, err := http.NewRequest("GET", url, nil)
715 require.CmpNoError(err)
716
717 resp, err := client.Do(req)
718 require.CmpNoError(err)
719
720 assertBody(assert, resp, body)
721 }
722
723 if info := GetCallCountInfo(); len(info) != 1 {
724 t.Fatalf("%s: len(GetCallCountInfo()) should be 1 but contains %+v", testURLPath, info)
725 }
726
727
728 RegisterResponderWithQuery("GET", testURLPath, query, nil)
729 require.Len(GetCallCountInfo(), 0)
730
731 for _, url := range test.URLs {
732 t.Logf("query=%v URL=%s", query, url)
733
734 req, err := http.NewRequest("GET", url, nil)
735 require.CmpNoError(err)
736
737 _, err = client.Do(req)
738 assert.HasSuffix(err, "no responder found")
739 }
740
741 DeactivateAndReset()
742 }
743 }
744 }
745
746 func TestRegisterResponderWithQueryPanic(t *testing.T) {
747 resp := NewStringResponder(200, "hello world!")
748
749 for _, test := range []struct {
750 Path string
751 Query interface{}
752 PanicPrefix string
753 }{
754 {
755 Path: "foobar",
756 Query: "%",
757 PanicPrefix: "RegisterResponderWithQuery bad query string: ",
758 },
759 {
760 Path: "foobar",
761 Query: 1234,
762 PanicPrefix: "RegisterResponderWithQuery bad query type int. Only url.Values, map[string]string and string are allowed",
763 },
764 {
765 Path: `=~regexp.*\z`,
766 Query: "",
767 PanicPrefix: `path begins with "=~", RegisterResponder should be used instead of RegisterResponderWithQuery`,
768 },
769 } {
770 td.CmpPanic(t,
771 func() { RegisterResponderWithQuery("GET", test.Path, test.Query, resp) },
772 td.HasPrefix(test.PanicPrefix),
773 `RegisterResponderWithQuery + query=%v`, test.Query)
774 }
775 }
776
777 func TestRegisterRegexpResponder(t *testing.T) {
778 Activate()
779 defer DeactivateAndReset()
780
781 rx := regexp.MustCompile("ex.mple")
782
783 RegisterRegexpResponder("GET", rx, NewStringResponder(200, "first"))
784
785 RegisterRegexpResponder("GET", rx, NewStringResponder(200, "second"))
786
787 resp, err := http.Get(testURL)
788 td.Require(t).CmpNoError(err)
789
790 assertBody(t, resp, "second")
791 }
792
793 func TestSubmatches(t *testing.T) {
794 assert, require := td.AssertRequire(t)
795
796 req, err := http.NewRequest("GET", "/foo/bar", nil)
797 require.CmpNoError(err)
798
799 req2 := internal.SetSubmatches(req, []string{"foo", "123", "-123", "12.3"})
800
801 assert.Run("GetSubmatch", func(assert *td.T) {
802 _, err := GetSubmatch(req, 1)
803 assert.Cmp(err, ErrSubmatchNotFound)
804
805 _, err = GetSubmatch(req2, 5)
806 assert.Cmp(err, ErrSubmatchNotFound)
807
808 s, err := GetSubmatch(req2, 1)
809 assert.CmpNoError(err)
810 assert.Cmp(s, "foo")
811
812 s, err = GetSubmatch(req2, 4)
813 assert.CmpNoError(err)
814 assert.Cmp(s, "12.3")
815
816 s = MustGetSubmatch(req2, 4)
817 assert.Cmp(s, "12.3")
818 })
819
820 assert.Run("GetSubmatchAsInt", func(assert *td.T) {
821 _, err := GetSubmatchAsInt(req, 1)
822 assert.Cmp(err, ErrSubmatchNotFound)
823
824 _, err = GetSubmatchAsInt(req2, 4)
825 assert.CmpError(err)
826 assert.Not(err, ErrSubmatchNotFound)
827
828 i, err := GetSubmatchAsInt(req2, 3)
829 assert.CmpNoError(err)
830 assert.CmpLax(i, -123)
831
832 i = MustGetSubmatchAsInt(req2, 3)
833 assert.CmpLax(i, -123)
834 })
835
836 assert.Run("GetSubmatchAsUint", func(assert *td.T) {
837 _, err := GetSubmatchAsUint(req, 1)
838 assert.Cmp(err, ErrSubmatchNotFound)
839
840 _, err = GetSubmatchAsUint(req2, 3)
841 assert.CmpError(err)
842 assert.Not(err, ErrSubmatchNotFound)
843
844 u, err := GetSubmatchAsUint(req2, 2)
845 assert.CmpNoError(err)
846 assert.CmpLax(u, 123)
847
848 u = MustGetSubmatchAsUint(req2, 2)
849 assert.CmpLax(u, 123)
850 })
851
852 assert.Run("GetSubmatchAsFloat", func(assert *td.T) {
853 _, err := GetSubmatchAsFloat(req, 1)
854 assert.Cmp(err, ErrSubmatchNotFound)
855
856 _, err = GetSubmatchAsFloat(req2, 1)
857 assert.CmpError(err)
858 assert.Not(err, ErrSubmatchNotFound)
859
860 f, err := GetSubmatchAsFloat(req2, 4)
861 assert.CmpNoError(err)
862 assert.Cmp(f, 12.3)
863
864 f = MustGetSubmatchAsFloat(req2, 4)
865 assert.Cmp(f, 12.3)
866 })
867
868 assert.Run("GetSubmatch* panics", func(assert *td.T) {
869 for _, test := range []struct {
870 Name string
871 Fn func()
872 PanicPrefix string
873 }{
874 {
875 Name: "GetSubmatch & n < 1",
876 Fn: func() { GetSubmatch(req, 0) },
877 PanicPrefix: "getting submatches starts at 1, not 0",
878 },
879 {
880 Name: "MustGetSubmatch",
881 Fn: func() { MustGetSubmatch(req, 1) },
882 PanicPrefix: "GetSubmatch failed: " + ErrSubmatchNotFound.Error(),
883 },
884 {
885 Name: "MustGetSubmatchAsInt",
886 Fn: func() { MustGetSubmatchAsInt(req2, 4) },
887 PanicPrefix: "GetSubmatchAsInt failed: ",
888 },
889 {
890 Name: "MustGetSubmatchAsUint",
891 Fn: func() { MustGetSubmatchAsUint(req2, 3) },
892 PanicPrefix: "GetSubmatchAsUint failed: ",
893 },
894 {
895 Name: "GetSubmatchAsFloat",
896 Fn: func() { MustGetSubmatchAsFloat(req2, 1) },
897 PanicPrefix: "GetSubmatchAsFloat failed: ",
898 },
899 } {
900 assert.CmpPanic(test.Fn, td.HasPrefix(test.PanicPrefix), test.Name)
901 }
902 })
903
904 assert.RunAssertRequire("Full test", func(assert, require *td.T) {
905 Activate()
906 defer DeactivateAndReset()
907
908 var (
909 id uint64
910 delta float64
911 deltaStr string
912 inc int64
913 )
914 RegisterResponder("GET", `=~^/id/(\d+)\?delta=(\d+(?:\.\d*)?)&inc=(-?\d+)\z`,
915 func(req *http.Request) (*http.Response, error) {
916 id = MustGetSubmatchAsUint(req, 1)
917 delta = MustGetSubmatchAsFloat(req, 2)
918 deltaStr = MustGetSubmatch(req, 2)
919 inc = MustGetSubmatchAsInt(req, 3)
920
921 return NewStringResponse(http.StatusOK, "OK"), nil
922 })
923
924 resp, err := http.Get("http://example.tld/id/123?delta=1.2&inc=-5")
925 require.CmpNoError(err)
926 assertBody(assert, resp, "OK")
927
928
929 assert.CmpLax(id, 123, "MustGetSubmatchAsUint")
930 assert.Cmp(delta, 1.2, "MustGetSubmatchAsFloat")
931 assert.Cmp(deltaStr, "1.2", "MustGetSubmatch")
932 assert.CmpLax(inc, -5, "MustGetSubmatchAsInt")
933 })
934 }
935
936 func TestCheckStackTracer(t *testing.T) {
937 assert, require := td.AssertRequire(t)
938
939
940 Activate()
941 defer Deactivate()
942
943 const url = "https://foo.bar/"
944 var mesg string
945 RegisterResponder("GET", url,
946 NewStringResponder(200, "{}").
947 Trace(func(args ...interface{}) { mesg = args[0].(string) }))
948
949 resp, err := http.Get(url)
950 require.CmpNoError(err)
951
952 assertBody(assert, resp, "{}")
953
954
955 assert.HasPrefix(mesg, "GET https://foo.bar/\nCalled from net/http.Get()\n at ")
956 assert.Not(mesg, td.HasSuffix("\n"))
957 }
958
959 func TestCheckMethod(t *testing.T) {
960 mt := NewMockTransport()
961
962 const expected = `You probably want to use method "GET" instead of "get"? If not and so want to disable this check, set MockTransport.DontCheckMethod field to true`
963
964 td.CmpPanic(t,
965 func() { mt.RegisterResponder("get", "/pipo", NewStringResponder(200, "")) },
966 expected)
967
968 td.CmpPanic(t,
969 func() { mt.RegisterRegexpResponder("get", regexp.MustCompile("."), NewStringResponder(200, "")) },
970 expected)
971
972 td.CmpPanic(t,
973 func() { mt.RegisterResponderWithQuery("get", "/pipo", url.Values(nil), NewStringResponder(200, "")) },
974 expected)
975
976
977
978 mt.DontCheckMethod = true
979 td.CmpNotPanic(t,
980 func() { mt.RegisterResponder("get", "/pipo", NewStringResponder(200, "")) })
981
982 td.CmpNotPanic(t,
983 func() { mt.RegisterRegexpResponder("get", regexp.MustCompile("."), NewStringResponder(200, "")) })
984
985 td.CmpNotPanic(t,
986 func() { mt.RegisterResponderWithQuery("get", "/pipo", url.Values(nil), NewStringResponder(200, "")) })
987 }
988
View as plain text