ingress-nginx-helm/vendor/gopkg.in/gavv/httpexpect.v2/expect.go

464 lines
14 KiB
Go
Raw Normal View History

2020-02-19 03:10:16 +00:00
// Package httpexpect helps with end-to-end HTTP and REST API testing.
//
// Usage examples
//
// See example directory:
// - https://godoc.org/github.com/gavv/httpexpect/_examples
// - https://github.com/gavv/httpexpect/tree/master/_examples
//
// Communication mode
//
// There are two common ways to test API with httpexpect:
// - start HTTP server and instruct httpexpect to use HTTP client for communication
// - don't start server and instruct httpexpect to invoke http handler directly
//
// The second approach works only if the server is a Go module and its handler can
// be imported in tests.
//
// Concrete behaviour is determined by Client implementation passed to Config struct.
// If you're using http.Client, set its Transport field (http.RoundTriper) to one of
// the following:
// 1. default (nil) - use HTTP transport from net/http (you should start server)
// 2. httpexpect.Binder - invoke given http.Handler directly
// 3. httpexpect.FastBinder - invoke given fasthttp.RequestHandler directly
//
// Note that http handler can be usually obtained from http framework you're using.
// E.g., echo framework provides either http.Handler or fasthttp.RequestHandler.
//
// You can also provide your own implementation of RequestFactory (creates http.Request),
// or Client (gets http.Request and returns http.Response).
//
// If you're starting server from tests, it's very handy to use net/http/httptest.
//
// Value equality
//
// Whenever values are checked for equality in httpexpect, they are converted
// to "canonical form":
// - structs are converted to map[string]interface{}
// - type aliases are removed
// - numeric types are converted to float64
// - non-nil interfaces pointing to nil slices and maps are replaced with
// nil interfaces
//
// This is equivalent to subsequently json.Marshal() and json.Unmarshal() the value
// and currently is implemented so.
//
// Failure handling
//
// When some check fails, failure is reported. If non-fatal failures are used
// (see Reporter interface), execution is continued and instance that was checked
// is marked as failed.
//
// If specific instance is marked as failed, all subsequent checks are ignored
// for this instance and for any child instances retrieved after failure.
//
// Example:
// array := NewArray(NewAssertReporter(t), []interface{}{"foo", 123})
//
// e0 := array.Element(0) // success
// e1 := array.Element(1) // success
//
// s0 := e0.String() // success
// s1 := e1.String() // failure; e1 and s1 are marked as failed, e0 and s0 are not
//
// s0.Equal("foo") // success
// s1.Equal("bar") // this check is ignored because s1 is marked as failed
package httpexpect
import (
"io"
"net/http"
"net/http/cookiejar"
"time"
"github.com/gorilla/websocket"
"golang.org/x/net/publicsuffix"
)
// Expect is a toplevel object that contains user Config and allows
// to construct Request objects.
type Expect struct {
config Config
builders []func(*Request)
matchers []func(*Response)
}
// Config contains various settings.
type Config struct {
// BaseURL is a URL to prepended to all request. My be empty. If
// non-empty, trailing slash is allowed but not required and is
// appended automatically.
BaseURL string
// RequestFactory is used to pass in a custom *http.Request generation func.
// May be nil.
//
// You can use DefaultRequestFactory, or provide custom implementation.
// Useful for Google App Engine testing for example.
RequestFactory RequestFactory
// Client is used to send http.Request and receive http.Response.
// Should not be nil.
//
// You can use http.DefaultClient or http.Client, or provide
// custom implementation.
Client Client
// WebsocketDialer is used to establish websocket.Conn and receive
// http.Response of handshake result.
// Should not be nil.
//
// You can use websocket.DefaultDialer or websocket.Dialer, or provide
// custom implementation.
WebsocketDialer WebsocketDialer
// Reporter is used to report failures.
// Should not be nil.
//
// You can use AssertReporter, RequireReporter (they use testify),
// or testing.TB, or provide custom implementation.
Reporter Reporter
// Printers are used to print requests and responses.
// May be nil.
//
// You can use CompactPrinter, DebugPrinter, CurlPrinter, or provide
// custom implementation.
//
// You can also use builtin printers with alternative Logger if
// you're happy with their format, but want to send logs somewhere
// else instead of testing.TB.
Printers []Printer
}
// RequestFactory is used to create all http.Request objects.
// aetest.Instance from the Google App Engine implements this interface.
type RequestFactory interface {
NewRequest(method, urlStr string, body io.Reader) (*http.Request, error)
}
// Client is used to send http.Request and receive http.Response.
// http.Client implements this interface.
//
// Binder and FastBinder may be used to obtain this interface implementation.
//
// Example:
// httpBinderClient := &http.Client{
// Transport: httpexpect.NewBinder(HTTPHandler),
// }
// fastBinderClient := &http.Client{
// Transport: httpexpect.NewFastBinder(FastHTTPHandler),
// }
type Client interface {
// Do sends request and returns response.
Do(*http.Request) (*http.Response, error)
}
// WebsocketDialer is used to establish websocket.Conn and receive http.Response
// of handshake result.
// websocket.Dialer implements this interface.
//
// NewWebsocketDialer and NewFastWebsocketDialer may be used to obtain this
// interface implementation.
//
// Example:
// e := httpexpect.WithConfig(httpexpect.Config{
// BaseURL: "http://example.com",
// WebsocketDialer: httpexpect.NewWebsocketDialer(myHandler),
// })
type WebsocketDialer interface {
// Dial establishes new WebSocket connection and returns response
// of handshake result.
Dial(url string, reqH http.Header) (*websocket.Conn, *http.Response, error)
}
// Printer is used to print requests and responses.
// CompactPrinter, DebugPrinter, and CurlPrinter implement this interface.
type Printer interface {
// Request is called before request is sent.
Request(*http.Request)
// Response is called after response is received.
Response(*http.Response, time.Duration)
}
// WebsocketPrinter is used to print writes and reads of WebSocket connection.
//
// If WebSocket connection is used, all Printers that also implement WebsocketPrinter
// are invoked on every WebSocket message read or written.
//
// DebugPrinter implements this interface.
type WebsocketPrinter interface {
Printer
// WebsocketWrite is called before writes to WebSocket connection.
WebsocketWrite(typ int, content []byte, closeCode int)
// WebsocketRead is called after reads from WebSocket connection.
WebsocketRead(typ int, content []byte, closeCode int)
}
// Logger is used as output backend for Printer.
// testing.TB implements this interface.
type Logger interface {
// Logf writes message to log.
Logf(fmt string, args ...interface{})
}
// Reporter is used to report failures.
// testing.TB, AssertReporter, and RequireReporter implement this interface.
type Reporter interface {
// Errorf reports failure.
// Allowed to return normally or terminate test using t.FailNow().
Errorf(message string, args ...interface{})
}
// LoggerReporter combines Logger and Reporter interfaces.
type LoggerReporter interface {
Logger
Reporter
}
// DefaultRequestFactory is the default RequestFactory implementation which just
// calls http.NewRequest.
type DefaultRequestFactory struct{}
// NewRequest implements RequestFactory.NewRequest.
func (DefaultRequestFactory) NewRequest(
method, urlStr string, body io.Reader) (*http.Request, error) {
return http.NewRequest(method, urlStr, body)
}
// New returns a new Expect object.
//
// baseURL specifies URL to prepended to all request. My be empty. If non-empty,
// trailing slash is allowed but not required and is appended automatically.
//
// New is a shorthand for WithConfig. It uses:
// - CompactPrinter as Printer, with testing.TB as Logger
// - AssertReporter as Reporter
// - DefaultRequestFactory as RequestFactory
//
// Client is set to a default client with a non-nil Jar:
// &http.Client{
// Jar: httpexpect.NewJar(),
// }
//
// Example:
// func TestSomething(t *testing.T) {
// e := httpexpect.New(t, "http://example.com/")
//
// e.GET("/path").
// Expect().
// Status(http.StatusOK)
// }
func New(t LoggerReporter, baseURL string) *Expect {
return WithConfig(Config{
BaseURL: baseURL,
Reporter: NewAssertReporter(t),
Printers: []Printer{
NewCompactPrinter(t),
},
})
}
// WithConfig returns a new Expect object with given config.
//
// Reporter should not be nil.
//
// If RequestFactory is nil, it's set to a DefaultRequestFactory instance.
//
// If Client is nil, it's set to a default client with a non-nil Jar:
// &http.Client{
// Jar: httpexpect.NewJar(),
// }
//
// If WebsocketDialer is nil, it's set to a default dialer:
// &websocket.Dialer{}
//
// Example:
// func TestSomething(t *testing.T) {
// e := httpexpect.WithConfig(httpexpect.Config{
// BaseURL: "http://example.com/",
// Client: &http.Client{
// Transport: httpexpect.NewBinder(myHandler()),
// Jar: httpexpect.NewJar(),
// },
// Reporter: httpexpect.NewAssertReporter(t),
// Printers: []httpexpect.Printer{
// httpexpect.NewCurlPrinter(t),
// httpexpect.NewDebugPrinter(t, true)
// },
// })
//
// e.GET("/path").
// Expect().
// Status(http.StatusOK)
// }
func WithConfig(config Config) *Expect {
if config.Reporter == nil {
panic("config.Reporter is nil")
}
if config.RequestFactory == nil {
config.RequestFactory = DefaultRequestFactory{}
}
if config.Client == nil {
config.Client = &http.Client{
Jar: NewJar(),
}
}
if config.WebsocketDialer == nil {
config.WebsocketDialer = &websocket.Dialer{}
}
return &Expect{
config: config,
}
}
// NewJar returns a new http.CookieJar.
//
// Returned jar is implemented in net/http/cookiejar. PublicSuffixList is
// implemented in golang.org/x/net/publicsuffix.
//
// Note that this jar ignores cookies when request url is empty.
func NewJar() http.CookieJar {
jar, err := cookiejar.New(&cookiejar.Options{
PublicSuffixList: publicsuffix.List,
})
if err != nil {
panic(err)
}
return jar
}
// Builder returns a copy of Expect instance with given builder attached to it.
// Returned copy contains all previously attached builders plus a new one.
// Builders are invoked from Request method, after constructing every new request.
//
// Example:
// e := httpexpect.New(t, "http://example.com")
//
// token := e.POST("/login").WithForm(Login{"ford", "betelgeuse7"}).
// Expect().
// Status(http.StatusOK).JSON().Object().Value("token").String().Raw()
//
// auth := e.Builder(func (req *httpexpect.Request) {
// req.WithHeader("Authorization", "Bearer "+token)
// })
//
// auth.GET("/restricted").
// Expect().
// Status(http.StatusOK)
func (e *Expect) Builder(builder func(*Request)) *Expect {
ret := *e
ret.builders = append(e.builders, builder)
return &ret
}
// Matcher returns a copy of Expect instance with given matcher attached to it.
// Returned copy contains all previously attached matchers plus a new one.
// Matchers are invoked from Request.Expect method, after retrieving a new response.
//
// Example:
// e := httpexpect.New(t, "http://example.com")
//
// m := e.Matcher(func (resp *httpexpect.Response) {
// resp.Header("API-Version").NotEmpty()
// })
//
// m.GET("/some-path").
// Expect().
// Status(http.StatusOK)
//
// m.GET("/bad-path").
// Expect().
// Status(http.StatusNotFound)
func (e *Expect) Matcher(matcher func(*Response)) *Expect {
ret := *e
ret.matchers = append(e.matchers, matcher)
return &ret
}
// Request returns a new Request object.
// Arguments a similar to NewRequest.
// After creating request, all builders attached to Expect object are invoked.
// See Builder.
func (e *Expect) Request(method, path string, pathargs ...interface{}) *Request {
req := NewRequest(e.config, method, path, pathargs...)
for _, builder := range e.builders {
builder(req)
}
for _, matcher := range e.matchers {
req.WithMatcher(matcher)
}
return req
}
// OPTIONS is a shorthand for e.Request("OPTIONS", path, pathargs...).
func (e *Expect) OPTIONS(path string, pathargs ...interface{}) *Request {
return e.Request("OPTIONS", path, pathargs...)
}
// HEAD is a shorthand for e.Request("HEAD", path, pathargs...).
func (e *Expect) HEAD(path string, pathargs ...interface{}) *Request {
return e.Request("HEAD", path, pathargs...)
}
// GET is a shorthand for e.Request("GET", path, pathargs...).
func (e *Expect) GET(path string, pathargs ...interface{}) *Request {
return e.Request("GET", path, pathargs...)
}
// POST is a shorthand for e.Request("POST", path, pathargs...).
func (e *Expect) POST(path string, pathargs ...interface{}) *Request {
return e.Request("POST", path, pathargs...)
}
// PUT is a shorthand for e.Request("PUT", path, pathargs...).
func (e *Expect) PUT(path string, pathargs ...interface{}) *Request {
return e.Request("PUT", path, pathargs...)
}
// PATCH is a shorthand for e.Request("PATCH", path, pathargs...).
func (e *Expect) PATCH(path string, pathargs ...interface{}) *Request {
return e.Request("PATCH", path, pathargs...)
}
// DELETE is a shorthand for e.Request("DELETE", path, pathargs...).
func (e *Expect) DELETE(path string, pathargs ...interface{}) *Request {
return e.Request("DELETE", path, pathargs...)
}
// Value is a shorthand for NewValue(e.config.Reporter, value).
func (e *Expect) Value(value interface{}) *Value {
return NewValue(e.config.Reporter, value)
}
// Object is a shorthand for NewObject(e.config.Reporter, value).
func (e *Expect) Object(value map[string]interface{}) *Object {
return NewObject(e.config.Reporter, value)
}
// Array is a shorthand for NewArray(e.config.Reporter, value).
func (e *Expect) Array(value []interface{}) *Array {
return NewArray(e.config.Reporter, value)
}
// String is a shorthand for NewString(e.config.Reporter, value).
func (e *Expect) String(value string) *String {
return NewString(e.config.Reporter, value)
}
// Number is a shorthand for NewNumber(e.config.Reporter, value).
func (e *Expect) Number(value float64) *Number {
return NewNumber(e.config.Reporter, value)
}
// Boolean is a shorthand for NewBoolean(e.config.Reporter, value).
func (e *Expect) Boolean(value bool) *Boolean {
return NewBoolean(e.config.Reporter, value)
}