From 507b3f3629f56453e37b9533fb5e9b8524cfc81b Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Tue, 16 Jun 2026 06:38:04 +0000 Subject: [PATCH 1/4] Add httptest response recorder assertions --- hammy/httpassert/recorder.go | 91 ++++++++++++++++++ hammy/httpassert/recorder_test.go | 149 ++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 hammy/httpassert/recorder.go create mode 100644 hammy/httpassert/recorder_test.go diff --git a/hammy/httpassert/recorder.go b/hammy/httpassert/recorder.go new file mode 100644 index 0000000..bb7e22d --- /dev/null +++ b/hammy/httpassert/recorder.go @@ -0,0 +1,91 @@ +package httpassert + +import ( + "net/http" + "net/http/httptest" + + "github.com/gogunit/gunit/hammy" +) + +// Recorder wraps an httptest.ResponseRecorder with HTTP assertion helpers. +func Recorder(rec *httptest.ResponseRecorder) *RecorderAssert { + return &RecorderAssert{actual: rec} +} + +// RecorderAssert provides HTTP assertions for an httptest.ResponseRecorder. +type RecorderAssert struct { + actual *httptest.ResponseRecorder +} + +// Status asserts that the recorded response status equals expected. +func (r *RecorderAssert) Status(expected int) hammy.AssertionMessage { + resp, result := r.response() + if !result.IsSuccessful { + return hammy.Assert(false, "got nil response recorder, wanted status <%d>", expected) + } + return Status(resp, expected) +} + +// StatusInRange asserts that the recorded response status is between min and max, inclusive. +func (r *RecorderAssert) StatusInRange(min, max int) hammy.AssertionMessage { + if min > max { + return hammy.Assert(false, "got invalid status range <%d..%d>", min, max) + } + resp, result := r.response() + if !result.IsSuccessful { + return hammy.Assert(false, "got nil response recorder, wanted status in range <%d..%d>", min, max) + } + return StatusInRange(resp, min, max) +} + +// Header asserts that the recorded response header equals expected. +func (r *RecorderAssert) Header(key, expected string) hammy.AssertionMessage { + resp, result := r.response() + if !result.IsSuccessful { + return hammy.Assert(false, "got nil response recorder, wanted header <%s> equal to <%s>", key, expected) + } + return Header(resp, key, expected) +} + +// HeaderContains asserts that the recorded response header contains expected. +func (r *RecorderAssert) HeaderContains(key, expected string) hammy.AssertionMessage { + resp, result := r.response() + if !result.IsSuccessful { + return hammy.Assert(false, "got nil response recorder, wanted header <%s> containing <%s>", key, expected) + } + return HeaderContains(resp, key, expected) +} + +// BodyEqual asserts that the recorded response body equals expected. +func (r *RecorderAssert) BodyEqual(expected string) hammy.AssertionMessage { + resp, result := r.response() + if !result.IsSuccessful { + return hammy.Assert(false, "got nil response recorder, wanted response body") + } + return BodyEqual(resp, expected) +} + +// BodyContains asserts that the recorded response body contains expected. +func (r *RecorderAssert) BodyContains(expected string) hammy.AssertionMessage { + resp, result := r.response() + if !result.IsSuccessful { + return hammy.Assert(false, "got nil response recorder, wanted response body") + } + return BodyContains(resp, expected) +} + +// BodyMatchesRegexp asserts that the recorded response body matches pattern. +func (r *RecorderAssert) BodyMatchesRegexp(pattern string) hammy.AssertionMessage { + resp, result := r.response() + if !result.IsSuccessful { + return hammy.Assert(false, "got nil response recorder, wanted response body") + } + return BodyMatchesRegexp(resp, pattern) +} + +func (r *RecorderAssert) response() (*http.Response, hammy.AssertionMessage) { + if r == nil || r.actual == nil { + return nil, hammy.Assert(false, "got nil response recorder") + } + return r.actual.Result(), hammy.Assert(true, "got response recorder") +} diff --git a/hammy/httpassert/recorder_test.go b/hammy/httpassert/recorder_test.go new file mode 100644 index 0000000..5cd6896 --- /dev/null +++ b/hammy/httpassert/recorder_test.go @@ -0,0 +1,149 @@ +package httpassert_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/gogunit/gunit/hammy" + "github.com/gogunit/gunit/hammy/httpassert" +) + +func Test_Recorder_Status_success(t *testing.T) { + assert := hammy.New(t) + rec := newRecorder(http.StatusCreated, nil, "") + + assert.Is(httpassert.Recorder(rec).Status(http.StatusCreated)) +} + +func Test_Recorder_Status_failure(t *testing.T) { + rec := newRecorder(http.StatusCreated, nil, "") + result := httpassert.Recorder(rec).Status(http.StatusOK) + + requireFailure(t, result, "got status <201>, wanted <200>") +} + +func Test_Recorder_Status_failure_nil_recorder(t *testing.T) { + result := httpassert.Recorder(nil).Status(http.StatusOK) + + requireFailure(t, result, "got nil response recorder") +} + +func Test_Recorder_StatusInRange_success(t *testing.T) { + assert := hammy.New(t) + rec := newRecorder(http.StatusNoContent, nil, "") + + assert.Is(httpassert.Recorder(rec).StatusInRange(200, 299)) +} + +func Test_Recorder_StatusInRange_failure(t *testing.T) { + rec := newRecorder(http.StatusBadRequest, nil, "") + result := httpassert.Recorder(rec).StatusInRange(200, 299) + + requireFailure(t, result, "got status <400>, wanted in range <200..299>") +} + +func Test_Recorder_StatusInRange_failure_invalid_range(t *testing.T) { + rec := newRecorder(http.StatusOK, nil, "") + result := httpassert.Recorder(rec).StatusInRange(299, 200) + + requireFailure(t, result, "got invalid status range <299..200>") +} + +func Test_Recorder_Header_success(t *testing.T) { + assert := hammy.New(t) + rec := newRecorder(http.StatusOK, http.Header{"Content-Type": {"application/json"}}, "") + + assert.Is(httpassert.Recorder(rec).Header("Content-Type", "application/json")) +} + +func Test_Recorder_Header_failure(t *testing.T) { + rec := newRecorder(http.StatusOK, http.Header{"Content-Type": {"text/plain"}}, "") + result := httpassert.Recorder(rec).Header("Content-Type", "application/json") + + requireFailure(t, result, "got header =, wanted ") +} + +func Test_Recorder_HeaderContains_success(t *testing.T) { + assert := hammy.New(t) + rec := newRecorder(http.StatusOK, http.Header{"Content-Type": {"application/json; charset=utf-8"}}, "") + + assert.Is(httpassert.Recorder(rec).HeaderContains("Content-Type", "application/json")) +} + +func Test_Recorder_HeaderContains_failure(t *testing.T) { + rec := newRecorder(http.StatusOK, http.Header{"Content-Type": {"text/plain"}}, "") + result := httpassert.Recorder(rec).HeaderContains("Content-Type", "application/json") + + requireFailure(t, result, "got header =, wanted containing ") +} + +func Test_Recorder_BodyEqual_success(t *testing.T) { + assert := hammy.New(t) + rec := newRecorder(http.StatusOK, nil, "hello world") + + assert.Is(httpassert.Recorder(rec).BodyEqual("hello world")) +} + +func Test_Recorder_BodyEqual_failure(t *testing.T) { + rec := newRecorder(http.StatusOK, nil, "hello world") + result := httpassert.Recorder(rec).BodyEqual("goodbye") + + requireFailure(t, result, "got body , wanted equal to ") +} + +func Test_Recorder_BodyContains_success(t *testing.T) { + assert := hammy.New(t) + rec := newRecorder(http.StatusOK, nil, "hello world") + + assert.Is(httpassert.Recorder(rec).BodyContains("world")) +} + +func Test_Recorder_BodyContains_failure(t *testing.T) { + rec := newRecorder(http.StatusOK, nil, "hello world") + result := httpassert.Recorder(rec).BodyContains("goodbye") + + requireFailure(t, result, "got body , wanted containing ") +} + +func Test_Recorder_BodyMatchesRegexp_success(t *testing.T) { + assert := hammy.New(t) + rec := newRecorder(http.StatusOK, nil, "status 204") + + assert.Is(httpassert.Recorder(rec).BodyMatchesRegexp(`status \d+`)) +} + +func Test_Recorder_BodyMatchesRegexp_failure(t *testing.T) { + rec := newRecorder(http.StatusOK, nil, "status 204") + result := httpassert.Recorder(rec).BodyMatchesRegexp(`status 5\d\d`) + + requireFailure(t, result, "got body , wanted regexp ") +} + +func Test_Recorder_BodyMatchesRegexp_failure_invalid_pattern(t *testing.T) { + rec := newRecorder(http.StatusOK, nil, "status 204") + result := httpassert.Recorder(rec).BodyMatchesRegexp(`(`) + + requireFailure(t, result, "invalid regexp <(>") +} + +func Test_Recorder_Body_assertions_restore_body(t *testing.T) { + assert := hammy.New(t) + recorderAssert := httpassert.Recorder(newRecorder(http.StatusOK, nil, "hello world")) + + assert.Is(recorderAssert.BodyContains("hello")) + assert.Is(recorderAssert.BodyEqual("hello world")) + assert.Is(recorderAssert.BodyMatchesRegexp(`world$`)) +} + +func newRecorder(status int, headers http.Header, body string) *httptest.ResponseRecorder { + recorder := httptest.NewRecorder() + for key, values := range headers { + for _, value := range values { + recorder.Header().Add(key, value) + } + } + recorder.WriteHeader(status) + _, _ = recorder.WriteString(body) + return recorder +} From ce7b173e381bfa12a6544298bcb167bd4c6f103c Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Tue, 16 Jun 2026 19:32:40 +0000 Subject: [PATCH 2/4] Inline recorder assertion failure checks --- hammy/httpassert/recorder_test.go | 71 ++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/hammy/httpassert/recorder_test.go b/hammy/httpassert/recorder_test.go index 5cd6896..616a171 100644 --- a/hammy/httpassert/recorder_test.go +++ b/hammy/httpassert/recorder_test.go @@ -3,6 +3,7 @@ package httpassert_test import ( "net/http" "net/http/httptest" + "strings" "testing" "github.com/gogunit/gunit/hammy" @@ -20,13 +21,23 @@ func Test_Recorder_Status_failure(t *testing.T) { rec := newRecorder(http.StatusCreated, nil, "") result := httpassert.Recorder(rec).Status(http.StatusOK) - requireFailure(t, result, "got status <201>, wanted <200>") + if result.IsSuccessful { + t.Fatal("got success, wanted failure") + } + if !strings.Contains(result.Message, "got status <201>, wanted <200>") { + t.Fatalf("got message %q, wanted containing %q", result.Message, "got status <201>, wanted <200>") + } } func Test_Recorder_Status_failure_nil_recorder(t *testing.T) { result := httpassert.Recorder(nil).Status(http.StatusOK) - requireFailure(t, result, "got nil response recorder") + if result.IsSuccessful { + t.Fatal("got success, wanted failure") + } + if !strings.Contains(result.Message, "got nil response recorder") { + t.Fatalf("got message %q, wanted containing %q", result.Message, "got nil response recorder") + } } func Test_Recorder_StatusInRange_success(t *testing.T) { @@ -40,14 +51,24 @@ func Test_Recorder_StatusInRange_failure(t *testing.T) { rec := newRecorder(http.StatusBadRequest, nil, "") result := httpassert.Recorder(rec).StatusInRange(200, 299) - requireFailure(t, result, "got status <400>, wanted in range <200..299>") + if result.IsSuccessful { + t.Fatal("got success, wanted failure") + } + if !strings.Contains(result.Message, "got status <400>, wanted in range <200..299>") { + t.Fatalf("got message %q, wanted containing %q", result.Message, "got status <400>, wanted in range <200..299>") + } } func Test_Recorder_StatusInRange_failure_invalid_range(t *testing.T) { rec := newRecorder(http.StatusOK, nil, "") result := httpassert.Recorder(rec).StatusInRange(299, 200) - requireFailure(t, result, "got invalid status range <299..200>") + if result.IsSuccessful { + t.Fatal("got success, wanted failure") + } + if !strings.Contains(result.Message, "got invalid status range <299..200>") { + t.Fatalf("got message %q, wanted containing %q", result.Message, "got invalid status range <299..200>") + } } func Test_Recorder_Header_success(t *testing.T) { @@ -61,7 +82,12 @@ func Test_Recorder_Header_failure(t *testing.T) { rec := newRecorder(http.StatusOK, http.Header{"Content-Type": {"text/plain"}}, "") result := httpassert.Recorder(rec).Header("Content-Type", "application/json") - requireFailure(t, result, "got header =, wanted ") + if result.IsSuccessful { + t.Fatal("got success, wanted failure") + } + if !strings.Contains(result.Message, "got header =, wanted ") { + t.Fatalf("got message %q, wanted containing %q", result.Message, "got header =, wanted ") + } } func Test_Recorder_HeaderContains_success(t *testing.T) { @@ -75,7 +101,12 @@ func Test_Recorder_HeaderContains_failure(t *testing.T) { rec := newRecorder(http.StatusOK, http.Header{"Content-Type": {"text/plain"}}, "") result := httpassert.Recorder(rec).HeaderContains("Content-Type", "application/json") - requireFailure(t, result, "got header =, wanted containing ") + if result.IsSuccessful { + t.Fatal("got success, wanted failure") + } + if !strings.Contains(result.Message, "got header =, wanted containing ") { + t.Fatalf("got message %q, wanted containing %q", result.Message, "got header =, wanted containing ") + } } func Test_Recorder_BodyEqual_success(t *testing.T) { @@ -89,7 +120,12 @@ func Test_Recorder_BodyEqual_failure(t *testing.T) { rec := newRecorder(http.StatusOK, nil, "hello world") result := httpassert.Recorder(rec).BodyEqual("goodbye") - requireFailure(t, result, "got body , wanted equal to ") + if result.IsSuccessful { + t.Fatal("got success, wanted failure") + } + if !strings.Contains(result.Message, "got body , wanted equal to ") { + t.Fatalf("got message %q, wanted containing %q", result.Message, "got body , wanted equal to ") + } } func Test_Recorder_BodyContains_success(t *testing.T) { @@ -103,7 +139,12 @@ func Test_Recorder_BodyContains_failure(t *testing.T) { rec := newRecorder(http.StatusOK, nil, "hello world") result := httpassert.Recorder(rec).BodyContains("goodbye") - requireFailure(t, result, "got body , wanted containing ") + if result.IsSuccessful { + t.Fatal("got success, wanted failure") + } + if !strings.Contains(result.Message, "got body , wanted containing ") { + t.Fatalf("got message %q, wanted containing %q", result.Message, "got body , wanted containing ") + } } func Test_Recorder_BodyMatchesRegexp_success(t *testing.T) { @@ -117,14 +158,24 @@ func Test_Recorder_BodyMatchesRegexp_failure(t *testing.T) { rec := newRecorder(http.StatusOK, nil, "status 204") result := httpassert.Recorder(rec).BodyMatchesRegexp(`status 5\d\d`) - requireFailure(t, result, "got body , wanted regexp ") + if result.IsSuccessful { + t.Fatal("got success, wanted failure") + } + if !strings.Contains(result.Message, "got body , wanted regexp ") { + t.Fatalf("got message %q, wanted containing %q", result.Message, "got body , wanted regexp ") + } } func Test_Recorder_BodyMatchesRegexp_failure_invalid_pattern(t *testing.T) { rec := newRecorder(http.StatusOK, nil, "status 204") result := httpassert.Recorder(rec).BodyMatchesRegexp(`(`) - requireFailure(t, result, "invalid regexp <(>") + if result.IsSuccessful { + t.Fatal("got success, wanted failure") + } + if !strings.Contains(result.Message, "invalid regexp <(>") { + t.Fatalf("got message %q, wanted containing %q", result.Message, "invalid regexp <(>") + } } func Test_Recorder_Body_assertions_restore_body(t *testing.T) { From 9bb44be757daa568edccfca96c51065dfcd6dcc5 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Tue, 16 Jun 2026 19:43:13 +0000 Subject: [PATCH 3/4] Use spy pattern for recorder failures --- hammy/httpassert/recorder_test.go | 92 +++++++++++-------------------- 1 file changed, 31 insertions(+), 61 deletions(-) diff --git a/hammy/httpassert/recorder_test.go b/hammy/httpassert/recorder_test.go index 616a171..78e71f2 100644 --- a/hammy/httpassert/recorder_test.go +++ b/hammy/httpassert/recorder_test.go @@ -3,9 +3,9 @@ package httpassert_test import ( "net/http" "net/http/httptest" - "strings" "testing" + "github.com/gogunit/gunit/eye" "github.com/gogunit/gunit/hammy" "github.com/gogunit/gunit/hammy/httpassert" ) @@ -21,23 +21,17 @@ func Test_Recorder_Status_failure(t *testing.T) { rec := newRecorder(http.StatusCreated, nil, "") result := httpassert.Recorder(rec).Status(http.StatusOK) - if result.IsSuccessful { - t.Fatal("got success, wanted failure") - } - if !strings.Contains(result.Message, "got status <201>, wanted <200>") { - t.Fatalf("got message %q, wanted containing %q", result.Message, "got status <201>, wanted <200>") - } + aSpy := eye.Spy() + hammy.New(aSpy).Is(result) + aSpy.HadErrorContaining(t, "got status <201>, wanted <200>") } func Test_Recorder_Status_failure_nil_recorder(t *testing.T) { result := httpassert.Recorder(nil).Status(http.StatusOK) - if result.IsSuccessful { - t.Fatal("got success, wanted failure") - } - if !strings.Contains(result.Message, "got nil response recorder") { - t.Fatalf("got message %q, wanted containing %q", result.Message, "got nil response recorder") - } + aSpy := eye.Spy() + hammy.New(aSpy).Is(result) + aSpy.HadErrorContaining(t, "got nil response recorder") } func Test_Recorder_StatusInRange_success(t *testing.T) { @@ -51,24 +45,18 @@ func Test_Recorder_StatusInRange_failure(t *testing.T) { rec := newRecorder(http.StatusBadRequest, nil, "") result := httpassert.Recorder(rec).StatusInRange(200, 299) - if result.IsSuccessful { - t.Fatal("got success, wanted failure") - } - if !strings.Contains(result.Message, "got status <400>, wanted in range <200..299>") { - t.Fatalf("got message %q, wanted containing %q", result.Message, "got status <400>, wanted in range <200..299>") - } + aSpy := eye.Spy() + hammy.New(aSpy).Is(result) + aSpy.HadErrorContaining(t, "got status <400>, wanted in range <200..299>") } func Test_Recorder_StatusInRange_failure_invalid_range(t *testing.T) { rec := newRecorder(http.StatusOK, nil, "") result := httpassert.Recorder(rec).StatusInRange(299, 200) - if result.IsSuccessful { - t.Fatal("got success, wanted failure") - } - if !strings.Contains(result.Message, "got invalid status range <299..200>") { - t.Fatalf("got message %q, wanted containing %q", result.Message, "got invalid status range <299..200>") - } + aSpy := eye.Spy() + hammy.New(aSpy).Is(result) + aSpy.HadErrorContaining(t, "got invalid status range <299..200>") } func Test_Recorder_Header_success(t *testing.T) { @@ -82,12 +70,9 @@ func Test_Recorder_Header_failure(t *testing.T) { rec := newRecorder(http.StatusOK, http.Header{"Content-Type": {"text/plain"}}, "") result := httpassert.Recorder(rec).Header("Content-Type", "application/json") - if result.IsSuccessful { - t.Fatal("got success, wanted failure") - } - if !strings.Contains(result.Message, "got header =, wanted ") { - t.Fatalf("got message %q, wanted containing %q", result.Message, "got header =, wanted ") - } + aSpy := eye.Spy() + hammy.New(aSpy).Is(result) + aSpy.HadErrorContaining(t, "got header =, wanted ") } func Test_Recorder_HeaderContains_success(t *testing.T) { @@ -101,12 +86,9 @@ func Test_Recorder_HeaderContains_failure(t *testing.T) { rec := newRecorder(http.StatusOK, http.Header{"Content-Type": {"text/plain"}}, "") result := httpassert.Recorder(rec).HeaderContains("Content-Type", "application/json") - if result.IsSuccessful { - t.Fatal("got success, wanted failure") - } - if !strings.Contains(result.Message, "got header =, wanted containing ") { - t.Fatalf("got message %q, wanted containing %q", result.Message, "got header =, wanted containing ") - } + aSpy := eye.Spy() + hammy.New(aSpy).Is(result) + aSpy.HadErrorContaining(t, "got header =, wanted containing ") } func Test_Recorder_BodyEqual_success(t *testing.T) { @@ -120,12 +102,9 @@ func Test_Recorder_BodyEqual_failure(t *testing.T) { rec := newRecorder(http.StatusOK, nil, "hello world") result := httpassert.Recorder(rec).BodyEqual("goodbye") - if result.IsSuccessful { - t.Fatal("got success, wanted failure") - } - if !strings.Contains(result.Message, "got body , wanted equal to ") { - t.Fatalf("got message %q, wanted containing %q", result.Message, "got body , wanted equal to ") - } + aSpy := eye.Spy() + hammy.New(aSpy).Is(result) + aSpy.HadErrorContaining(t, "got body , wanted equal to ") } func Test_Recorder_BodyContains_success(t *testing.T) { @@ -139,12 +118,9 @@ func Test_Recorder_BodyContains_failure(t *testing.T) { rec := newRecorder(http.StatusOK, nil, "hello world") result := httpassert.Recorder(rec).BodyContains("goodbye") - if result.IsSuccessful { - t.Fatal("got success, wanted failure") - } - if !strings.Contains(result.Message, "got body , wanted containing ") { - t.Fatalf("got message %q, wanted containing %q", result.Message, "got body , wanted containing ") - } + aSpy := eye.Spy() + hammy.New(aSpy).Is(result) + aSpy.HadErrorContaining(t, "got body , wanted containing ") } func Test_Recorder_BodyMatchesRegexp_success(t *testing.T) { @@ -158,24 +134,18 @@ func Test_Recorder_BodyMatchesRegexp_failure(t *testing.T) { rec := newRecorder(http.StatusOK, nil, "status 204") result := httpassert.Recorder(rec).BodyMatchesRegexp(`status 5\d\d`) - if result.IsSuccessful { - t.Fatal("got success, wanted failure") - } - if !strings.Contains(result.Message, "got body , wanted regexp ") { - t.Fatalf("got message %q, wanted containing %q", result.Message, "got body , wanted regexp ") - } + aSpy := eye.Spy() + hammy.New(aSpy).Is(result) + aSpy.HadErrorContaining(t, "got body , wanted regexp ") } func Test_Recorder_BodyMatchesRegexp_failure_invalid_pattern(t *testing.T) { rec := newRecorder(http.StatusOK, nil, "status 204") result := httpassert.Recorder(rec).BodyMatchesRegexp(`(`) - if result.IsSuccessful { - t.Fatal("got success, wanted failure") - } - if !strings.Contains(result.Message, "invalid regexp <(>") { - t.Fatalf("got message %q, wanted containing %q", result.Message, "invalid regexp <(>") - } + aSpy := eye.Spy() + hammy.New(aSpy).Is(result) + aSpy.HadErrorContaining(t, "invalid regexp <(>") } func Test_Recorder_Body_assertions_restore_body(t *testing.T) { From ee460eede948a8854bbc3d399bb267bd11808beb Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Tue, 16 Jun 2026 20:11:55 +0000 Subject: [PATCH 4/4] Make recorder assertions self-contained --- hammy/httpassert/recorder.go | 38 ++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/hammy/httpassert/recorder.go b/hammy/httpassert/recorder.go index bb7e22d..7309938 100644 --- a/hammy/httpassert/recorder.go +++ b/hammy/httpassert/recorder.go @@ -3,6 +3,8 @@ package httpassert import ( "net/http" "net/http/httptest" + "regexp" + "strings" "github.com/gogunit/gunit/hammy" ) @@ -23,7 +25,7 @@ func (r *RecorderAssert) Status(expected int) hammy.AssertionMessage { if !result.IsSuccessful { return hammy.Assert(false, "got nil response recorder, wanted status <%d>", expected) } - return Status(resp, expected) + return hammy.Assert(resp.StatusCode == expected, "got status <%d>, wanted <%d>", resp.StatusCode, expected) } // StatusInRange asserts that the recorded response status is between min and max, inclusive. @@ -35,7 +37,7 @@ func (r *RecorderAssert) StatusInRange(min, max int) hammy.AssertionMessage { if !result.IsSuccessful { return hammy.Assert(false, "got nil response recorder, wanted status in range <%d..%d>", min, max) } - return StatusInRange(resp, min, max) + return hammy.Assert(resp.StatusCode >= min && resp.StatusCode <= max, "got status <%d>, wanted in range <%d..%d>", resp.StatusCode, min, max) } // Header asserts that the recorded response header equals expected. @@ -44,7 +46,8 @@ func (r *RecorderAssert) Header(key, expected string) hammy.AssertionMessage { if !result.IsSuccessful { return hammy.Assert(false, "got nil response recorder, wanted header <%s> equal to <%s>", key, expected) } - return Header(resp, key, expected) + actual := resp.Header.Get(key) + return hammy.Assert(actual == expected, "got header <%s>=<%s>, wanted <%s>", key, actual, expected) } // HeaderContains asserts that the recorded response header contains expected. @@ -53,34 +56,39 @@ func (r *RecorderAssert) HeaderContains(key, expected string) hammy.AssertionMes if !result.IsSuccessful { return hammy.Assert(false, "got nil response recorder, wanted header <%s> containing <%s>", key, expected) } - return HeaderContains(resp, key, expected) + actual := resp.Header.Get(key) + return hammy.Assert(strings.Contains(actual, expected), "got header <%s>=<%s>, wanted containing <%s>", key, actual, expected) } // BodyEqual asserts that the recorded response body equals expected. func (r *RecorderAssert) BodyEqual(expected string) hammy.AssertionMessage { - resp, result := r.response() + actual, result := r.body() if !result.IsSuccessful { return hammy.Assert(false, "got nil response recorder, wanted response body") } - return BodyEqual(resp, expected) + return hammy.Assert(actual == expected, "got body <%s>, wanted equal to <%s>", actual, expected) } // BodyContains asserts that the recorded response body contains expected. func (r *RecorderAssert) BodyContains(expected string) hammy.AssertionMessage { - resp, result := r.response() + actual, result := r.body() if !result.IsSuccessful { return hammy.Assert(false, "got nil response recorder, wanted response body") } - return BodyContains(resp, expected) + return hammy.Assert(strings.Contains(actual, expected), "got body <%s>, wanted containing <%s>", actual, expected) } // BodyMatchesRegexp asserts that the recorded response body matches pattern. func (r *RecorderAssert) BodyMatchesRegexp(pattern string) hammy.AssertionMessage { - resp, result := r.response() + actual, result := r.body() if !result.IsSuccessful { return hammy.Assert(false, "got nil response recorder, wanted response body") } - return BodyMatchesRegexp(resp, pattern) + re, err := regexp.Compile(pattern) + if err != nil { + return hammy.Assert(false, "invalid regexp <%s>: %v", pattern, err) + } + return hammy.Assert(re.MatchString(actual), "got body <%s>, wanted regexp <%s>", actual, pattern) } func (r *RecorderAssert) response() (*http.Response, hammy.AssertionMessage) { @@ -89,3 +97,13 @@ func (r *RecorderAssert) response() (*http.Response, hammy.AssertionMessage) { } return r.actual.Result(), hammy.Assert(true, "got response recorder") } + +func (r *RecorderAssert) body() (string, hammy.AssertionMessage) { + if r == nil || r.actual == nil { + return "", hammy.Assert(false, "got nil response recorder") + } + if r.actual.Body == nil { + return "", hammy.Assert(true, "got response recorder body") + } + return r.actual.Body.String(), hammy.Assert(true, "got response recorder body") +}