From b3f32bc8e84d7c496806e43a80ce2a8613ce62b4 Mon Sep 17 00:00:00 2001 From: Rafael Dantas Justo Date: Thu, 31 Oct 2019 09:44:40 +0000 Subject: [PATCH 1/3] Feature: Handle binary response body When retrieving a binary response body, convert it to hexadecimal so we can safely add it to the snapshot file. --- assert.go | 31 +++++++++++++++++++++----- assert_test.go | 26 +++++++++++++++++++++ example/__snapshots__/example.snapshot | 3 +++ example/main.go | 6 +++++ example/main_test.go | 6 +++++ 5 files changed, 67 insertions(+), 5 deletions(-) diff --git a/assert.go b/assert.go index 6800dd4..1bb4d0d 100644 --- a/assert.go +++ b/assert.go @@ -1,6 +1,7 @@ package abide import ( + "encoding/hex" "encoding/json" "fmt" "io" @@ -32,7 +33,9 @@ func AssertHTTPResponse(t *testing.T, id string, w *http.Response) { t.Fatal(err) } - assertHTTP(t, id, body, contentTypeIsJSON(w.Header.Get("Content-Type"))) + isJSON := contentTypeIsJSON(w.Header.Get("Content-Type")) + isBinary := contentTypeIsBinary(w.Header.Get("Content-Type")) + assertHTTP(t, id, body, isJSON, isBinary) } // AssertHTTPRequestOut asserts the value of an http.Request. @@ -44,7 +47,9 @@ func AssertHTTPRequestOut(t *testing.T, id string, r *http.Request) { t.Fatal(err) } - assertHTTP(t, id, body, contentTypeIsJSON(r.Header.Get("Content-Type"))) + isJSON := contentTypeIsJSON(r.Header.Get("Content-Type")) + isBinary := contentTypeIsBinary(r.Header.Get("Content-Type")) + assertHTTP(t, id, body, isJSON, isBinary) } // AssertHTTPRequest asserts the value of an http.Request. @@ -56,16 +61,24 @@ func AssertHTTPRequest(t *testing.T, id string, r *http.Request) { t.Fatal(err) } - assertHTTP(t, id, body, contentTypeIsJSON(r.Header.Get("Content-Type"))) + isJSON := contentTypeIsJSON(r.Header.Get("Content-Type")) + isBinary := contentTypeIsBinary(r.Header.Get("Content-Type")) + assertHTTP(t, id, body, isJSON, isBinary) } -func assertHTTP(t *testing.T, id string, body []byte, isJSON bool) { +func assertHTTP(t *testing.T, id string, body []byte, isJSON, isBinary bool) { config, err := getConfig() if err != nil { t.Fatal(err) } - data := string(body) + var data string + if isBinary { + data = hex.EncodeToString(body) + } else { + data = string(body) + } + lines := strings.Split(strings.TrimSpace(data), "\n") if config != nil { @@ -126,6 +139,14 @@ func contentTypeIsJSON(contentType string) bool { return isVendor && isJSON } +func contentTypeIsBinary(contentType string) bool { + contentTypeParts := strings.Split(contentType, ";") + firstPart := contentTypeParts[0] + + return firstPart == "application/pdf" || + firstPart == "application/octet-stream" +} + // AssertReader asserts the value of an io.Reader. func AssertReader(t *testing.T, id string, r io.Reader) { data, err := ioutil.ReadAll(r) diff --git a/assert_test.go b/assert_test.go index 5ae7e87..aee2c65 100644 --- a/assert_test.go +++ b/assert_test.go @@ -27,3 +27,29 @@ func TestContentTypeIsJSON(test *testing.T) { } } } + +func TestContentTypeIsBinary(test *testing.T) { + contentTypeTestCases := map[string]bool{ + "application/pdf": true, + "application/octet-stream": true, + "application/json": false, + "application/json; charset=utf-8": false, + "application/vnd.foo.bar.v2+json": false, + "application/application/json": false, + "application/json/json": false, + "application/jsoner; charset=utf-8": false, + "application/jsoner": false, + "application/vnd.foo.bar.v2+jsoner": false, + "application/xml": false, + "text/html": false, + "": false, + } + + for input, expectedOutput := range contentTypeTestCases { + result := contentTypeIsBinary(input) + + if result != expectedOutput { + test.Errorf("contentTypeIsBinary(\"%s\" unexpected result. Got=%t, Want=%t", input, result, expectedOutput) + } + } +} diff --git a/example/__snapshots__/example.snapshot b/example/__snapshots__/example.snapshot index cd96eaa..496ba4c 100755 --- a/example/__snapshots__/example.snapshot +++ b/example/__snapshots__/example.snapshot @@ -4,6 +4,9 @@ main.MyStruct {Field1:String1 Field2:1234567 Field3:true field4:string4} /* snapshot: assertable string */ string to be asserted +/* snapshot: fifth route */ +485454502f312e3120323030204f4b0d0a436f6e6e656374696f6e3a20636c6f73650d0a436f6e74656e742d547970653a206170706c69636174696f6e2f7064660d0a0d0a0102030405060708090a + /* snapshot: first route */ HTTP/1.1 200 OK Connection: close diff --git a/example/main.go b/example/main.go index c3f76eb..10d2eb0 100644 --- a/example/main.go +++ b/example/main.go @@ -61,10 +61,16 @@ func fourthHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(`Hello World.`)) } +func fifthHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/pdf") + w.Write([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) +} + func main() { http.HandleFunc("/first", firstHandler) http.HandleFunc("/second", secondHandler) http.HandleFunc("/third", thirdHandler) http.HandleFunc("/fourth", fourthHandler) + http.HandleFunc("/fifth", fifthHandler) http.ListenAndServe(":8080", nil) } diff --git a/example/main_test.go b/example/main_test.go index 7d1a24d..b54fab6 100644 --- a/example/main_test.go +++ b/example/main_test.go @@ -34,6 +34,12 @@ func TestRequests(t *testing.T) { thirdHandler(w, req) res = w.Result() abide.AssertHTTPResponse(t, "third route", res) + + req = httptest.NewRequest("GET", "http://example.com/", nil) + w = httptest.NewRecorder() + fifthHandler(w, req) + res = w.Result() + abide.AssertHTTPResponse(t, "fifth route", res) } func TestReader(t *testing.T) { From 6f018e5da7290d72cc023f094533284979b962ba Mon Sep 17 00:00:00 2001 From: Rafael Dantas Justo Date: Tue, 12 Nov 2019 16:36:06 +0000 Subject: [PATCH 2/3] Add assert options to accommodate all response types --- assert.go | 84 ++++++++++++++++++++++++++++++++++++-------------- assert_test.go | 26 ---------------- 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/assert.go b/assert.go index 1bb4d0d..dc7703c 100644 --- a/assert.go +++ b/assert.go @@ -15,6 +15,28 @@ import ( "github.com/sergi/go-diff/diffmatchpatch" ) +// ContentType expected response body type. +type ContentType string + +// ContentType possible values. +const ( + ContentTypeJSON ContentType = "json" + ContentTypeText ContentType = "text" + ContentTypeBinary ContentType = "binary" +) + +// SetContentType define the response type in the options. +func SetContentType(typ ContentType) func(*AssertOptions) { + return func(options *AssertOptions) { + options.ContentType = typ + } +} + +// AssertOptions possible options for the assert. +type AssertOptions struct { + ContentType ContentType +} + // Assertable represents an object that can be asserted. type Assertable interface { String() string @@ -27,53 +49,77 @@ func Assert(t *testing.T, id string, a Assertable) { } // AssertHTTPResponse asserts the value of an http.Response. -func AssertHTTPResponse(t *testing.T, id string, w *http.Response) { +func AssertHTTPResponse(t *testing.T, id string, w *http.Response, opts ...func(*AssertOptions)) { + options := &AssertOptions{} + for _, opt := range opts { + opt(options) + } + body, err := httputil.DumpResponse(w, true) if err != nil { t.Fatal(err) } - isJSON := contentTypeIsJSON(w.Header.Get("Content-Type")) - isBinary := contentTypeIsBinary(w.Header.Get("Content-Type")) - assertHTTP(t, id, body, isJSON, isBinary) + // keep backward compatibility checking for JSON type when the response type + // wasn't provided + if options.ContentType == ContentType("") && contentTypeIsJSON(r.Header.Get("Content-Type")) { + options.ContentType = ContentTypeJSON + } + assertHTTP(t, id, body, options.ContentType) } // AssertHTTPRequestOut asserts the value of an http.Request. // Intended for use when testing outgoing client requests // See https://golang.org/pkg/net/http/httputil/#DumpRequestOut for more -func AssertHTTPRequestOut(t *testing.T, id string, r *http.Request) { +func AssertHTTPRequestOut(t *testing.T, id string, r *http.Request, opts ...func(*AssertOptions)) { + options := &AssertOptions{} + for _, opt := range opts { + opt(options) + } + body, err := httputil.DumpRequestOut(r, true) if err != nil { t.Fatal(err) } - isJSON := contentTypeIsJSON(r.Header.Get("Content-Type")) - isBinary := contentTypeIsBinary(r.Header.Get("Content-Type")) - assertHTTP(t, id, body, isJSON, isBinary) + // keep backward compatibility checking for JSON type when the content type + // wasn't provided + if options.ContentType == ContentType("") && contentTypeIsJSON(r.Header.Get("Content-Type")) { + options.ContentType = ContentTypeJSON + } + assertHTTP(t, id, body, options.ContentType) } // AssertHTTPRequest asserts the value of an http.Request. // Intended for use when testing incoming client requests // See https://golang.org/pkg/net/http/httputil/#DumpRequest for more -func AssertHTTPRequest(t *testing.T, id string, r *http.Request) { +func AssertHTTPRequest(t *testing.T, id string, r *http.Request, opts ...func(*AssertOptions)) { + options := &AssertOptions{} + for _, opt := range opts { + opt(options) + } + body, err := httputil.DumpRequest(r, true) if err != nil { t.Fatal(err) } - isJSON := contentTypeIsJSON(r.Header.Get("Content-Type")) - isBinary := contentTypeIsBinary(r.Header.Get("Content-Type")) - assertHTTP(t, id, body, isJSON, isBinary) + // keep backward compatibility checking for JSON type when the content type + // wasn't provided + if options.ContentType == ContentType("") && contentTypeIsJSON(r.Header.Get("Content-Type")) { + options.ContentType = ContentTypeJSON + } + assertHTTP(t, id, body, options.ContentType) } -func assertHTTP(t *testing.T, id string, body []byte, isJSON, isBinary bool) { +func assertHTTP(t *testing.T, id string, body []byte, contentType ContentType) { config, err := getConfig() if err != nil { t.Fatal(err) } var data string - if isBinary { + if contentType == ContentTypeBinary { data = hex.EncodeToString(body) } else { data = string(body) @@ -96,7 +142,7 @@ func assertHTTP(t *testing.T, id string, body []byte, isJSON, isBinary bool) { } // If the response body is JSON, indent. - if isJSON { + if contentType == ContentTypeJSON { jsonStr := lines[len(lines)-1] var jsonIface map[string]interface{} @@ -139,14 +185,6 @@ func contentTypeIsJSON(contentType string) bool { return isVendor && isJSON } -func contentTypeIsBinary(contentType string) bool { - contentTypeParts := strings.Split(contentType, ";") - firstPart := contentTypeParts[0] - - return firstPart == "application/pdf" || - firstPart == "application/octet-stream" -} - // AssertReader asserts the value of an io.Reader. func AssertReader(t *testing.T, id string, r io.Reader) { data, err := ioutil.ReadAll(r) diff --git a/assert_test.go b/assert_test.go index aee2c65..5ae7e87 100644 --- a/assert_test.go +++ b/assert_test.go @@ -27,29 +27,3 @@ func TestContentTypeIsJSON(test *testing.T) { } } } - -func TestContentTypeIsBinary(test *testing.T) { - contentTypeTestCases := map[string]bool{ - "application/pdf": true, - "application/octet-stream": true, - "application/json": false, - "application/json; charset=utf-8": false, - "application/vnd.foo.bar.v2+json": false, - "application/application/json": false, - "application/json/json": false, - "application/jsoner; charset=utf-8": false, - "application/jsoner": false, - "application/vnd.foo.bar.v2+jsoner": false, - "application/xml": false, - "text/html": false, - "": false, - } - - for input, expectedOutput := range contentTypeTestCases { - result := contentTypeIsBinary(input) - - if result != expectedOutput { - test.Errorf("contentTypeIsBinary(\"%s\" unexpected result. Got=%t, Want=%t", input, result, expectedOutput) - } - } -} From 9de9909d6024bb529dddfff4d8d146644df214bc Mon Sep 17 00:00:00 2001 From: Rafael Dantas Justo Date: Tue, 12 Nov 2019 16:43:24 +0000 Subject: [PATCH 3/3] Fix tests and typo in assert --- assert.go | 2 +- example/main_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assert.go b/assert.go index dc7703c..3b3999a 100644 --- a/assert.go +++ b/assert.go @@ -62,7 +62,7 @@ func AssertHTTPResponse(t *testing.T, id string, w *http.Response, opts ...func( // keep backward compatibility checking for JSON type when the response type // wasn't provided - if options.ContentType == ContentType("") && contentTypeIsJSON(r.Header.Get("Content-Type")) { + if options.ContentType == ContentType("") && contentTypeIsJSON(w.Header.Get("Content-Type")) { options.ContentType = ContentTypeJSON } assertHTTP(t, id, body, options.ContentType) diff --git a/example/main_test.go b/example/main_test.go index b54fab6..0a4b791 100644 --- a/example/main_test.go +++ b/example/main_test.go @@ -39,7 +39,7 @@ func TestRequests(t *testing.T) { w = httptest.NewRecorder() fifthHandler(w, req) res = w.Result() - abide.AssertHTTPResponse(t, "fifth route", res) + abide.AssertHTTPResponse(t, "fifth route", res, abide.SetContentType(abide.ContentTypeBinary)) } func TestReader(t *testing.T) {