Skip to content

Commit

Permalink
assert: Add HTTP builder to combine request x response using builder.
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Vaško authored and Matovidlo committed Feb 16, 2024
1 parent 5911e38 commit 8cec282
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 0 deletions.
12 changes: 12 additions & 0 deletions assert/assertion_format.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions assert/assertion_forward.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions assert/http_assertions.go
Expand Up @@ -5,6 +5,7 @@ import (
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"strings"
)

Expand Down Expand Up @@ -163,3 +164,50 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url strin

return !contains
}

// HTTP asserts that a specfied handler returns set of expected values given by HttpOptions.
//
// assert.HTTP(t, myHandler, "www.google.com", nil, WithCode(200), WithBody("I'm Feeling Lucky"), WithRequestHeader(http.Header{"a": []string{"b"}}, WithExpectedBody(bytes.NewBuffer("c"))))
//
// Returns whether the assertion was successful (true) or not (false).
func HTTP(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, options ...HttpOption) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}

b := &builder{}
for _, option := range options {
err := option(b)
if err != nil {
Fail(t, fmt.Sprintf("Failed to build http options, got error: %s", err))
}
}

w := httptest.NewRecorder()
req, err := http.NewRequest(method, url, b.body)
if b.err == nil && err != nil {
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
} else if b.err != nil {
return err == b.err
}

req.Header = b.requestHeader
req.URL.RawQuery = values.Encode()
handler(w, req)
if w.Code != b.code {
Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), w.Code))
return false
}

if b.responseHeader != nil && !reflect.DeepEqual(w.HeaderMap, b.responseHeader) {
Fail(t, fmt.Sprintf("Expected HTTP header to be equal for %q but received %v", url+"?"+values.Encode(), w.HeaderMap))
return false
}

contains := strings.Contains(w.Body.String(), b.expectedBody.String())
if !contains {
Fail(t, fmt.Sprintf("Expected HTTP body to be equal for %q but received %s", url+"?"+values.Encode(), w.Body.String()))
}

return contains
}
25 changes: 25 additions & 0 deletions assert/http_assertions_test.go
@@ -1,6 +1,7 @@
package assert

import (
"bytes"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -192,3 +193,27 @@ func TestHttpBodyWrappers(t *testing.T) {
assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
assert.True(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
}

func TestHTTPBuilder(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))

// Test status codes
assert.Equal(mockAssert.HTTP(httpOK, "GET", "/", nil, WithCode(200)), true)
assert.Equal(mockAssert.HTTP(httpRedirect, "GET", "/", nil, WithCode(200)), false)
assert.Equal(mockAssert.HTTP(httpError, "GET", "/", nil, WithCode(200)), false)

assert.Equal(mockAssert.HTTP(httpOK, "GET", "/", nil, WithCode(200)), true)
assert.Equal(mockAssert.HTTP(httpRedirect, "GET", "/", nil, WithCode(307)), true)
assert.Equal(mockAssert.HTTP(httpError, "GET", "/", nil, WithCode(500)), true)

// Test codes and body
assert.True(mockAssert.HTTP(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, WithCode(200), WithExpectedBody(*bytes.NewBufferString("Hello, World!"))))
assert.True(mockAssert.HTTP(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, WithCode(200), WithExpectedBody(*bytes.NewBufferString("World"))))
assert.False(mockAssert.HTTP(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, WithCode(200), WithExpectedBody(*bytes.NewBufferString("world"))))

// Test codes headers and body
assert.True(mockAssert.HTTP(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, WithCode(200), WithResponseHeader(http.Header{"Content-Type": []string{"text/plain; charset=utf-8"}}), WithExpectedBody(*bytes.NewBufferString("Hello, World!"))))
assert.True(mockAssert.HTTP(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, WithCode(200), WithResponseHeader(http.Header{"Content-Type": []string{"text/plain; charset=utf-8"}}), WithExpectedBody(*bytes.NewBufferString("World"))))
assert.False(mockAssert.HTTP(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, WithCode(200), WithResponseHeader(http.Header{"Content-Type": []string{"text/plain; charset=utf-8"}}), WithExpectedBody(*bytes.NewBufferString("world"))))
}
65 changes: 65 additions & 0 deletions assert/http_options.go
@@ -0,0 +1,65 @@
package assert

import (
"bytes"
"errors"
"io"
http "net/http"
)

type builder struct {
code int
body io.ReadCloser
expectedBody bytes.Buffer
requestHeader http.Header
responseHeader http.Header
err error
}

type HttpOption func(*builder) error

func WithCode(code int) HttpOption {
return func(b *builder) error {
if code < 100 || code > 511 {
return errors.New("Given HTTP code is outside range of possible values assignement")
}

b.code = code
return nil
}
}

func WithErr(err error) HttpOption {
return func(b *builder) error {
b.err = err
return nil
}
}

func WithBody(body io.ReadCloser) HttpOption {
return func(b *builder) error {
b.body = body
return nil
}
}

func WithExpectedBody(expectedBody bytes.Buffer) HttpOption {
return func(b *builder) error {
b.expectedBody = expectedBody
return nil
}
}

func WithRequestHeader(requestHeader http.Header) HttpOption {
return func(b *builder) error {
b.requestHeader = requestHeader
return nil
}
}

func WithResponseHeader(responseHeader http.Header) HttpOption {
return func(b *builder) error {
b.responseHeader = responseHeader
return nil
}
}
30 changes: 30 additions & 0 deletions require/require.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions require/require_forward.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 8cec282

Please sign in to comment.