diff --git a/matchers.go b/matchers.go index 11f5b1070..cdf1476d6 100644 --- a/matchers.go +++ b/matchers.go @@ -408,6 +408,15 @@ func BeADirectory() types.GomegaMatcher { return &matchers.BeADirectoryMatcher{} } +//HaveHTTPStatus succeeds if the Status or StatusCode field of an HTTP response matches. +//Actual must be either a *http.Response or *httptest.ResponseRecorder. +//Expected must be either an int or a string. +// Expect(resp).Should(HaveHTTPStatus(http.StatusOK)) // asserts that resp.StatusCode == 200 +// Expect(resp).Should(HaveHTTPStatus("404 Not Found")) // asserts that resp.Status == "404 Not Found" +func HaveHTTPStatus(expected interface{}) types.GomegaMatcher { + return &matchers.HaveHTTPStatusMatcher{Expected: expected} +} + //And succeeds only if all of the given matchers succeed. //The matchers are tried in order, and will fail-fast if one doesn't succeed. // Expect("hi").To(And(HaveLen(2), Equal("hi")) diff --git a/matchers/have_http_status_matcher.go b/matchers/have_http_status_matcher.go new file mode 100644 index 000000000..3ce4800b7 --- /dev/null +++ b/matchers/have_http_status_matcher.go @@ -0,0 +1,42 @@ +package matchers + +import ( + "fmt" + "net/http" + "net/http/httptest" + + "github.com/onsi/gomega/format" +) + +type HaveHTTPStatusMatcher struct { + Expected interface{} +} + +func (matcher *HaveHTTPStatusMatcher) Match(actual interface{}) (success bool, err error) { + var resp *http.Response + switch a := actual.(type) { + case *http.Response: + resp = a + case *httptest.ResponseRecorder: + resp = a.Result() + default: + return false, fmt.Errorf("HaveHTTPStatus matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1)) + } + + switch e := matcher.Expected.(type) { + case int: + return resp.StatusCode == e, nil + case string: + return resp.Status == e, nil + } + + return false, fmt.Errorf("HaveHTTPStatus matcher must be passed an int or a string. Got:\n%s", format.Object(matcher.Expected, 1)) +} + +func (matcher *HaveHTTPStatusMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to have HTTP status", matcher.Expected) +} + +func (matcher *HaveHTTPStatusMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to have HTTP status", matcher.Expected) +} diff --git a/matchers/have_http_status_matcher_test.go b/matchers/have_http_status_matcher_test.go new file mode 100644 index 000000000..9e46a5650 --- /dev/null +++ b/matchers/have_http_status_matcher_test.go @@ -0,0 +1,91 @@ +package matchers_test + +import ( + "net/http" + "net/http/httptest" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("HaveHTTPStatus", func() { + When("ACTUAL is *http.Response", func() { + When("EXPECTED is integer", func() { + It("matches the StatusCode", func() { + resp := &http.Response{StatusCode: http.StatusOK} + Expect(resp).To(HaveHTTPStatus(http.StatusOK)) + Expect(resp).NotTo(HaveHTTPStatus(http.StatusNotFound)) + }) + }) + When("EXPECTED is string", func() { + It("matches the Status", func() { + resp := &http.Response{Status: "200 OK"} + Expect(resp).To(HaveHTTPStatus("200 OK")) + Expect(resp).NotTo(HaveHTTPStatus("404 Not Found")) + }) + }) + When("EXPECTED is anything else", func() { + It("does not match", func() { + failures := InterceptGomegaFailures(func() { + resp := &http.Response{StatusCode: http.StatusOK} + Expect(resp).NotTo(HaveHTTPStatus(true)) + }) + Expect(failures).To(ConsistOf("HaveHTTPStatus matcher must be passed an int or a string. Got:\n : true")) + }) + }) + }) + + When("ACTUAL is *httptest.ResponseRecorder", func() { + When("EXPECTED is integer", func() { + It("matches the StatusCode", func() { + resp := &httptest.ResponseRecorder{Code: http.StatusOK} + Expect(resp).To(HaveHTTPStatus(http.StatusOK)) + Expect(resp).NotTo(HaveHTTPStatus(http.StatusNotFound)) + }) + }) + When("EXPECTED is string", func() { + It("matches the Status", func() { + resp := &httptest.ResponseRecorder{Code: http.StatusOK} + Expect(resp).To(HaveHTTPStatus("200 OK")) + Expect(resp).NotTo(HaveHTTPStatus("404 Not Found")) + }) + }) + When("EXPECTED is anything else", func() { + It("does not match", func() { + failures := InterceptGomegaFailures(func() { + resp := &httptest.ResponseRecorder{Code: http.StatusOK} + Expect(resp).NotTo(HaveHTTPStatus(nil)) + }) + Expect(failures).To(ConsistOf("HaveHTTPStatus matcher must be passed an int or a string. Got:\n : nil")) + }) + }) + }) + + When("ACTUAL is neither *http.Response nor *httptest.ResponseRecorder", func() { + It("errors", func() { + failures := InterceptGomegaFailures(func() { + Expect("foo").To(HaveHTTPStatus(http.StatusOK)) + }) + Expect(failures).To(ConsistOf("HaveHTTPStatus matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n : foo")) + }) + }) + + Describe("FailureMessage", func() { + It("returns message", func() { + failures := InterceptGomegaFailures(func() { + resp := &http.Response{StatusCode: http.StatusBadGateway} + Expect(resp).To(HaveHTTPStatus(http.StatusOK)) + }) + Expect(failures).To(ConsistOf(MatchRegexp("Expected(.|\n)*StatusCode: 502(.|\n)*to have HTTP status\n : 200"))) + }) + }) + Describe("NegatedFailureMessage", func() { + It("returns message", func() { + failures := InterceptGomegaFailures(func() { + resp := &http.Response{StatusCode: http.StatusOK} + Expect(resp).NotTo(HaveHTTPStatus(http.StatusOK)) + }) + Expect(failures).To(ConsistOf(MatchRegexp("Expected(.|\n)*StatusCode: 200(.|\n)*not to have HTTP status\n : 200"))) + }) + }) +})