Update go dependencies
This commit is contained in:
parent
f9624cbe46
commit
307bf76454
280 changed files with 54728 additions and 2991 deletions
588
vendor/gopkg.in/gavv/httpexpect.v2/response.go
generated
vendored
Normal file
588
vendor/gopkg.in/gavv/httpexpect.v2/response.go
generated
vendored
Normal file
|
|
@ -0,0 +1,588 @@
|
|||
package httpexpect
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ajg/form"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// StatusRange is enum for response status ranges.
|
||||
type StatusRange int
|
||||
|
||||
const (
|
||||
// Status1xx defines "Informational" status codes.
|
||||
Status1xx StatusRange = 100
|
||||
|
||||
// Status2xx defines "Success" status codes.
|
||||
Status2xx StatusRange = 200
|
||||
|
||||
// Status3xx defines "Redirection" status codes.
|
||||
Status3xx StatusRange = 300
|
||||
|
||||
// Status4xx defines "Client Error" status codes.
|
||||
Status4xx StatusRange = 400
|
||||
|
||||
// Status5xx defines "Server Error" status codes.
|
||||
Status5xx StatusRange = 500
|
||||
)
|
||||
|
||||
// Response provides methods to inspect attached http.Response object.
|
||||
type Response struct {
|
||||
config Config
|
||||
chain chain
|
||||
resp *http.Response
|
||||
content []byte
|
||||
cookies []*http.Cookie
|
||||
websocket *websocket.Conn
|
||||
rtt *time.Duration
|
||||
}
|
||||
|
||||
// NewResponse returns a new Response given a reporter used to report
|
||||
// failures and http.Response to be inspected.
|
||||
//
|
||||
// Both reporter and response should not be nil. If response is nil,
|
||||
// failure is reported.
|
||||
//
|
||||
// If rtt is given, it defines response round-trip time to be reported
|
||||
// by response.RoundTripTime().
|
||||
func NewResponse(
|
||||
reporter Reporter, response *http.Response, rtt ...time.Duration,
|
||||
) *Response {
|
||||
var rttPtr *time.Duration
|
||||
if len(rtt) > 0 {
|
||||
rttPtr = &rtt[0]
|
||||
}
|
||||
return makeResponse(responseOpts{
|
||||
chain: makeChain(reporter),
|
||||
response: response,
|
||||
rtt: rttPtr,
|
||||
})
|
||||
}
|
||||
|
||||
type responseOpts struct {
|
||||
config Config
|
||||
chain chain
|
||||
response *http.Response
|
||||
websocket *websocket.Conn
|
||||
rtt *time.Duration
|
||||
}
|
||||
|
||||
func makeResponse(opts responseOpts) *Response {
|
||||
var content []byte
|
||||
var cookies []*http.Cookie
|
||||
if opts.response != nil {
|
||||
content = getContent(&opts.chain, opts.response)
|
||||
cookies = opts.response.Cookies()
|
||||
} else {
|
||||
opts.chain.fail("expected non-nil response")
|
||||
}
|
||||
return &Response{
|
||||
config: opts.config,
|
||||
chain: opts.chain,
|
||||
resp: opts.response,
|
||||
content: content,
|
||||
cookies: cookies,
|
||||
websocket: opts.websocket,
|
||||
rtt: opts.rtt,
|
||||
}
|
||||
}
|
||||
|
||||
func getContent(chain *chain, resp *http.Response) []byte {
|
||||
if resp.Body == nil {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
chain.fail(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
// Raw returns underlying http.Response object.
|
||||
// This is the value originally passed to NewResponse.
|
||||
func (r *Response) Raw() *http.Response {
|
||||
return r.resp
|
||||
}
|
||||
|
||||
// RoundTripTime returns a new Duration object that may be used to inspect
|
||||
// the round-trip time.
|
||||
//
|
||||
// The returned duration is a time interval starting just before request is
|
||||
// sent and ending right after response is received (handshake finished for
|
||||
// WebSocket request), retrieved from a monotonic clock source.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response, time.Duration(10000000))
|
||||
// resp.RoundTripTime().Lt(10 * time.Millisecond)
|
||||
func (r *Response) RoundTripTime() *Duration {
|
||||
return &Duration{r.chain, r.rtt}
|
||||
}
|
||||
|
||||
// Deprecated: use RoundTripTime instead.
|
||||
func (r *Response) Duration() *Number {
|
||||
if r.rtt == nil {
|
||||
return &Number{r.chain, 0}
|
||||
}
|
||||
return &Number{r.chain, float64(*r.rtt)}
|
||||
}
|
||||
|
||||
// Status succeeds if response contains given status code.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response)
|
||||
// resp.Status(http.StatusOK)
|
||||
func (r *Response) Status(status int) *Response {
|
||||
if r.chain.failed() {
|
||||
return r
|
||||
}
|
||||
r.checkEqual("status", statusCodeText(status), statusCodeText(r.resp.StatusCode))
|
||||
return r
|
||||
}
|
||||
|
||||
// StatusRange succeeds if response status belongs to given range.
|
||||
//
|
||||
// Supported ranges:
|
||||
// - Status1xx - Informational
|
||||
// - Status2xx - Success
|
||||
// - Status3xx - Redirection
|
||||
// - Status4xx - Client Error
|
||||
// - Status5xx - Server Error
|
||||
//
|
||||
// See https://en.wikipedia.org/wiki/List_of_HTTP_status_codes.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response)
|
||||
// resp.StatusRange(Status2xx)
|
||||
func (r *Response) StatusRange(rn StatusRange) *Response {
|
||||
if r.chain.failed() {
|
||||
return r
|
||||
}
|
||||
|
||||
status := statusCodeText(r.resp.StatusCode)
|
||||
|
||||
actual := statusRangeText(r.resp.StatusCode)
|
||||
expected := statusRangeText(int(rn))
|
||||
|
||||
if actual == "" || actual != expected {
|
||||
if actual == "" {
|
||||
r.chain.fail("\nexpected status from range:\n %q\n\nbut got:\n %q",
|
||||
expected, status)
|
||||
} else {
|
||||
r.chain.fail(
|
||||
"\nexpected status from range:\n %q\n\nbut got:\n %q (%q)",
|
||||
expected, actual, status)
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func statusCodeText(code int) string {
|
||||
if s := http.StatusText(code); s != "" {
|
||||
return strconv.Itoa(code) + " " + s
|
||||
}
|
||||
return strconv.Itoa(code)
|
||||
}
|
||||
|
||||
func statusRangeText(code int) string {
|
||||
switch {
|
||||
case code >= 100 && code < 200:
|
||||
return "1xx Informational"
|
||||
case code >= 200 && code < 300:
|
||||
return "2xx Success"
|
||||
case code >= 300 && code < 400:
|
||||
return "3xx Redirection"
|
||||
case code >= 400 && code < 500:
|
||||
return "4xx Client Error"
|
||||
case code >= 500 && code < 600:
|
||||
return "5xx Server Error"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Headers returns a new Object that may be used to inspect header map.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response)
|
||||
// resp.Headers().Value("Content-Type").String().Equal("application-json")
|
||||
func (r *Response) Headers() *Object {
|
||||
var value map[string]interface{}
|
||||
if !r.chain.failed() {
|
||||
value, _ = canonMap(&r.chain, r.resp.Header)
|
||||
}
|
||||
return &Object{r.chain, value}
|
||||
}
|
||||
|
||||
// Header returns a new String object that may be used to inspect given header.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response)
|
||||
// resp.Header("Content-Type").Equal("application-json")
|
||||
// resp.Header("Date").DateTime().Le(time.Now())
|
||||
func (r *Response) Header(header string) *String {
|
||||
value := ""
|
||||
if !r.chain.failed() {
|
||||
value = r.resp.Header.Get(header)
|
||||
}
|
||||
return &String{r.chain, value}
|
||||
}
|
||||
|
||||
// Cookies returns a new Array object with all cookie names set by this response.
|
||||
// Returned Array contains a String value for every cookie name.
|
||||
//
|
||||
// Note that this returns only cookies set by Set-Cookie headers of this response.
|
||||
// It doesn't return session cookies from previous responses, which may be stored
|
||||
// in a cookie jar.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response)
|
||||
// resp.Cookies().Contains("session")
|
||||
func (r *Response) Cookies() *Array {
|
||||
if r.chain.failed() {
|
||||
return &Array{r.chain, nil}
|
||||
}
|
||||
names := []interface{}{}
|
||||
for _, c := range r.cookies {
|
||||
names = append(names, c.Name)
|
||||
}
|
||||
return &Array{r.chain, names}
|
||||
}
|
||||
|
||||
// Cookie returns a new Cookie object that may be used to inspect given cookie
|
||||
// set by this response.
|
||||
//
|
||||
// Note that this returns only cookies set by Set-Cookie headers of this response.
|
||||
// It doesn't return session cookies from previous responses, which may be stored
|
||||
// in a cookie jar.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response)
|
||||
// resp.Cookie("session").Domain().Equal("example.com")
|
||||
func (r *Response) Cookie(name string) *Cookie {
|
||||
if r.chain.failed() {
|
||||
return &Cookie{r.chain, nil}
|
||||
}
|
||||
names := []string{}
|
||||
for _, c := range r.cookies {
|
||||
if c.Name == name {
|
||||
return &Cookie{r.chain, c}
|
||||
}
|
||||
names = append(names, c.Name)
|
||||
}
|
||||
r.chain.fail("\nexpected response with cookie:\n %q\n\nbut got only cookies:\n%s",
|
||||
name, dumpValue(names))
|
||||
return &Cookie{r.chain, nil}
|
||||
}
|
||||
|
||||
// Websocket returns Websocket object that can be used to interact with
|
||||
// WebSocket server.
|
||||
//
|
||||
// May be called only if the WithWebsocketUpgrade was called on the request.
|
||||
// That is responsibility of the caller to explicitly close the websocket after use.
|
||||
//
|
||||
// Example:
|
||||
// req := NewRequest(config, "GET", "/path")
|
||||
// req.WithWebsocketUpgrade()
|
||||
// ws := req.Expect().Websocket()
|
||||
// defer ws.Disconnect()
|
||||
func (r *Response) Websocket() *Websocket {
|
||||
if !r.chain.failed() && r.websocket == nil {
|
||||
r.chain.fail("\nunexpected Websocket call for non-WebSocket response")
|
||||
}
|
||||
return makeWebsocket(r.config, r.chain, r.websocket)
|
||||
}
|
||||
|
||||
// Body returns a new String object that may be used to inspect response body.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response)
|
||||
// resp.Body().NotEmpty()
|
||||
// resp.Body().Length().Equal(100)
|
||||
func (r *Response) Body() *String {
|
||||
return &String{r.chain, string(r.content)}
|
||||
}
|
||||
|
||||
// NoContent succeeds if response contains empty Content-Type header and
|
||||
// empty body.
|
||||
func (r *Response) NoContent() *Response {
|
||||
if r.chain.failed() {
|
||||
return r
|
||||
}
|
||||
|
||||
contentType := r.resp.Header.Get("Content-Type")
|
||||
|
||||
r.checkEqual("\"Content-Type\" header", "", contentType)
|
||||
r.checkEqual("body", "", string(r.content))
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// ContentType succeeds if response contains Content-Type header with given
|
||||
// media type and charset.
|
||||
//
|
||||
// If charset is omitted, and mediaType is non-empty, Content-Type header
|
||||
// should contain empty or utf-8 charset.
|
||||
//
|
||||
// If charset is omitted, and mediaType is also empty, Content-Type header
|
||||
// should contain no charset.
|
||||
func (r *Response) ContentType(mediaType string, charset ...string) *Response {
|
||||
r.checkContentType(mediaType, charset...)
|
||||
return r
|
||||
}
|
||||
|
||||
// ContentEncoding succeeds if response has exactly given Content-Encoding list.
|
||||
// Common values are empty, "gzip", "compress", "deflate", "identity" and "br".
|
||||
func (r *Response) ContentEncoding(encoding ...string) *Response {
|
||||
if r.chain.failed() {
|
||||
return r
|
||||
}
|
||||
r.checkEqual("\"Content-Encoding\" header", encoding, r.resp.Header["Content-Encoding"])
|
||||
return r
|
||||
}
|
||||
|
||||
// TransferEncoding succeeds if response contains given Transfer-Encoding list.
|
||||
// Common values are empty, "chunked" and "identity".
|
||||
func (r *Response) TransferEncoding(encoding ...string) *Response {
|
||||
if r.chain.failed() {
|
||||
return r
|
||||
}
|
||||
r.checkEqual("\"Transfer-Encoding\" header", encoding, r.resp.TransferEncoding)
|
||||
return r
|
||||
}
|
||||
|
||||
// ContentOpts define parameters for matching the response content parameters.
|
||||
type ContentOpts struct {
|
||||
// The media type Content-Type part, e.g. "application/json"
|
||||
MediaType string
|
||||
// The character set Content-Type part, e.g. "utf-8"
|
||||
Charset string
|
||||
}
|
||||
|
||||
// Text returns a new String object that may be used to inspect response body.
|
||||
//
|
||||
// Text succeeds if response contains "text/plain" Content-Type header
|
||||
// with empty or "utf-8" charset.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response)
|
||||
// resp.Text().Equal("hello, world!")
|
||||
// resp.Text(ContentOpts{
|
||||
// MediaType: "text/plain",
|
||||
// }).Equal("hello, world!")
|
||||
func (r *Response) Text(opts ...ContentOpts) *String {
|
||||
var content string
|
||||
|
||||
if !r.chain.failed() && r.checkContentOpts(opts, "text/plain") {
|
||||
content = string(r.content)
|
||||
}
|
||||
|
||||
return &String{r.chain, content}
|
||||
}
|
||||
|
||||
// Form returns a new Object that may be used to inspect form contents
|
||||
// of response.
|
||||
//
|
||||
// Form succeeds if response contains "application/x-www-form-urlencoded"
|
||||
// Content-Type header and if form may be decoded from response body.
|
||||
// Decoding is performed using https://github.com/ajg/form.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response)
|
||||
// resp.Form().Value("foo").Equal("bar")
|
||||
// resp.Form(ContentOpts{
|
||||
// MediaType: "application/x-www-form-urlencoded",
|
||||
// }).Value("foo").Equal("bar")
|
||||
func (r *Response) Form(opts ...ContentOpts) *Object {
|
||||
object := r.getForm(opts...)
|
||||
return &Object{r.chain, object}
|
||||
}
|
||||
|
||||
func (r *Response) getForm(opts ...ContentOpts) map[string]interface{} {
|
||||
if r.chain.failed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !r.checkContentOpts(opts, "application/x-www-form-urlencoded", "") {
|
||||
return nil
|
||||
}
|
||||
|
||||
decoder := form.NewDecoder(bytes.NewReader(r.content))
|
||||
|
||||
var object map[string]interface{}
|
||||
if err := decoder.Decode(&object); err != nil {
|
||||
r.chain.fail(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
// JSON returns a new Value object that may be used to inspect JSON contents
|
||||
// of response.
|
||||
//
|
||||
// JSON succeeds if response contains "application/json" Content-Type header
|
||||
// with empty or "utf-8" charset and if JSON may be decoded from response body.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response)
|
||||
// resp.JSON().Array().Elements("foo", "bar")
|
||||
// resp.JSON(ContentOpts{
|
||||
// MediaType: "application/json",
|
||||
// }).Array.Elements("foo", "bar")
|
||||
func (r *Response) JSON(opts ...ContentOpts) *Value {
|
||||
value := r.getJSON(opts...)
|
||||
return &Value{r.chain, value}
|
||||
}
|
||||
|
||||
func (r *Response) getJSON(opts ...ContentOpts) interface{} {
|
||||
if r.chain.failed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !r.checkContentOpts(opts, "application/json") {
|
||||
return nil
|
||||
}
|
||||
|
||||
var value interface{}
|
||||
if err := json.Unmarshal(r.content, &value); err != nil {
|
||||
r.chain.fail(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// JSONP returns a new Value object that may be used to inspect JSONP contents
|
||||
// of response.
|
||||
//
|
||||
// JSONP succeeds if response contains "application/javascript" Content-Type
|
||||
// header with empty or "utf-8" charset and response body of the following form:
|
||||
// callback(<valid json>);
|
||||
// or:
|
||||
// callback(<valid json>)
|
||||
//
|
||||
// Whitespaces are allowed.
|
||||
//
|
||||
// Example:
|
||||
// resp := NewResponse(t, response)
|
||||
// resp.JSONP("myCallback").Array().Elements("foo", "bar")
|
||||
// resp.JSONP("myCallback", ContentOpts{
|
||||
// MediaType: "application/javascript",
|
||||
// }).Array.Elements("foo", "bar")
|
||||
func (r *Response) JSONP(callback string, opts ...ContentOpts) *Value {
|
||||
value := r.getJSONP(callback, opts...)
|
||||
return &Value{r.chain, value}
|
||||
}
|
||||
|
||||
var (
|
||||
jsonp = regexp.MustCompile(`^\s*([^\s(]+)\s*\((.*)\)\s*;*\s*$`)
|
||||
)
|
||||
|
||||
func (r *Response) getJSONP(callback string, opts ...ContentOpts) interface{} {
|
||||
if r.chain.failed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !r.checkContentOpts(opts, "application/javascript") {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := jsonp.FindSubmatch(r.content)
|
||||
if len(m) != 3 || string(m[1]) != callback {
|
||||
r.chain.fail(
|
||||
"\nexpected JSONP body in form of:\n \"%s(<valid json>)\"\n\nbut got:\n %q\n",
|
||||
callback,
|
||||
string(r.content))
|
||||
return nil
|
||||
}
|
||||
|
||||
var value interface{}
|
||||
if err := json.Unmarshal(m[2], &value); err != nil {
|
||||
r.chain.fail(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func (r *Response) checkContentOpts(
|
||||
opts []ContentOpts, expectedType string, expectedCharset ...string,
|
||||
) bool {
|
||||
if len(opts) != 0 {
|
||||
if opts[0].MediaType != "" {
|
||||
expectedType = opts[0].MediaType
|
||||
}
|
||||
if opts[0].Charset != "" {
|
||||
expectedCharset = []string{opts[0].Charset}
|
||||
}
|
||||
}
|
||||
return r.checkContentType(expectedType, expectedCharset...)
|
||||
}
|
||||
|
||||
func (r *Response) checkContentType(expectedType string, expectedCharset ...string) bool {
|
||||
if r.chain.failed() {
|
||||
return false
|
||||
}
|
||||
|
||||
contentType := r.resp.Header.Get("Content-Type")
|
||||
|
||||
if expectedType == "" && len(expectedCharset) == 0 {
|
||||
if contentType == "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
mediaType, params, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
r.chain.fail("\ngot invalid \"Content-Type\" header %q", contentType)
|
||||
return false
|
||||
}
|
||||
|
||||
if mediaType != expectedType {
|
||||
r.chain.fail(
|
||||
"\nexpected \"Content-Type\" header with %q media type,"+
|
||||
"\nbut got %q", expectedType, mediaType)
|
||||
return false
|
||||
}
|
||||
|
||||
charset := params["charset"]
|
||||
|
||||
if len(expectedCharset) == 0 {
|
||||
if charset != "" && !strings.EqualFold(charset, "utf-8") {
|
||||
r.chain.fail(
|
||||
"\nexpected \"Content-Type\" header with \"utf-8\" or empty charset,"+
|
||||
"\nbut got %q", charset)
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if !strings.EqualFold(charset, expectedCharset[0]) {
|
||||
r.chain.fail(
|
||||
"\nexpected \"Content-Type\" header with %q charset,"+
|
||||
"\nbut got %q", expectedCharset[0], charset)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *Response) checkEqual(what string, expected, actual interface{}) {
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
r.chain.fail("\nexpected %s equal to:\n%s\n\nbut got:\n%s", what,
|
||||
dumpValue(expected), dumpValue(actual))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue