Repository: onsi/gomega (Go)

Issue:
HaveHTTPBody Does Not Work with Eventually
The following code does not work as expected when the first attempt does not succeed:
```golang
Eventually(func(ctx SpecContext) (*http.Response, error) {
  req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://localhost:8080/test", strings.NewReader(query))
  req.Header.Add("Content-Type", "application/json")
  if err != nil {
    return nil, err
  }
  return http.DefaultClient.Do(req)
}).
  WithContext(ctx).
  WithPolling(5 * time.Second).
  WithTimeout(30 * time.Second).
  Should(HaveHTTPBody(MatchJSON(expectedResult)))
```

This appears to be caused by the use of a cached variable that is never updated after being set:
https://github.com/onsi/gomega/blob/master/matchers/have_http_body_matcher.go#L76


Discussion:
Possible solution:
Store the pointer for the response and use that to determine if the cache should be used or not.
hey @Kyle-Cooley - good catch; agreed on checking the pointer as you're proposing.  Would you be up for submitting a PR?
Hello @onsi , yes, I will make a PR for it.



Patch:
diff --git a/matchers/have_http_body_matcher.go b/matchers/have_http_body_matcher.go
index 6a3dcdc35..d14d9e5fc 100644
--- a/matchers/have_http_body_matcher.go
+++ b/matchers/have_http_body_matcher.go
@@ -11,8 +11,9 @@ import (
 )
 
 type HaveHTTPBodyMatcher struct {
-	Expected   interface{}
-	cachedBody []byte
+	Expected       interface{}
+	cachedResponse interface{}
+	cachedBody     []byte
 }
 
 func (matcher *HaveHTTPBodyMatcher) Match(actual interface{}) (bool, error) {
@@ -73,7 +74,7 @@ func (matcher *HaveHTTPBodyMatcher) NegatedFailureMessage(actual interface{}) (m
 // 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 {
+	if matcher.cachedResponse == actual && matcher.cachedBody != nil {
 		return matcher.cachedBody, nil
 	}
 
@@ -91,8 +92,10 @@ func (matcher *HaveHTTPBodyMatcher) body(actual interface{}) ([]byte, error) {
 
 	switch a := actual.(type) {
 	case *http.Response:
+		matcher.cachedResponse = a
 		return body(a)
 	case *httptest.ResponseRecorder:
+		matcher.cachedResponse = a
 		return body(a.Result())
 	default:
 		return nil, fmt.Errorf("HaveHTTPBody matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1))


Test patch:
diff --git a/matchers/have_http_body_matcher_test.go b/matchers/have_http_body_matcher_test.go
index 926d6a5e5..8fd3e3d7a 100644
--- a/matchers/have_http_body_matcher_test.go
+++ b/matchers/have_http_body_matcher_test.go
@@ -2,6 +2,7 @@ package matchers_test
 
 import (
 	"bytes"
+	"io"
 	"net/http"
 	"net/http/httptest"
 	"strings"
@@ -24,6 +25,19 @@ var _ = Describe("HaveHTTPBody", func() {
 			resp := &http.Response{Body: gutil.NopCloser(strings.NewReader(body))}
 			Expect(resp).NotTo(HaveHTTPBody("something else"))
 		})
+
+		It("matches the body against later calls", func() {
+			firstCall := true
+			getResp := func() *http.Response {
+				if firstCall {
+					firstCall = false
+					return &http.Response{Body: io.NopCloser(strings.NewReader("first_call"))}
+				} else {
+					return &http.Response{Body: io.NopCloser(strings.NewReader("later_call"))}
+				}
+			}
+			Eventually(getResp).MustPassRepeatedly(2).Should(HaveHTTPBody([]byte("later_call")))
+		})
 	})
 
 	When("ACTUAL is *httptest.ResponseRecorder", func() {
@@ -38,6 +52,19 @@ var _ = Describe("HaveHTTPBody", func() {
 			resp := &httptest.ResponseRecorder{Body: bytes.NewBufferString(body)}
 			Expect(resp).NotTo(HaveHTTPBody("something else"))
 		})
+
+		It("matches the body against later calls", func() {
+			firstCall := true
+			getResp := func() *httptest.ResponseRecorder {
+				if firstCall {
+					firstCall = false
+					return &httptest.ResponseRecorder{Body: bytes.NewBufferString("first_call")}
+				} else {
+					return &httptest.ResponseRecorder{Body: bytes.NewBufferString("later_call")}
+				}
+			}
+			Eventually(getResp).MustPassRepeatedly(2).Should(HaveHTTPBody([]byte("later_call")))
+		})
 	})
 
 	When("ACTUAL is neither *http.Response nor *httptest.ResponseRecorder", func() {