Files
acme-dns/vendor/github.com/gavv/httpexpect/expect_test.go
2018-01-22 11:19:33 +02:00

867 lines
19 KiB
Go

package httpexpect
import (
"bufio"
"crypto/tls"
"encoding/json"
"errors"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"path"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttpadaptor"
)
func TestExpectMethods(t *testing.T) {
client := &mockClient{}
reporter := NewAssertReporter(t)
config := Config{
BaseURL: "http://example.com",
Client: client,
Reporter: reporter,
}
var reqs [8]*Request
e := WithConfig(config)
reqs[0] = e.Request("METHOD", "/url")
reqs[1] = e.OPTIONS("/url")
reqs[2] = e.HEAD("/url")
reqs[3] = e.GET("/url")
reqs[4] = e.POST("/url")
reqs[5] = e.PUT("/url")
reqs[6] = e.PATCH("/url")
reqs[7] = e.DELETE("/url")
assert.Equal(t, "METHOD", reqs[0].http.Method)
assert.Equal(t, "OPTIONS", reqs[1].http.Method)
assert.Equal(t, "HEAD", reqs[2].http.Method)
assert.Equal(t, "GET", reqs[3].http.Method)
assert.Equal(t, "POST", reqs[4].http.Method)
assert.Equal(t, "PUT", reqs[5].http.Method)
assert.Equal(t, "PATCH", reqs[6].http.Method)
assert.Equal(t, "DELETE", reqs[7].http.Method)
}
func TestExpectBuilders(t *testing.T) {
client := &mockClient{}
reporter := NewAssertReporter(t)
config := Config{
Client: client,
Reporter: reporter,
}
e := WithConfig(config)
var reqs1 []*Request
e1 := e.Builder(func(r *Request) {
reqs1 = append(reqs1, r)
})
var reqs2 []*Request
e2 := e1.Builder(func(r *Request) {
reqs2 = append(reqs2, r)
})
e.Request("METHOD", "/url")
r1 := e1.Request("METHOD", "/url")
r2 := e2.Request("METHOD", "/url")
assert.Equal(t, 2, int(len(reqs1)))
assert.Equal(t, 1, int(len(reqs2)))
assert.Equal(t, r1, reqs1[0])
assert.Equal(t, r2, reqs1[1])
assert.Equal(t, r1, reqs2[0])
}
func TestExpectValues(t *testing.T) {
client := &mockClient{}
r := NewAssertReporter(t)
config := Config{
Client: client,
Reporter: r,
}
e := WithConfig(config)
m := map[string]interface{}{}
a := []interface{}{}
s := ""
n := 0.0
b := false
assert.Equal(t, NewValue(r, m), e.Value(m))
assert.Equal(t, NewObject(r, m), e.Object(m))
assert.Equal(t, NewArray(r, a), e.Array(a))
assert.Equal(t, NewString(r, s), e.String(s))
assert.Equal(t, NewNumber(r, n), e.Number(n))
assert.Equal(t, NewBoolean(r, b), e.Boolean(b))
}
func TestExpectTraverse(t *testing.T) {
client := &mockClient{}
reporter := NewAssertReporter(t)
config := Config{
BaseURL: "http://example.com",
Client: client,
Reporter: reporter,
}
data := map[string]interface{}{
"foo": []interface{}{"bar", 123, false, nil},
"bar": "hello",
"baz": 456,
}
resp := WithConfig(config).GET("/url").WithJSON(data).Expect()
m := resp.JSON().Object()
m.Equal(data)
m.ContainsKey("foo")
m.ContainsKey("bar")
m.ContainsKey("foo")
m.ValueEqual("foo", data["foo"])
m.ValueEqual("bar", data["bar"])
m.ValueEqual("baz", data["baz"])
m.Keys().ContainsOnly("foo", "bar", "baz")
m.Values().ContainsOnly(data["foo"], data["bar"], data["baz"])
m.Value("foo").Array().Elements("bar", 123, false, nil)
m.Value("bar").String().Equal("hello")
m.Value("baz").Number().Equal(456)
m.Value("foo").Array().Element(2).Boolean().False()
m.Value("foo").Array().Element(3).Null()
}
func TestExpectBranches(t *testing.T) {
client := &mockClient{}
config := Config{
BaseURL: "http://example.com",
Client: client,
Reporter: newMockReporter(t),
}
data := map[string]interface{}{
"foo": []interface{}{"bar", 123, false, nil},
"bar": "hello",
"baz": 456,
}
resp := WithConfig(config).GET("/url").WithJSON(data).Expect()
m1 := resp.JSON().Array()
m2 := resp.JSON().Object()
e1 := m2.Value("foo").Object()
e2 := m2.Value("foo").Array().Element(999).String()
e3 := m2.Value("foo").Array().Element(0).Number()
e4 := m2.Value("foo").Array().Element(0).String()
e4.Equal("bar")
m1.chain.assertFailed(t)
m2.chain.assertOK(t)
e1.chain.assertFailed(t)
e2.chain.assertFailed(t)
e3.chain.assertFailed(t)
e4.chain.assertOK(t)
}
func TestExpectStdCompat(_ *testing.T) {
New(&testing.T{}, "")
New(&testing.B{}, "")
New(testing.TB(&testing.T{}), "")
}
type testRequestFactory struct {
lastreq *http.Request
fail bool
}
func (f *testRequestFactory) NewRequest(
method, urlStr string, body io.Reader) (*http.Request, error) {
if f.fail {
return nil, errors.New("testRequestFactory")
}
f.lastreq = httptest.NewRequest(method, urlStr, body)
return f.lastreq, nil
}
func TestExpectRequestFactory(t *testing.T) {
e1 := WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
})
r1 := e1.Request("GET", "/")
r1.chain.assertOK(t)
assert.NotNil(t, r1.http)
f2 := &testRequestFactory{}
e2 := WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
RequestFactory: f2,
})
r2 := e2.Request("GET", "/")
r2.chain.assertOK(t)
assert.NotNil(t, f2.lastreq)
assert.True(t, f2.lastreq == r2.http)
f3 := &testRequestFactory{
fail: true,
}
e3 := WithConfig(Config{
BaseURL: "http://example.com",
Reporter: newMockReporter(t),
RequestFactory: f3,
})
r3 := e3.Request("GET", "/")
r3.chain.assertFailed(t)
assert.Nil(t, f3.lastreq)
}
func createBasicHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/foo", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"foo":123}`))
})
mux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
w.Write([]byte(`field1=` + r.FormValue("field1")))
w.Write([]byte(`&field2=` + r.PostFormValue("field2")))
})
mux.HandleFunc("/baz/qux", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`[true, false]`))
case "PUT":
decoder := json.NewDecoder(r.Body)
var m map[string]interface{}
if err := decoder.Decode(&m); err != nil {
w.WriteHeader(http.StatusBadRequest)
} else if m["test"] != "ok" {
w.WriteHeader(http.StatusBadRequest)
} else {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`ok`))
}
}
})
mux.HandleFunc("/wee", func(w http.ResponseWriter, r *http.Request) {
if u, p, ok := r.BasicAuth(); ok {
w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
w.Write([]byte(`username=` + u))
w.Write([]byte(`&password=` + p))
} else {
w.WriteHeader(http.StatusBadRequest)
}
})
return mux
}
func testBasicHandler(e *Expect) {
e.GET("/foo")
e.GET("/foo").Expect()
e.GET("/foo").Expect().Status(http.StatusOK)
e.GET("/foo").Expect().
Status(http.StatusOK).JSON().Object().ValueEqual("foo", 123)
e.PUT("/bar").WithQuery("field1", "hello").WithFormField("field2", "world").
Expect().
Status(http.StatusOK).
Form().ValueEqual("field1", "hello").ValueEqual("field2", "world")
e.GET("/{a}/{b}", "baz", "qux").
Expect().
Status(http.StatusOK).JSON().Array().Elements(true, false)
e.PUT("/{a}/{b}").
WithPath("a", "baz").
WithPath("b", "qux").
WithJSON(map[string]string{"test": "ok"}).
Expect().
Status(http.StatusOK).Body().Equal("ok")
auth := e.Builder(func(req *Request) {
req.WithBasicAuth("john", "secret")
})
auth.PUT("/wee").
Expect().
Status(http.StatusOK).
Form().ValueEqual("username", "john").ValueEqual("password", "secret")
}
func TestExpectBasicHandlerLiveDefault(t *testing.T) {
handler := createBasicHandler()
server := httptest.NewServer(handler)
defer server.Close()
testBasicHandler(New(t, server.URL))
}
func TestExpectBasicHandlerLiveConfig(t *testing.T) {
handler := createBasicHandler()
server := httptest.NewServer(handler)
defer server.Close()
testBasicHandler(WithConfig(Config{
BaseURL: server.URL,
Reporter: NewAssertReporter(t),
Printers: []Printer{
NewCurlPrinter(t),
NewDebugPrinter(t, true),
},
}))
}
func TestExpectBasicHandlerLiveTLS(t *testing.T) {
handler := createBasicHandler()
server := httptest.NewTLSServer(handler)
defer server.Close()
testBasicHandler(WithConfig(Config{
BaseURL: server.URL,
Reporter: NewAssertReporter(t),
Client: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
},
}))
}
func TestExpectBasicHandlerLiveLongRun(t *testing.T) {
if testing.Short() {
return
}
handler := createBasicHandler()
server := httptest.NewServer(handler)
defer server.Close()
e := New(t, server.URL)
for i := 0; i < 2; i++ {
testBasicHandler(e)
}
}
func TestExpectBasicHandlerBinderStandard(t *testing.T) {
handler := createBasicHandler()
testBasicHandler(WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
Client: &http.Client{
Transport: NewBinder(handler),
},
}))
}
func TestExpectBasicHandlerBinderFast(t *testing.T) {
handler := fasthttpadaptor.NewFastHTTPHandler(createBasicHandler())
testBasicHandler(WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
Client: &http.Client{
Transport: NewFastBinder(handler),
},
}))
}
func createRedirectHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/foo", func(w http.ResponseWriter, _ *http.Request) {
w.Write([]byte(`hello`))
})
mux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/foo", http.StatusFound)
})
return mux
}
func createRedirectFastHandler() fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
switch string(ctx.Path()) {
case "/foo":
ctx.SetBody([]byte(`hello`))
case "/bar":
ctx.Redirect("/foo", http.StatusFound)
}
}
}
func testRedirectHandler(e *Expect) {
e.POST("/bar").
Expect().
Status(http.StatusOK).Body().Equal(`hello`)
}
func TestExpectRedirectHandlerLive(t *testing.T) {
handler := createRedirectHandler()
server := httptest.NewServer(handler)
defer server.Close()
testRedirectHandler(New(t, server.URL))
}
func TestExpectRedirectHandlerBinderStandard(t *testing.T) {
handler := createRedirectHandler()
testRedirectHandler(WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
Client: &http.Client{
Transport: NewBinder(handler),
},
}))
}
func TestExpectRedirectHandlerBinderFast(t *testing.T) {
handler := createRedirectFastHandler()
testRedirectHandler(WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
Client: &http.Client{
Transport: NewFastBinder(handler),
},
}))
}
func createAutoTLSHandler(https string) http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/tls", func(w http.ResponseWriter, r *http.Request) {
if r.TLS == nil {
w.Write([]byte(`no`))
} else {
w.Write([]byte(`yes`))
}
})
mux.HandleFunc("/protected", func(w http.ResponseWriter, r *http.Request) {
if r.TLS == nil {
http.Redirect(w, r, https+r.RequestURI, http.StatusFound)
} else {
w.Write([]byte(`hello`))
}
})
return mux
}
func createAutoTLSFastHandler(https string) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
switch string(ctx.Path()) {
case "/tls":
if !ctx.IsTLS() {
ctx.SetBody([]byte(`no`))
} else {
ctx.SetBody([]byte(`yes`))
}
case "/protected":
if !ctx.IsTLS() {
ctx.Redirect(https+string(ctx.Request.RequestURI()), http.StatusFound)
} else {
ctx.SetBody([]byte(`hello`))
}
}
}
}
func testAutoTLSHandler(config Config) {
e := WithConfig(config)
tls := e.POST("/tls").
Expect().
Status(http.StatusOK).Body()
if strings.HasPrefix(config.BaseURL, "https://") {
tls.Equal(`yes`)
} else {
tls.Equal(`no`)
}
e.POST("/protected").
Expect().
Status(http.StatusOK).Body().Equal(`hello`)
}
func TestExpectAutoTLSHandlerLive(t *testing.T) {
httpsServ := httptest.NewTLSServer(createAutoTLSHandler(""))
defer httpsServ.Close()
httpServ := httptest.NewServer(createAutoTLSHandler(httpsServ.URL))
defer httpServ.Close()
assert.True(t, strings.HasPrefix(httpsServ.URL, "https://"))
assert.True(t, strings.HasPrefix(httpServ.URL, "http://"))
for _, url := range []string{httpsServ.URL, httpServ.URL} {
testAutoTLSHandler(Config{
BaseURL: url,
Reporter: NewRequireReporter(t),
Printers: []Printer{
NewDebugPrinter(t, true),
},
Client: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
},
})
}
}
func TestExpectAutoTLSHandlerBinderStandard(t *testing.T) {
handler := createAutoTLSHandler("https://example.com")
for _, url := range []string{"https://example.com", "http://example.com"} {
testAutoTLSHandler(Config{
BaseURL: url,
Reporter: NewRequireReporter(t),
Printers: []Printer{
NewDebugPrinter(t, true),
},
Client: &http.Client{
Transport: &Binder{
Handler: handler,
TLS: &tls.ConnectionState{},
},
},
})
}
}
func TestExpectAutoTLSHandlerBinderFast(t *testing.T) {
handler := createAutoTLSFastHandler("https://example.com")
for _, url := range []string{"https://example.com", "http://example.com"} {
testAutoTLSHandler(Config{
BaseURL: url,
Reporter: NewRequireReporter(t),
Printers: []Printer{
NewDebugPrinter(t, true),
},
Client: &http.Client{
Transport: &FastBinder{
Handler: handler,
TLS: &tls.ConnectionState{},
},
},
})
}
}
func createChunkedHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Proto != "HTTP/1.1" {
w.WriteHeader(http.StatusBadRequest)
} else if len(r.TransferEncoding) != 1 || r.TransferEncoding[0] != "chunked" {
w.WriteHeader(http.StatusBadRequest)
} else if r.PostFormValue("key") != "value" {
w.WriteHeader(http.StatusBadRequest)
} else {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`[1, `))
w.(http.Flusher).Flush()
w.Write([]byte(`2]`))
}
})
return mux
}
func createChunkedFastHandler(t *testing.T) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
headers := map[string][]string{}
ctx.Request.Header.VisitAll(func(k, v []byte) {
headers[string(k)] = append(headers[string(k)], string(v))
})
assert.Equal(t, []string{"chunked"}, headers["Transfer-Encoding"])
assert.Equal(t, "value", string(ctx.FormValue("key")))
assert.Equal(t, "key=value", string(ctx.Request.Body()))
ctx.Response.Header.Set("Content-Type", "application/json")
ctx.Response.SetBodyStreamWriter(func(w *bufio.Writer) {
w.WriteString(`[1, `)
w.Flush()
w.WriteString(`2]`)
})
}
}
func testChunkedHandler(e *Expect) {
e.PUT("/").
WithHeader("Content-Type", "application/x-www-form-urlencoded").
WithChunked(strings.NewReader("key=value")).
Expect().
Status(http.StatusOK).
ContentType("application/json").
TransferEncoding("chunked").
JSON().Array().Elements(1, 2)
}
func TestExpectChunkedHandlerLive(t *testing.T) {
handler := createChunkedHandler()
server := httptest.NewServer(handler)
defer server.Close()
testChunkedHandler(New(t, server.URL))
}
func TestExpectChunkedHandlerBinderStandard(t *testing.T) {
handler := createChunkedHandler()
testChunkedHandler(WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
Client: &http.Client{
Transport: NewBinder(handler),
},
}))
}
func TestExpectChunkedHandlerBinderFast(t *testing.T) {
handler := createChunkedFastHandler(t)
testChunkedHandler(WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
Client: &http.Client{
Transport: NewFastBinder(handler),
},
}))
}
func createCookieHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &http.Cookie{
Name: "myname",
Value: "myvalue",
Path: "/",
Expires: time.Date(3000, 0, 0, 0, 0, 0, 0, time.UTC),
})
w.WriteHeader(http.StatusNoContent)
})
mux.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("myname")
if err != nil {
w.WriteHeader(http.StatusBadRequest)
} else {
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(cookie.Value))
}
})
return mux
}
func testCookieHandler(e *Expect, enabled bool) {
r := e.PUT("/set").Expect().Status(http.StatusNoContent)
r.Cookies().ContainsOnly("myname")
c := r.Cookie("myname")
c.Value().Equal("myvalue")
c.Path().Equal("/")
c.Expires().Equal(time.Date(3000, 0, 0, 0, 0, 0, 0, time.UTC))
if enabled {
e.GET("/get").Expect().Status(http.StatusOK).Text().Equal("myvalue")
} else {
e.GET("/get").Expect().Status(http.StatusBadRequest)
}
}
func TestExpectCookieHandlerLiveDisabled(t *testing.T) {
handler := createCookieHandler()
server := httptest.NewServer(handler)
defer server.Close()
e := WithConfig(Config{
BaseURL: server.URL,
Reporter: NewAssertReporter(t),
Client: &http.Client{
Jar: nil,
},
})
testCookieHandler(e, false)
}
func TestExpecCookieHandlerLiveEnabled(t *testing.T) {
handler := createCookieHandler()
server := httptest.NewServer(handler)
defer server.Close()
e := WithConfig(Config{
BaseURL: server.URL,
Reporter: NewAssertReporter(t),
Client: &http.Client{
Jar: NewJar(),
},
})
testCookieHandler(e, true)
}
func TestExpectCookieHandlerBinderStandardDisabled(t *testing.T) {
handler := createCookieHandler()
e := WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
Client: &http.Client{
Transport: NewBinder(handler),
Jar: nil,
},
})
testCookieHandler(e, false)
}
func TestExpectCookieHandlerBinderStandardEnabled(t *testing.T) {
handler := createCookieHandler()
e := WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
Client: &http.Client{
Transport: NewBinder(handler),
Jar: NewJar(),
},
})
testCookieHandler(e, true)
}
func TestExpectCookieHandlerBinderFastDisabled(t *testing.T) {
handler := fasthttpadaptor.NewFastHTTPHandler(createCookieHandler())
e := WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
Client: &http.Client{
Transport: NewFastBinder(handler),
Jar: nil,
},
})
testCookieHandler(e, false)
}
func TestExpectCookieHandlerBinderFastEnabled(t *testing.T) {
handler := fasthttpadaptor.NewFastHTTPHandler(createCookieHandler())
e := WithConfig(Config{
BaseURL: "http://example.com",
Reporter: NewAssertReporter(t),
Client: &http.Client{
Transport: NewFastBinder(handler),
Jar: NewJar(),
},
})
testCookieHandler(e, true)
}
func TestExpectStaticFastBinder(t *testing.T) {
tempdir, err := ioutil.TempDir("", "httpexpect")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tempdir)
if err := ioutil.WriteFile(
path.Join(tempdir, "hello"), []byte("hello, world!"), 0666); err != nil {
t.Fatal(err)
}
fs := &fasthttp.FS{
Root: tempdir,
}
handler := fs.NewRequestHandler()
e := WithConfig(Config{
Client: &http.Client{
Transport: NewFastBinder(handler),
Jar: NewJar(),
},
Reporter: NewAssertReporter(t),
Printers: []Printer{
NewDebugPrinter(t, true),
},
})
e.GET("/hello").
Expect().
Status(http.StatusOK).
Text().Equal("hello, world!")
}