Skip to content

Commit

Permalink
Add HaveHTTPStatus matcher (#378)
Browse files Browse the repository at this point in the history
Co-authored-by: David Ansari <dansari@pivotal.io>

Co-authored-by: Danny Berger <dberger+github@pivotal.io>
  • Loading branch information
ansd and dpb587-pivotal committed Mar 4, 2020
1 parent 6be6c43 commit f335c94
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 0 deletions.
9 changes: 9 additions & 0 deletions matchers.go
Expand Up @@ -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"))
Expand Down
42 changes: 42 additions & 0 deletions 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)
}
91 changes: 91 additions & 0 deletions 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 <bool>: 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>: 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 <string>: 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 <int>: 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 <int>: 200")))
})
})
})

0 comments on commit f335c94

Please sign in to comment.