Repository: stretchr/testify
Pull request #1484 (2023-10-11 08:19:29)

assert: fix httpCode and HTTPBody occur panic when http.Handler read Body
http.Request.Body  should be `http.NoBoy` not `nil` when no body.

https://github.com/stretchr/testify/blob/11a6452626f0eacefe8a99889ca4b36f9340488c/assert/http_assertions.go#L15

Test:

```go
func httpReadBody(w http.ResponseWriter, r *http.Request) {
	_, _ = io.ReadAll(r.Body)  // will panic
}

assert.HTTPStatusCode(t, httpReadBody, "GET", "/", nil, 200)
```

## Summary
<!-- High-level, one sentence summary of what this PR accomplishes -->

## Changes
<!-- * Description of change 1 -->
<!-- * Description of change 2 -->
<!-- ... -->

## Motivation
<!-- Why were the changes necessary. -->

<!-- ## Example usage (if applicable) -->

## Related issues
<!-- Put `Closes #XXXX` for each issue number this PR fixes/closes -->


dolmen: @hidu Please revert the last commit [7412b1b](https://github.com/stretchr/testify/pull/1484/commits/7412b1bd8086b592ff8078bcf6d73c55e87a0799) which is a merge commit. Do a `git rebase origin/master` instead of pulling master into your branch.
dolmen: Reproducer for the issue: https://go.dev/play/p/GuLSWavdJ-8

Patch:
diff --git a/assert/http_assertions.go b/assert/http_assertions.go
index 6cfb6a8..8b82ca8 100644
--- a/assert/http_assertions.go
+++ b/assert/http_assertions.go
@@ -12,7 +12,7 @@ import (
 // an error if building a new request fails.
 func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) {
 	w := httptest.NewRecorder()
-	req, err := http.NewRequest(method, url, nil)
+	req, err := http.NewRequest(method, url, http.NoBody)
 	if err != nil {
 		return -1, err
 	}
@@ -116,7 +116,7 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s
 	if len(values) > 0 {
 		url += "?" + values.Encode()
 	}
-	req, err := http.NewRequest(method, url, nil)
+	req, err := http.NewRequest(method, url, http.NoBody)
 	if err != nil {
 		return ""
 	}


Test patch:
diff --git a/assert/http_assertions_test.go b/assert/http_assertions_test.go
index 413c971..8da117e 100644
--- a/assert/http_assertions_test.go
+++ b/assert/http_assertions_test.go
@@ -2,6 +2,7 @@ package assert
 
 import (
 	"fmt"
+	"io"
 	"net/http"
 	"net/url"
 	"testing"
@@ -11,6 +12,12 @@ func httpOK(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusOK)
 }
 
+func httpReadBody(w http.ResponseWriter, r *http.Request) {
+	_, _ = io.Copy(io.Discard, r.Body)
+	w.WriteHeader(http.StatusOK)
+	_, _ = w.Write([]byte("hello"))
+}
+
 func httpRedirect(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusTemporaryRedirect)
 }
@@ -41,6 +48,10 @@ func TestHTTPSuccess(t *testing.T) {
 	mockT4 := new(testing.T)
 	assert.Equal(HTTPSuccess(mockT4, httpStatusCode, "GET", "/", nil), false)
 	assert.True(mockT4.Failed())
+
+	mockT5 := new(testing.T)
+	assert.Equal(HTTPSuccess(mockT5, httpReadBody, "POST", "/", nil), true)
+	assert.False(mockT5.Failed())
 }
 
 func TestHTTPRedirect(t *testing.T) {
@@ -122,7 +133,7 @@ func TestHTTPStatusesWrapper(t *testing.T) {
 
 func httpHelloName(w http.ResponseWriter, r *http.Request) {
 	name := r.FormValue("name")
-	_, _ = w.Write([]byte(fmt.Sprintf("Hello, %s!", name)))
+	_, _ = fmt.Fprintf(w, "Hello, %s!", name)
 }
 
 func TestHTTPRequestWithNoParams(t *testing.T) {
@@ -165,6 +176,8 @@ func TestHttpBody(t *testing.T) {
 	assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
 	assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
 	assert.True(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
+
+	assert.True(HTTPBodyContains(mockT, httpReadBody, "GET", "/", nil, "hello"))
 }
 
 func TestHttpBodyWrappers(t *testing.T) {
@@ -178,5 +191,4 @@ func TestHttpBodyWrappers(t *testing.T) {
 	assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
 	assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
 	assert.True(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
-
 }