diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..304cd85 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/thingful/httpmock + +go 1.12 + +require ( + github.com/PuerkitoBio/purell v1.0.0 + github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2 + github.com/goware/urlx v0.0.0-20160113170155-86bdc2456038 + golang.org/x/net v0.0.0-20161101191631-4bb47a1098b3 + golang.org/x/text v0.0.0-20161027091323-a8b38433e35b +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6b703ed --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/PuerkitoBio/purell v1.0.0 h1:0GoNN3taZV6QI81IXgCbxMyEaJDXMSIjArYBCYzVVvs= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2 h1:JCHLVE3B+kJde7bIEo5N4J+ZbLhp0J1Fs+ulyRws4gE= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/goware/urlx v0.0.0-20160113170155-86bdc2456038 h1:Gc3BCC674dsGqSp0cu9UAPydZRC7gxEOuUNKgXQPAx8= +github.com/goware/urlx v0.0.0-20160113170155-86bdc2456038/go.mod h1:Zn362WbIrTvMfW1tj4MxrEct8vJtNlnljZPnRssPfDU= +golang.org/x/net v0.0.0-20161101191631-4bb47a1098b3 h1:9FrZULpPblLeSMxFmRapLbJGYHjcvaCZYD+5rwKQqZA= +golang.org/x/net v0.0.0-20161101191631-4bb47a1098b3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/text v0.0.0-20161027091323-a8b38433e35b h1:bQeEFHDAz2T0N9zVAr0qdH8BRhoZocCxLudf/77OxGY= +golang.org/x/text v0.0.0-20161027091323-a8b38433e35b/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/stubbed_request.go b/stubbed_request.go index c992246..f991424 100644 --- a/stubbed_request.go +++ b/stubbed_request.go @@ -57,7 +57,7 @@ type StubRequest struct { Method string URL string Header *http.Header - Body io.Reader + Body []byte Responder Responder Called bool } @@ -74,7 +74,11 @@ func (r *StubRequest) WithHeader(header *http.Header) *StubRequest { // // Deprecated: use the functional WithBody configuration function instead func (r *StubRequest) WithBody(body io.Reader) *StubRequest { - r.Body = body + var err error + r.Body, err = ioutil.ReadAll(body) + if err != nil { + panic(err) + } return r } @@ -90,7 +94,11 @@ func WithHeader(header *http.Header) Option { // request func WithBody(body io.Reader) Option { return func(r *StubRequest) { - r.Body = body + var err error + r.Body, err = ioutil.ReadAll(body) + if err != nil { + panic(err) + } } } @@ -136,17 +144,12 @@ func (r *StubRequest) Matches(req *http.Request) error { // if our stub includes a body, then it should equal the actual request body // to match if r.Body != nil { - stubBody, err := ioutil.ReadAll(r.Body) - if err != nil { - return err - } - requestBody, err := ioutil.ReadAll(req.Body) if err != nil { return err } - if bytes.Compare(stubBody, requestBody) != 0 { + if bytes.Compare(r.Body, requestBody) != 0 { return ErrIncorrectRequestBody } } diff --git a/transport.go b/transport.go index 7491c3d..70b5d2c 100644 --- a/transport.go +++ b/transport.go @@ -1,6 +1,8 @@ package httpmock import ( + "bytes" + "io/ioutil" "net/http" "strings" "sync" @@ -72,8 +74,20 @@ func (m *MockTransport) stubForRequest(req *http.Request) (*StubRequest, error) var err error var errs = []error{} + haveBody := false + var body []byte + if req.Body != nil { + body, err = ioutil.ReadAll(req.Body) + if err != nil { + return nil, err + } + haveBody = true + } // find the first stub that matches the request for _, stub := range m.stubs { + if haveBody { + req.Body = ioutil.NopCloser(bytes.NewReader(body)) + } err = stub.Matches(req) if err == nil { return stub, nil diff --git a/transport_test.go b/transport_test.go index 48ec177..634d182 100644 --- a/transport_test.go +++ b/transport_test.go @@ -231,6 +231,186 @@ func TestAllStubsCalled(t *testing.T) { } } +func TestAllStubsCalledWithMultipleRequestsWithTheSameMethodAndUrl(t *testing.T) { + Activate() + defer DeactivateAndReset() + + RegisterStubRequest(NewStubRequest("GET", "http://example.com", NewStringResponder(200, "ok"))) + + // make a single request + resp, err := http.Get("http://example.com") + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + + err = AllStubsCalled() + if err != nil { + t.Errorf("Expected no error when not all stubs called") + } + + // make a second request + resp, err = http.Get("http://example.com") + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + + err = AllStubsCalled() + if err != nil { + t.Errorf("Expected no error when not all stubs called") + } + +} + +func TestAllStubsCalledWithTheSameMethodUrlHeaderAndBody(t *testing.T) { + Activate() + defer DeactivateAndReset() + + body, err := json.Marshal(map[string]string{"foo": "bar"}) + if err != nil { + panic(err) + } + + // register a stub with body and header + RegisterStubRequest( + NewStubRequest( + "POST", "http://example.com", NewStringResponder(200, "ok"), + ).WithHeader( + &http.Header{"X-Foo-Bar": []string{"Foo Bar Baz"}}, + ).WithBody(bytes.NewBuffer(body)), + ) + + // make a single request + req, err := http.NewRequest("POST", "http://example.com", bytes.NewReader(body)) + if err != nil { + t.Fatal(err) + } + req.Header.Add("X-Foo-Bar", "Foo Bar Baz") + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + + err = AllStubsCalled() + if err != nil { + t.Errorf("Expected no error when not all stubs called") + } + + // make a second identical request + req, err = http.NewRequest("POST", "http://example.com", bytes.NewReader(body)) + if err != nil { + t.Fatal(err) + } + req.Header.Add("X-Foo-Bar", "Foo Bar Baz") + resp, err = http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + + err = AllStubsCalled() + if err != nil { + t.Errorf("Expected no error when all stubs called") + } + +} + +func TestAllStubsCalledWithDifferentMethodUrlHeaderAndBody(t *testing.T) { + Activate() + defer DeactivateAndReset() + + body, err := json.Marshal(map[string]string{"foo": "bar"}) + if err != nil { + panic(err) + } + + body2, err := json.Marshal(map[string]string{"foo": "baz"}) + if err != nil { + panic(err) + } + + // register two stubs + RegisterStubRequest( + NewStubRequest( + "POST", "http://example.com", NewStringResponder(200, "ok"), + ).WithHeader( + &http.Header{"X-Foo-Bar": []string{"Foo Bar Baz"}}, + ).WithBody( + bytes.NewBuffer(body2), + ), + ) + + RegisterStubRequest( + NewStubRequest( + "POST", "http://example.com", NewStringResponder(200, "ok"), + ).WithHeader( + &http.Header{"X-Foo-Bar": []string{"Foo Bar Baz"}}, + ).WithBody(bytes.NewBuffer(body)), + ) + + // make a single request + req, err := http.NewRequest("POST", "http://example.com", bytes.NewReader(body)) + if err != nil { + t.Fatal(err) + } + req.Header.Add("X-Foo-Bar", "Foo Bar Baz") + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + + err = AllStubsCalled() + if err == nil { + t.Errorf("Expected error when not all stubs called") + } + + if !strings.Contains(err.Error(), "http://example.com") { + t.Errorf("Expected error message to contain uncalled stub, got: '%s'", err.Error()) + } + + // make a single request + req, err = http.NewRequest("POST", "http://example.com", bytes.NewReader(body)) + if err != nil { + t.Fatal(err) + } + req.Header.Add("X-Foo-Bar", "Foo Bar Baz") + resp, err = http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + + err = AllStubsCalled() + if err == nil { + t.Errorf("Expected error when not all stubs called") + } + + if !strings.Contains(err.Error(), "http://example.com") { + t.Errorf("Expected error message to contain uncalled stub, got: '%s'", err.Error()) + } + + // make a single request + req, err = http.NewRequest("POST", "http://example.com", bytes.NewReader(body2)) + if err != nil { + t.Fatal(err) + } + req.Header.Add("X-Foo-Bar", "Foo Bar Baz") + resp, err = http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + + err = AllStubsCalled() + if err != nil { + t.Errorf("Expected no error when all stubs called") + } + +} + func TestMockTransportReset(t *testing.T) { DeactivateAndReset() @@ -451,3 +631,5 @@ func TestMockTransportNonDefaultAllowedHosts(t *testing.T) { // restore our original initialTransport = cachedTransport } + +// func