Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: HaveHTTPHeaderWithValue() matcher #463

Merged
merged 2 commits into from Aug 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions matchers.go
Expand Up @@ -427,6 +427,17 @@ func HaveHTTPStatus(expected interface{}) types.GomegaMatcher {
return &matchers.HaveHTTPStatusMatcher{Expected: expected}
}

// HaveHTTPHeaderWithValue succeeds if the header is found and the value matches.
// Actual must be either a *http.Response or *httptest.ResponseRecorder.
// Expected must be a string header name, followed by a header value which
// can be a string, or another matcher.
func HaveHTTPHeaderWithValue(header string, value interface{}) types.GomegaMatcher {
return &matchers.HaveHTTPHeaderWithValueMatcher{
Header: header,
Value: value,
}
}

// HaveHTTPBody matches if the body matches.
// Actual must be either a *http.Response or *httptest.ResponseRecorder.
// Expected must be either a string, []byte, or other matcher
Expand Down
81 changes: 81 additions & 0 deletions matchers/have_http_header_with_value_matcher.go
@@ -0,0 +1,81 @@
package matchers

import (
"fmt"
"net/http"
"net/http/httptest"

"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)

type HaveHTTPHeaderWithValueMatcher struct {
Header string
Value interface{}
}

func (matcher *HaveHTTPHeaderWithValueMatcher) Match(actual interface{}) (success bool, err error) {
headerValue, err := matcher.extractHeader(actual)
if err != nil {
return false, err
}

headerMatcher, err := matcher.getSubMatcher()
if err != nil {
return false, err
}

return headerMatcher.Match(headerValue)
}

func (matcher *HaveHTTPHeaderWithValueMatcher) FailureMessage(actual interface{}) string {
headerValue, err := matcher.extractHeader(actual)
if err != nil {
panic(err) // protected by Match()
}

headerMatcher, err := matcher.getSubMatcher()
if err != nil {
panic(err) // protected by Match()
}

diff := format.IndentString(headerMatcher.FailureMessage(headerValue), 1)
return fmt.Sprintf("HTTP header %q:\n%s", matcher.Header, diff)
}

func (matcher *HaveHTTPHeaderWithValueMatcher) NegatedFailureMessage(actual interface{}) (message string) {
headerValue, err := matcher.extractHeader(actual)
if err != nil {
panic(err) // protected by Match()
}

headerMatcher, err := matcher.getSubMatcher()
if err != nil {
panic(err) // protected by Match()
}

diff := format.IndentString(headerMatcher.NegatedFailureMessage(headerValue), 1)
return fmt.Sprintf("HTTP header %q:\n%s", matcher.Header, diff)
}

func (matcher *HaveHTTPHeaderWithValueMatcher) getSubMatcher() (types.GomegaMatcher, error) {
switch m := matcher.Value.(type) {
case string:
return &EqualMatcher{Expected: matcher.Value}, nil
case types.GomegaMatcher:
return m, nil
default:
return nil, fmt.Errorf("HaveHTTPHeaderWithValue matcher must be passed a string or a GomegaMatcher. Got:\n%s", format.Object(matcher.Value, 1))
}
}

func (matcher *HaveHTTPHeaderWithValueMatcher) extractHeader(actual interface{}) (string, error) {
switch r := actual.(type) {
case *http.Response:
return r.Header.Get(matcher.Header), nil
case *httptest.ResponseRecorder:
return r.Result().Header.Get(matcher.Header), nil
default:
return "", fmt.Errorf("HaveHTTPHeaderWithValue matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1))
}
}
161 changes: 161 additions & 0 deletions matchers/have_http_header_with_value_matcher_test.go
@@ -0,0 +1,161 @@
package matchers_test

import (
"net/http"
"net/http/httptest"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("HaveHTTPHeader", func() {
It("can match an HTTP header", func() {
resp := &http.Response{}
resp.Header = make(http.Header)
resp.Header.Add("fake-header", "fake value")
Expect(resp).To(HaveHTTPHeaderWithValue("fake-header", "fake value"))
})

It("can mismatch an HTTP header", func() {
resp := &http.Response{}
resp.Header = make(http.Header)
resp.Header.Add("fake-header", "fake value")
Expect(resp).NotTo(HaveHTTPHeaderWithValue("other-header", "fake value"))
Expect(resp).NotTo(HaveHTTPHeaderWithValue("fake-header", "other value"))
})

When("the header is set more than once", func() {
It("matches the first value and not the second", func() {
resp := &http.Response{}
resp.Header = make(http.Header)
resp.Header.Add("fake-header", "fake value1")
resp.Header.Add("fake-header", "fake value2")
Expect(resp).To(HaveHTTPHeaderWithValue("fake-header", "fake value1"))
Expect(resp).NotTo(HaveHTTPHeaderWithValue("fake-header", "fake value2"))
})
})

When("ACTUAL is *httptest.ResponseRecorder", func() {
It("can match an HTTP header", func() {
resp := &httptest.ResponseRecorder{}
resp.Header().Add("fake-header", "fake value")
Expect(resp).To(HaveHTTPHeaderWithValue("fake-header", "fake value"))
})

It("can mismatch an HTTP header", func() {
resp := &httptest.ResponseRecorder{}
resp.Header().Add("fake-header", "fake value")
Expect(resp).NotTo(HaveHTTPHeaderWithValue("other-header", "fake value"))
Expect(resp).NotTo(HaveHTTPHeaderWithValue("fake-header", "other value"))
})
})

When("ACTUAL is neither *http.Response nor *httptest.ResponseRecorder", func() {
It("errors", func() {
failures := InterceptGomegaFailures(func() {
Expect("foo").To(HaveHTTPHeaderWithValue("bar", "baz"))
})
Expect(failures).To(HaveLen(1))
Expect(failures[0]).To(Equal("HaveHTTPHeaderWithValue matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n <string>: foo"))
})
})

When("EXPECTED VALUE is a matcher", func() {
It("can match an HTTP header", func() {
resp := &http.Response{}
resp.Header = make(http.Header)
resp.Header.Add("fake-header", "fake value")
Expect(resp).To(HaveHTTPHeaderWithValue("fake-header", ContainSubstring("value")))
})

It("can mismatch an HTTP header", func() {
resp := &http.Response{}
resp.Header = make(http.Header)
resp.Header.Add("fake-header", "fake value")
Expect(resp).NotTo(HaveHTTPHeaderWithValue("fake-header", ContainSubstring("foo")))
})
})

When("EXPECTED VALUE is something else", func() {
It("errors", func() {
failures := InterceptGomegaFailures(func() {
resp := &http.Response{}
Expect(resp).To(HaveHTTPHeaderWithValue("bar", 42))
})
Expect(failures).To(HaveLen(1))
Expect(failures[0]).To(Equal("HaveHTTPHeaderWithValue matcher must be passed a string or a GomegaMatcher. Got:\n <int>: 42"))
})
})

Describe("FailureMessage", func() {
When("matching a string", func() {
It("returns message", func() {
failures := InterceptGomegaFailures(func() {
resp := &http.Response{}
resp.Header = make(http.Header)
resp.Header.Add("fake-header", "fake value")
Expect(resp).To(HaveHTTPHeaderWithValue("fake-header", "other value"))
})
Expect(failures).To(HaveLen(1))
Expect(failures[0]).To(Equal(`HTTP header "fake-header":
Expected
<string>: fake value
to equal
<string>: other value`), failures[0])
})
})

When("matching a matcher", func() {
It("returns message", func() {
failures := InterceptGomegaFailures(func() {
resp := &http.Response{}
resp.Header = make(http.Header)
resp.Header.Add("fake-header", "fake value")
Expect(resp).To(HaveHTTPHeaderWithValue("fake-header", ContainSubstring("other")))
})
Expect(failures).To(HaveLen(1))
Expect(failures[0]).To(Equal(`HTTP header "fake-header":
Expected
<string>: fake value
to contain substring
<string>: other`), failures[0])
})
})
})

Describe("NegatedFailureMessage", func() {
When("matching a string", func() {
It("returns message", func() {
failures := InterceptGomegaFailures(func() {
resp := &http.Response{}
resp.Header = make(http.Header)
resp.Header.Add("fake-header", "fake value")
Expect(resp).NotTo(HaveHTTPHeaderWithValue("fake-header", "fake value"))
})
Expect(failures).To(HaveLen(1))
Expect(failures[0]).To(Equal(`HTTP header "fake-header":
Expected
<string>: fake value
not to equal
<string>: fake value`), failures[0])
})
})

When("matching a matcher", func() {
It("returns message", func() {
failures := InterceptGomegaFailures(func() {
resp := &http.Response{}
resp.Header = make(http.Header)
resp.Header.Add("fake-header", "fake value")
Expect(resp).NotTo(HaveHTTPHeaderWithValue("fake-header", ContainSubstring("value")))
})
Expect(failures).To(HaveLen(1))
Expect(failures[0]).To(Equal(`HTTP header "fake-header":
Expected
<string>: fake value
not to contain substring
<string>: value`), failures[0])
})
})
})
})