Repository: gavv/httpexpect
Pull request #197 (2022-12-10 15:17:33)

feat(status): implement status list
Closes issue #109 
Hi @gavv kindly review this PR. Thanks

coveralls: 
[![Coverage Status](https://coveralls.io/builds/55037686/badge)](https://coveralls.io/builds/55037686)

Coverage increased (+0.03%) to 93.075% when pulling **6450aa2a7792ae92e7aa32191c27bf2b81e7ee8c on edwinwijaya94:f_edwin_http_status_list** into **213c224c2fc02424934bce5f394727eec568f9d8 on gavv:master**.

edwinwijaya94: Thanks @gavv for the comments! Code has been updated
gavv: FYI: welcome to join discord chat if you prefer such format https://discord.gg/5SCPCuCWA9
gavv: Please rebase on fresh `master`.
edwinwijaya94: > FYI: welcome to join discord chat if you prefer such format https://discord.gg/5SCPCuCWA9

sure, will do

Patch:
diff --git a/response.go b/response.go
index 03529d6..d095aa7 100644
--- a/response.go
+++ b/response.go
@@ -262,6 +262,52 @@ func (r *Response) StatusRange(rn StatusRange) *Response {
 	return r
 }
 
+// StatusList succeeds if response matches with any given status code list
+//
+// Example:
+//
+//	resp := NewResponse(t, response)
+//	resp.StatusList(http.StatusForbidden, http.StatusUnauthorized)
+func (r *Response) StatusList(values ...int) *Response {
+	r.chain.enter("StatusList()")
+	defer r.chain.leave()
+
+	if r.chain.failed() {
+		return r
+	}
+
+	if len(values) == 0 {
+		r.chain.fail(AssertionFailure{
+			Type: AssertUsage,
+			Errors: []error{
+				errors.New("unexpected empty status list"),
+			},
+		})
+		return r
+	}
+
+	var found bool
+	for _, v := range values {
+		if v == r.httpResp.StatusCode {
+			found = true
+			break
+		}
+	}
+
+	if !found {
+		r.chain.fail(AssertionFailure{
+			Type:     AssertBelongs,
+			Actual:   &AssertionValue{statusCodeText(r.httpResp.StatusCode)},
+			Expected: &AssertionValue{AssertionList(statusListText(values))},
+			Errors: []error{
+				errors.New("expected: http status belongs to given list"),
+			},
+		})
+	}
+
+	return r
+}
+
 func statusCodeText(code int) string {
 	if s := http.StatusText(code); s != "" {
 		return strconv.Itoa(code) + " " + s
@@ -286,6 +332,14 @@ func statusRangeText(code int) string {
 	}
 }
 
+func statusListText(values []int) []interface{} {
+	var statusText []interface{}
+	for _, v := range values {
+		statusText = append(statusText, statusCodeText(v))
+	}
+	return statusText
+}
+
 // Headers returns a new Object instance with response header map.
 //
 // Example:


Test patch:
diff --git a/response_test.go b/response_test.go
index 06bc7db..4e13cde 100644
--- a/response_test.go
+++ b/response_test.go
@@ -42,6 +42,7 @@ func TestResponseFailed(t *testing.T) {
 
 		resp.Status(123)
 		resp.StatusRange(Status2xx)
+		resp.StatusList(http.StatusOK, http.StatusBadGateway)
 		resp.NoContent()
 		resp.ContentType("", "")
 		resp.ContentEncoding("")
@@ -196,6 +197,49 @@ func TestResponseStatusRange(t *testing.T) {
 	}
 }
 
+func TestResponseStatusList(t *testing.T) {
+	reporter := newMockReporter(t)
+
+	cases := []struct {
+		Status int
+		List   []int
+		WantOK bool
+	}{
+		{
+			http.StatusOK,
+			[]int{http.StatusOK, http.StatusBadRequest, http.StatusInternalServerError},
+			true,
+		},
+		{
+			http.StatusBadRequest,
+			[]int{http.StatusOK, http.StatusBadRequest, http.StatusInternalServerError},
+			true,
+		},
+		{
+			http.StatusOK,
+			[]int{http.StatusInternalServerError, http.StatusBadRequest},
+			false,
+		},
+		{
+			http.StatusBadGateway,
+			[]int{},
+			false,
+		},
+	}
+
+	for _, c := range cases {
+		resp := NewResponse(reporter, &http.Response{
+			StatusCode: c.Status,
+		})
+		resp.StatusList(c.List...)
+		if c.WantOK {
+			resp.chain.assertNotFailed(t)
+		} else {
+			resp.chain.assertFailed(t)
+		}
+	}
+}
+
 func TestResponseHeaders(t *testing.T) {
 	reporter := newMockReporter(t)