-
-
Notifications
You must be signed in to change notification settings - Fork 288
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
295 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package matchers | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"net/http/httptest" | ||
|
||
"github.com/onsi/gomega/format" | ||
"github.com/onsi/gomega/types" | ||
) | ||
|
||
type HaveHTTPBodyMatcher struct { | ||
Expected interface{} | ||
cachedBody []byte | ||
} | ||
|
||
func (matcher *HaveHTTPBodyMatcher) Match(actual interface{}) (bool, error) { | ||
body, err := matcher.body(actual) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
switch e := matcher.Expected.(type) { | ||
case string: | ||
return (&EqualMatcher{Expected: e}).Match(string(body)) | ||
case []byte: | ||
return (&EqualMatcher{Expected: e}).Match(body) | ||
case types.GomegaMatcher: | ||
return e.Match(body) | ||
default: | ||
return false, fmt.Errorf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1)) | ||
} | ||
} | ||
|
||
func (matcher *HaveHTTPBodyMatcher) FailureMessage(actual interface{}) (message string) { | ||
body, err := matcher.body(actual) | ||
if err != nil { | ||
return fmt.Sprintf("failed to read body: %s", err) | ||
} | ||
|
||
switch e := matcher.Expected.(type) { | ||
case string: | ||
return (&EqualMatcher{Expected: e}).FailureMessage(string(body)) | ||
case []byte: | ||
return (&EqualMatcher{Expected: e}).FailureMessage(body) | ||
case types.GomegaMatcher: | ||
return e.FailureMessage(body) | ||
default: | ||
return fmt.Sprintf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1)) | ||
} | ||
} | ||
|
||
func (matcher *HaveHTTPBodyMatcher) NegatedFailureMessage(actual interface{}) (message string) { | ||
body, err := matcher.body(actual) | ||
if err != nil { | ||
return fmt.Sprintf("failed to read body: %s", err) | ||
} | ||
|
||
switch e := matcher.Expected.(type) { | ||
case string: | ||
return (&EqualMatcher{Expected: e}).NegatedFailureMessage(string(body)) | ||
case []byte: | ||
return (&EqualMatcher{Expected: e}).NegatedFailureMessage(body) | ||
case types.GomegaMatcher: | ||
return e.NegatedFailureMessage(body) | ||
default: | ||
return fmt.Sprintf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1)) | ||
} | ||
} | ||
|
||
// body returns the body. It is cached because once we read it in Match() | ||
// the Reader is closed and it is not readable again in FailureMessage() | ||
// or NegatedFailureMessage() | ||
func (matcher *HaveHTTPBodyMatcher) body(actual interface{}) ([]byte, error) { | ||
if matcher.cachedBody != nil { | ||
return matcher.cachedBody, nil | ||
} | ||
|
||
body := func(a *http.Response) ([]byte, error) { | ||
if a.Body != nil { | ||
defer a.Body.Close() | ||
var err error | ||
matcher.cachedBody, err = ioutil.ReadAll(a.Body) | ||
if err != nil { | ||
return nil, fmt.Errorf("error reading response body: %w", err) | ||
} | ||
} | ||
return matcher.cachedBody, nil | ||
} | ||
|
||
switch a := actual.(type) { | ||
case *http.Response: | ||
return body(a) | ||
case *httptest.ResponseRecorder: | ||
return body(a.Result()) | ||
default: | ||
return nil, fmt.Errorf("HaveHTTPBody matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1)) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
package matchers_test | ||
|
||
import ( | ||
"bytes" | ||
"io/ioutil" | ||
"net/http" | ||
"net/http/httptest" | ||
"strings" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("HaveHTTPBody", func() { | ||
When("ACTUAL is *http.Response", func() { | ||
It("matches the body", func() { | ||
const body = "this is the body" | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader(body))} | ||
Expect(resp).To(HaveHTTPBody(body)) | ||
}) | ||
|
||
It("mismatches the body", func() { | ||
const body = "this is the body" | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader(body))} | ||
Expect(resp).NotTo(HaveHTTPBody("something else")) | ||
}) | ||
}) | ||
|
||
When("ACTUAL is *httptest.ResponseRecorder", func() { | ||
It("matches the body", func() { | ||
const body = "this is the body" | ||
resp := &httptest.ResponseRecorder{Body: bytes.NewBufferString(body)} | ||
Expect(resp).To(HaveHTTPBody(body)) | ||
}) | ||
|
||
It("mismatches the body", func() { | ||
const body = "this is the body" | ||
resp := &httptest.ResponseRecorder{Body: bytes.NewBufferString(body)} | ||
Expect(resp).NotTo(HaveHTTPBody("something else")) | ||
}) | ||
}) | ||
|
||
When("ACTUAL is neither *http.Response nor *httptest.ResponseRecorder", func() { | ||
It("errors", func() { | ||
failures := InterceptGomegaFailures(func() { | ||
Expect("foo").To(HaveHTTPBody("bar")) | ||
}) | ||
Expect(failures).To(ConsistOf("HaveHTTPBody matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n <string>: foo")) | ||
}) | ||
}) | ||
|
||
When("EXPECTED is []byte", func() { | ||
It("matches the body", func() { | ||
const body = "this is the body" | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader(body))} | ||
Expect(resp).To(HaveHTTPBody([]byte(body))) | ||
}) | ||
|
||
It("mismatches the body", func() { | ||
const body = "this is the body" | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader(body))} | ||
Expect(resp).NotTo(HaveHTTPBody([]byte("something else"))) | ||
}) | ||
}) | ||
|
||
When("EXPECTED is a submatcher", func() { | ||
It("matches the body", func() { | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader(`{"some":"json"}`))} | ||
Expect(resp).To(HaveHTTPBody(MatchJSON(`{ "some": "json" }`))) | ||
}) | ||
|
||
It("mismatches the body", func() { | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader(`{"some":"json"}`))} | ||
Expect(resp).NotTo(HaveHTTPBody(MatchJSON(`{ "something": "different" }`))) | ||
}) | ||
}) | ||
|
||
When("EXPECTED is something else", func() { | ||
It("errors", func() { | ||
failures := InterceptGomegaFailures(func() { | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader("body"))} | ||
Expect(resp).To(HaveHTTPBody(map[int]bool{})) | ||
}) | ||
Expect(failures).To(HaveLen(1)) | ||
Expect(failures[0]).To(Equal("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n <map[int]bool | len:0>: {}")) | ||
}) | ||
}) | ||
|
||
Describe("FailureMessage", func() { | ||
Context("EXPECTED is string", func() { | ||
It("returns a match failure message", func() { | ||
failures := InterceptGomegaFailures(func() { | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader("this is the body"))} | ||
Expect(resp).To(HaveHTTPBody("this is a different body")) | ||
}) | ||
Expect(failures).To(HaveLen(1)) | ||
Expect(failures[0]).To(Equal(`Expected | ||
<string>: this is the body | ||
to equal | ||
<string>: this is a different body`), failures[0]) | ||
}) | ||
}) | ||
|
||
Context("EXPECTED is []byte", func() { | ||
It("returns a match failure message", func() { | ||
failures := InterceptGomegaFailures(func() { | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader("this is the body"))} | ||
Expect(resp).To(HaveHTTPBody([]byte("this is a different body"))) | ||
}) | ||
Expect(failures).To(HaveLen(1)) | ||
Expect(failures[0]).To(MatchRegexp(`^Expected | ||
<\[\]uint8 \| len:\d+, cap:\d+>: this is the body | ||
to equal | ||
<\[\]uint8 ]| len:\d+, cap:\d+>: this is a different body$`)) | ||
}) | ||
}) | ||
|
||
Context("EXPECTED is submatcher", func() { | ||
It("returns a match failure message", func() { | ||
failures := InterceptGomegaFailures(func() { | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader(`{"some":"json"}`))} | ||
Expect(resp).To(HaveHTTPBody(MatchJSON(`{"other":"stuff"}`))) | ||
}) | ||
Expect(failures).To(HaveLen(1)) | ||
Expect(failures[0]).To(Equal(`Expected | ||
<string>: { | ||
"some": "json" | ||
} | ||
to match JSON of | ||
<string>: { | ||
"other": "stuff" | ||
}`)) | ||
}) | ||
}) | ||
}) | ||
|
||
Describe("NegatedFailureMessage", func() { | ||
Context("EXPECTED is string", func() { | ||
It("returns a negated failure message", func() { | ||
const body = "this is the body" | ||
failures := InterceptGomegaFailures(func() { | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader(body))} | ||
Expect(resp).NotTo(HaveHTTPBody(body)) | ||
}) | ||
Expect(failures).To(HaveLen(1)) | ||
Expect(failures[0]).To(Equal(`Expected | ||
<string>: this is the body | ||
not to equal | ||
<string>: this is the body`)) | ||
}) | ||
}) | ||
|
||
Context("EXPECTED is []byte", func() { | ||
It("returns a match failure message", func() { | ||
const body = "this is the body" | ||
failures := InterceptGomegaFailures(func() { | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader(body))} | ||
Expect(resp).NotTo(HaveHTTPBody([]byte(body))) | ||
}) | ||
Expect(failures).To(HaveLen(1)) | ||
Expect(failures[0]).To(MatchRegexp(`^Expected | ||
<\[\]uint8 \| len:\d+, cap:\d+>: this is the body | ||
not to equal | ||
<\[\]uint8 \| len:\d+, cap:\d+>: this is the body$`)) | ||
}) | ||
}) | ||
|
||
Context("EXPECTED is submatcher", func() { | ||
It("returns a match failure message", func() { | ||
const body = `{"some":"json"}` | ||
failures := InterceptGomegaFailures(func() { | ||
resp := &http.Response{Body: ioutil.NopCloser(strings.NewReader(body))} | ||
Expect(resp).NotTo(HaveHTTPBody(MatchJSON(body))) | ||
}) | ||
Expect(failures).To(HaveLen(1)) | ||
Expect(failures[0]).To(Equal(`Expected | ||
<string>: { | ||
"some": "json" | ||
} | ||
not to match JSON of | ||
<string>: { | ||
"some": "json" | ||
}`)) | ||
}) | ||
}) | ||
}) | ||
}) |