f98b339b77
* support bind http header param #1956 update #1956 ``` package main import ( "fmt" "github.com/gin-gonic/gin" ) type testHeader struct { Rate int `header:"Rate"` Domain string `header:"Domain"` } func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { h := testHeader{} if err := c.ShouldBindHeader(&h); err != nil { c.JSON(200, err) } fmt.Printf("%#v\n", h) c.JSON(200, gin.H{"Rate": h.Rate, "Domain": h.Domain}) }) r.Run() // client // curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/ // output // {"Domain":"music","Rate":300} } ``` * add unit test * Modify the code to get the http header When the http header is obtained in the standard library, the key value will be modified by the CanonicalMIMEHeaderKey function, and finally the value of the http header will be obtained from the map. As follows. ```go func (h MIMEHeader) Get(key string) string { // ... v := h[CanonicalMIMEHeaderKey(key)] // ... } ``` This pr also follows this modification * Thanks to vkd for suggestions, modifying code * Increase test coverage env GOPATH=`pwd` go test github.com/gin-gonic/gin/binding -coverprofile=cover.prof ok github.com/gin-gonic/gin/binding 0.015s coverage: 100.0% of statements * Rollback check code * add use case to README.md
1891 lines
57 KiB
Go
1891 lines
57 KiB
Go
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
// Use of this source code is governed by a MIT style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gin
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"html/template"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gin-contrib/sse"
|
|
"github.com/gin-gonic/gin/binding"
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
testdata "github.com/gin-gonic/gin/testdata/protoexample"
|
|
)
|
|
|
|
var _ context.Context = &Context{}
|
|
|
|
// Unit tests TODO
|
|
// func (c *Context) File(filepath string) {
|
|
// func (c *Context) Negotiate(code int, config Negotiate) {
|
|
// BAD case: func (c *Context) Render(code int, render render.Render, obj ...interface{}) {
|
|
// test that information is not leaked when reusing Contexts (using the Pool)
|
|
|
|
func createMultipartRequest() *http.Request {
|
|
boundary := "--testboundary"
|
|
body := new(bytes.Buffer)
|
|
mw := multipart.NewWriter(body)
|
|
defer mw.Close()
|
|
|
|
must(mw.SetBoundary(boundary))
|
|
must(mw.WriteField("foo", "bar"))
|
|
must(mw.WriteField("bar", "10"))
|
|
must(mw.WriteField("bar", "foo2"))
|
|
must(mw.WriteField("array", "first"))
|
|
must(mw.WriteField("array", "second"))
|
|
must(mw.WriteField("id", ""))
|
|
must(mw.WriteField("time_local", "31/12/2016 14:55"))
|
|
must(mw.WriteField("time_utc", "31/12/2016 14:55"))
|
|
must(mw.WriteField("time_location", "31/12/2016 14:55"))
|
|
must(mw.WriteField("names[a]", "thinkerou"))
|
|
must(mw.WriteField("names[b]", "tianou"))
|
|
req, err := http.NewRequest("POST", "/", body)
|
|
must(err)
|
|
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
|
|
return req
|
|
}
|
|
|
|
func must(err error) {
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
}
|
|
|
|
func TestContextFormFile(t *testing.T) {
|
|
buf := new(bytes.Buffer)
|
|
mw := multipart.NewWriter(buf)
|
|
w, err := mw.CreateFormFile("file", "test")
|
|
if assert.NoError(t, err) {
|
|
_, err = w.Write([]byte("test"))
|
|
assert.NoError(t, err)
|
|
}
|
|
mw.Close()
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", buf)
|
|
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
|
|
f, err := c.FormFile("file")
|
|
if assert.NoError(t, err) {
|
|
assert.Equal(t, "test", f.Filename)
|
|
}
|
|
|
|
assert.NoError(t, c.SaveUploadedFile(f, "test"))
|
|
}
|
|
|
|
func TestContextFormFileFailed(t *testing.T) {
|
|
buf := new(bytes.Buffer)
|
|
mw := multipart.NewWriter(buf)
|
|
mw.Close()
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
|
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
|
|
c.engine.MaxMultipartMemory = 8 << 20
|
|
f, err := c.FormFile("file")
|
|
assert.Error(t, err)
|
|
assert.Nil(t, f)
|
|
}
|
|
|
|
func TestContextMultipartForm(t *testing.T) {
|
|
buf := new(bytes.Buffer)
|
|
mw := multipart.NewWriter(buf)
|
|
assert.NoError(t, mw.WriteField("foo", "bar"))
|
|
w, err := mw.CreateFormFile("file", "test")
|
|
if assert.NoError(t, err) {
|
|
_, err = w.Write([]byte("test"))
|
|
assert.NoError(t, err)
|
|
}
|
|
mw.Close()
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", buf)
|
|
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
|
|
f, err := c.MultipartForm()
|
|
if assert.NoError(t, err) {
|
|
assert.NotNil(t, f)
|
|
}
|
|
|
|
assert.NoError(t, c.SaveUploadedFile(f.File["file"][0], "test"))
|
|
}
|
|
|
|
func TestSaveUploadedOpenFailed(t *testing.T) {
|
|
buf := new(bytes.Buffer)
|
|
mw := multipart.NewWriter(buf)
|
|
mw.Close()
|
|
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", buf)
|
|
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
|
|
|
|
f := &multipart.FileHeader{
|
|
Filename: "file",
|
|
}
|
|
assert.Error(t, c.SaveUploadedFile(f, "test"))
|
|
}
|
|
|
|
func TestSaveUploadedCreateFailed(t *testing.T) {
|
|
buf := new(bytes.Buffer)
|
|
mw := multipart.NewWriter(buf)
|
|
w, err := mw.CreateFormFile("file", "test")
|
|
if assert.NoError(t, err) {
|
|
_, err = w.Write([]byte("test"))
|
|
assert.NoError(t, err)
|
|
}
|
|
mw.Close()
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", buf)
|
|
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
|
|
f, err := c.FormFile("file")
|
|
if assert.NoError(t, err) {
|
|
assert.Equal(t, "test", f.Filename)
|
|
}
|
|
|
|
assert.Error(t, c.SaveUploadedFile(f, "/"))
|
|
}
|
|
|
|
func TestContextReset(t *testing.T) {
|
|
router := New()
|
|
c := router.allocateContext()
|
|
assert.Equal(t, c.engine, router)
|
|
|
|
c.index = 2
|
|
c.Writer = &responseWriter{ResponseWriter: httptest.NewRecorder()}
|
|
c.Params = Params{Param{}}
|
|
c.Error(errors.New("test")) // nolint: errcheck
|
|
c.Set("foo", "bar")
|
|
c.reset()
|
|
|
|
assert.False(t, c.IsAborted())
|
|
assert.Nil(t, c.Keys)
|
|
assert.Nil(t, c.Accepted)
|
|
assert.Len(t, c.Errors, 0)
|
|
assert.Empty(t, c.Errors.Errors())
|
|
assert.Empty(t, c.Errors.ByType(ErrorTypeAny))
|
|
assert.Len(t, c.Params, 0)
|
|
assert.EqualValues(t, c.index, -1)
|
|
assert.Equal(t, c.Writer.(*responseWriter), &c.writermem)
|
|
}
|
|
|
|
func TestContextHandlers(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
assert.Nil(t, c.handlers)
|
|
assert.Nil(t, c.handlers.Last())
|
|
|
|
c.handlers = HandlersChain{}
|
|
assert.NotNil(t, c.handlers)
|
|
assert.Nil(t, c.handlers.Last())
|
|
|
|
f := func(c *Context) {}
|
|
g := func(c *Context) {}
|
|
|
|
c.handlers = HandlersChain{f}
|
|
compareFunc(t, f, c.handlers.Last())
|
|
|
|
c.handlers = HandlersChain{f, g}
|
|
compareFunc(t, g, c.handlers.Last())
|
|
}
|
|
|
|
// TestContextSetGet tests that a parameter is set correctly on the
|
|
// current context and can be retrieved using Get.
|
|
func TestContextSetGet(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Set("foo", "bar")
|
|
|
|
value, err := c.Get("foo")
|
|
assert.Equal(t, "bar", value)
|
|
assert.True(t, err)
|
|
|
|
value, err = c.Get("foo2")
|
|
assert.Nil(t, value)
|
|
assert.False(t, err)
|
|
|
|
assert.Equal(t, "bar", c.MustGet("foo"))
|
|
assert.Panics(t, func() { c.MustGet("no_exist") })
|
|
}
|
|
|
|
func TestContextSetGetValues(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Set("string", "this is a string")
|
|
c.Set("int32", int32(-42))
|
|
c.Set("int64", int64(42424242424242))
|
|
c.Set("uint64", uint64(42))
|
|
c.Set("float32", float32(4.2))
|
|
c.Set("float64", 4.2)
|
|
var a interface{} = 1
|
|
c.Set("intInterface", a)
|
|
|
|
assert.Exactly(t, c.MustGet("string").(string), "this is a string")
|
|
assert.Exactly(t, c.MustGet("int32").(int32), int32(-42))
|
|
assert.Exactly(t, c.MustGet("int64").(int64), int64(42424242424242))
|
|
assert.Exactly(t, c.MustGet("uint64").(uint64), uint64(42))
|
|
assert.Exactly(t, c.MustGet("float32").(float32), float32(4.2))
|
|
assert.Exactly(t, c.MustGet("float64").(float64), 4.2)
|
|
assert.Exactly(t, c.MustGet("intInterface").(int), 1)
|
|
|
|
}
|
|
|
|
func TestContextGetString(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Set("string", "this is a string")
|
|
assert.Equal(t, "this is a string", c.GetString("string"))
|
|
}
|
|
|
|
func TestContextSetGetBool(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Set("bool", true)
|
|
assert.True(t, c.GetBool("bool"))
|
|
}
|
|
|
|
func TestContextGetInt(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Set("int", 1)
|
|
assert.Equal(t, 1, c.GetInt("int"))
|
|
}
|
|
|
|
func TestContextGetInt64(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Set("int64", int64(42424242424242))
|
|
assert.Equal(t, int64(42424242424242), c.GetInt64("int64"))
|
|
}
|
|
|
|
func TestContextGetFloat64(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Set("float64", 4.2)
|
|
assert.Equal(t, 4.2, c.GetFloat64("float64"))
|
|
}
|
|
|
|
func TestContextGetTime(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
t1, _ := time.Parse("1/2/2006 15:04:05", "01/01/2017 12:00:00")
|
|
c.Set("time", t1)
|
|
assert.Equal(t, t1, c.GetTime("time"))
|
|
}
|
|
|
|
func TestContextGetDuration(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Set("duration", time.Second)
|
|
assert.Equal(t, time.Second, c.GetDuration("duration"))
|
|
}
|
|
|
|
func TestContextGetStringSlice(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Set("slice", []string{"foo"})
|
|
assert.Equal(t, []string{"foo"}, c.GetStringSlice("slice"))
|
|
}
|
|
|
|
func TestContextGetStringMap(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
var m = make(map[string]interface{})
|
|
m["foo"] = 1
|
|
c.Set("map", m)
|
|
|
|
assert.Equal(t, m, c.GetStringMap("map"))
|
|
assert.Equal(t, 1, c.GetStringMap("map")["foo"])
|
|
}
|
|
|
|
func TestContextGetStringMapString(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
var m = make(map[string]string)
|
|
m["foo"] = "bar"
|
|
c.Set("map", m)
|
|
|
|
assert.Equal(t, m, c.GetStringMapString("map"))
|
|
assert.Equal(t, "bar", c.GetStringMapString("map")["foo"])
|
|
}
|
|
|
|
func TestContextGetStringMapStringSlice(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
var m = make(map[string][]string)
|
|
m["foo"] = []string{"foo"}
|
|
c.Set("map", m)
|
|
|
|
assert.Equal(t, m, c.GetStringMapStringSlice("map"))
|
|
assert.Equal(t, []string{"foo"}, c.GetStringMapStringSlice("map")["foo"])
|
|
}
|
|
|
|
func TestContextCopy(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.index = 2
|
|
c.Request, _ = http.NewRequest("POST", "/hola", nil)
|
|
c.handlers = HandlersChain{func(c *Context) {}}
|
|
c.Params = Params{Param{Key: "foo", Value: "bar"}}
|
|
c.Set("foo", "bar")
|
|
|
|
cp := c.Copy()
|
|
assert.Nil(t, cp.handlers)
|
|
assert.Nil(t, cp.writermem.ResponseWriter)
|
|
assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter))
|
|
assert.Equal(t, cp.Request, c.Request)
|
|
assert.Equal(t, cp.index, abortIndex)
|
|
assert.Equal(t, cp.Keys, c.Keys)
|
|
assert.Equal(t, cp.engine, c.engine)
|
|
assert.Equal(t, cp.Params, c.Params)
|
|
cp.Set("foo", "notBar")
|
|
assert.False(t, cp.Keys["foo"] == c.Keys["foo"])
|
|
}
|
|
|
|
func TestContextHandlerName(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.handlers = HandlersChain{func(c *Context) {}, handlerNameTest}
|
|
|
|
assert.Regexp(t, "^(.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest$", c.HandlerName())
|
|
}
|
|
|
|
func TestContextHandlerNames(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.handlers = HandlersChain{func(c *Context) {}, handlerNameTest, func(c *Context) {}, handlerNameTest2}
|
|
|
|
names := c.HandlerNames()
|
|
|
|
assert.True(t, len(names) == 4)
|
|
for _, name := range names {
|
|
assert.Regexp(t, `^(.*/vendor/)?(github\.com/gin-gonic/gin\.){1}(TestContextHandlerNames\.func.*){0,1}(handlerNameTest.*){0,1}`, name)
|
|
}
|
|
}
|
|
|
|
func handlerNameTest(c *Context) {
|
|
|
|
}
|
|
|
|
func handlerNameTest2(c *Context) {
|
|
|
|
}
|
|
|
|
var handlerTest HandlerFunc = func(c *Context) {
|
|
|
|
}
|
|
|
|
func TestContextHandler(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.handlers = HandlersChain{func(c *Context) {}, handlerTest}
|
|
|
|
assert.Equal(t, reflect.ValueOf(handlerTest).Pointer(), reflect.ValueOf(c.Handler()).Pointer())
|
|
}
|
|
|
|
func TestContextQuery(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10&id=", nil)
|
|
|
|
value, ok := c.GetQuery("foo")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "bar", value)
|
|
assert.Equal(t, "bar", c.DefaultQuery("foo", "none"))
|
|
assert.Equal(t, "bar", c.Query("foo"))
|
|
|
|
value, ok = c.GetQuery("page")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "10", value)
|
|
assert.Equal(t, "10", c.DefaultQuery("page", "0"))
|
|
assert.Equal(t, "10", c.Query("page"))
|
|
|
|
value, ok = c.GetQuery("id")
|
|
assert.True(t, ok)
|
|
assert.Empty(t, value)
|
|
assert.Empty(t, c.DefaultQuery("id", "nada"))
|
|
assert.Empty(t, c.Query("id"))
|
|
|
|
value, ok = c.GetQuery("NoKey")
|
|
assert.False(t, ok)
|
|
assert.Empty(t, value)
|
|
assert.Equal(t, "nada", c.DefaultQuery("NoKey", "nada"))
|
|
assert.Empty(t, c.Query("NoKey"))
|
|
|
|
// postform should not mess
|
|
value, ok = c.GetPostForm("page")
|
|
assert.False(t, ok)
|
|
assert.Empty(t, value)
|
|
assert.Empty(t, c.PostForm("foo"))
|
|
}
|
|
|
|
func TestContextQueryAndPostForm(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
body := bytes.NewBufferString("foo=bar&page=11&both=&foo=second")
|
|
c.Request, _ = http.NewRequest("POST",
|
|
"/?both=GET&id=main&id=omit&array[]=first&array[]=second&ids[a]=hi&ids[b]=3.14", body)
|
|
c.Request.Header.Add("Content-Type", MIMEPOSTForm)
|
|
|
|
assert.Equal(t, "bar", c.DefaultPostForm("foo", "none"))
|
|
assert.Equal(t, "bar", c.PostForm("foo"))
|
|
assert.Empty(t, c.Query("foo"))
|
|
|
|
value, ok := c.GetPostForm("page")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "11", value)
|
|
assert.Equal(t, "11", c.DefaultPostForm("page", "0"))
|
|
assert.Equal(t, "11", c.PostForm("page"))
|
|
assert.Empty(t, c.Query("page"))
|
|
|
|
value, ok = c.GetPostForm("both")
|
|
assert.True(t, ok)
|
|
assert.Empty(t, value)
|
|
assert.Empty(t, c.PostForm("both"))
|
|
assert.Empty(t, c.DefaultPostForm("both", "nothing"))
|
|
assert.Equal(t, "GET", c.Query("both"), "GET")
|
|
|
|
value, ok = c.GetQuery("id")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "main", value)
|
|
assert.Equal(t, "000", c.DefaultPostForm("id", "000"))
|
|
assert.Equal(t, "main", c.Query("id"))
|
|
assert.Empty(t, c.PostForm("id"))
|
|
|
|
value, ok = c.GetQuery("NoKey")
|
|
assert.False(t, ok)
|
|
assert.Empty(t, value)
|
|
value, ok = c.GetPostForm("NoKey")
|
|
assert.False(t, ok)
|
|
assert.Empty(t, value)
|
|
assert.Equal(t, "nada", c.DefaultPostForm("NoKey", "nada"))
|
|
assert.Equal(t, "nothing", c.DefaultQuery("NoKey", "nothing"))
|
|
assert.Empty(t, c.PostForm("NoKey"))
|
|
assert.Empty(t, c.Query("NoKey"))
|
|
|
|
var obj struct {
|
|
Foo string `form:"foo"`
|
|
ID string `form:"id"`
|
|
Page int `form:"page"`
|
|
Both string `form:"both"`
|
|
Array []string `form:"array[]"`
|
|
}
|
|
assert.NoError(t, c.Bind(&obj))
|
|
assert.Equal(t, "bar", obj.Foo, "bar")
|
|
assert.Equal(t, "main", obj.ID, "main")
|
|
assert.Equal(t, 11, obj.Page, 11)
|
|
assert.Empty(t, obj.Both)
|
|
assert.Equal(t, []string{"first", "second"}, obj.Array)
|
|
|
|
values, ok := c.GetQueryArray("array[]")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "first", values[0])
|
|
assert.Equal(t, "second", values[1])
|
|
|
|
values = c.QueryArray("array[]")
|
|
assert.Equal(t, "first", values[0])
|
|
assert.Equal(t, "second", values[1])
|
|
|
|
values = c.QueryArray("nokey")
|
|
assert.Equal(t, 0, len(values))
|
|
|
|
values = c.QueryArray("both")
|
|
assert.Equal(t, 1, len(values))
|
|
assert.Equal(t, "GET", values[0])
|
|
|
|
dicts, ok := c.GetQueryMap("ids")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "hi", dicts["a"])
|
|
assert.Equal(t, "3.14", dicts["b"])
|
|
|
|
dicts, ok = c.GetQueryMap("nokey")
|
|
assert.False(t, ok)
|
|
assert.Equal(t, 0, len(dicts))
|
|
|
|
dicts, ok = c.GetQueryMap("both")
|
|
assert.False(t, ok)
|
|
assert.Equal(t, 0, len(dicts))
|
|
|
|
dicts, ok = c.GetQueryMap("array")
|
|
assert.False(t, ok)
|
|
assert.Equal(t, 0, len(dicts))
|
|
|
|
dicts = c.QueryMap("ids")
|
|
assert.Equal(t, "hi", dicts["a"])
|
|
assert.Equal(t, "3.14", dicts["b"])
|
|
|
|
dicts = c.QueryMap("nokey")
|
|
assert.Equal(t, 0, len(dicts))
|
|
}
|
|
|
|
func TestContextPostFormMultipart(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request = createMultipartRequest()
|
|
|
|
var obj struct {
|
|
Foo string `form:"foo"`
|
|
Bar string `form:"bar"`
|
|
BarAsInt int `form:"bar"`
|
|
Array []string `form:"array"`
|
|
ID string `form:"id"`
|
|
TimeLocal time.Time `form:"time_local" time_format:"02/01/2006 15:04"`
|
|
TimeUTC time.Time `form:"time_utc" time_format:"02/01/2006 15:04" time_utc:"1"`
|
|
TimeLocation time.Time `form:"time_location" time_format:"02/01/2006 15:04" time_location:"Asia/Tokyo"`
|
|
BlankTime time.Time `form:"blank_time" time_format:"02/01/2006 15:04"`
|
|
}
|
|
assert.NoError(t, c.Bind(&obj))
|
|
assert.Equal(t, "bar", obj.Foo)
|
|
assert.Equal(t, "10", obj.Bar)
|
|
assert.Equal(t, 10, obj.BarAsInt)
|
|
assert.Equal(t, []string{"first", "second"}, obj.Array)
|
|
assert.Empty(t, obj.ID)
|
|
assert.Equal(t, "31/12/2016 14:55", obj.TimeLocal.Format("02/01/2006 15:04"))
|
|
assert.Equal(t, time.Local, obj.TimeLocal.Location())
|
|
assert.Equal(t, "31/12/2016 14:55", obj.TimeUTC.Format("02/01/2006 15:04"))
|
|
assert.Equal(t, time.UTC, obj.TimeUTC.Location())
|
|
loc, _ := time.LoadLocation("Asia/Tokyo")
|
|
assert.Equal(t, "31/12/2016 14:55", obj.TimeLocation.Format("02/01/2006 15:04"))
|
|
assert.Equal(t, loc, obj.TimeLocation.Location())
|
|
assert.True(t, obj.BlankTime.IsZero())
|
|
|
|
value, ok := c.GetQuery("foo")
|
|
assert.False(t, ok)
|
|
assert.Empty(t, value)
|
|
assert.Empty(t, c.Query("bar"))
|
|
assert.Equal(t, "nothing", c.DefaultQuery("id", "nothing"))
|
|
|
|
value, ok = c.GetPostForm("foo")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "bar", value)
|
|
assert.Equal(t, "bar", c.PostForm("foo"))
|
|
|
|
value, ok = c.GetPostForm("array")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "first", value)
|
|
assert.Equal(t, "first", c.PostForm("array"))
|
|
|
|
assert.Equal(t, "10", c.DefaultPostForm("bar", "nothing"))
|
|
|
|
value, ok = c.GetPostForm("id")
|
|
assert.True(t, ok)
|
|
assert.Empty(t, value)
|
|
assert.Empty(t, c.PostForm("id"))
|
|
assert.Empty(t, c.DefaultPostForm("id", "nothing"))
|
|
|
|
value, ok = c.GetPostForm("nokey")
|
|
assert.False(t, ok)
|
|
assert.Empty(t, value)
|
|
assert.Equal(t, "nothing", c.DefaultPostForm("nokey", "nothing"))
|
|
|
|
values, ok := c.GetPostFormArray("array")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "first", values[0])
|
|
assert.Equal(t, "second", values[1])
|
|
|
|
values = c.PostFormArray("array")
|
|
assert.Equal(t, "first", values[0])
|
|
assert.Equal(t, "second", values[1])
|
|
|
|
values = c.PostFormArray("nokey")
|
|
assert.Equal(t, 0, len(values))
|
|
|
|
values = c.PostFormArray("foo")
|
|
assert.Equal(t, 1, len(values))
|
|
assert.Equal(t, "bar", values[0])
|
|
|
|
dicts, ok := c.GetPostFormMap("names")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "thinkerou", dicts["a"])
|
|
assert.Equal(t, "tianou", dicts["b"])
|
|
|
|
dicts, ok = c.GetPostFormMap("nokey")
|
|
assert.False(t, ok)
|
|
assert.Equal(t, 0, len(dicts))
|
|
|
|
dicts = c.PostFormMap("names")
|
|
assert.Equal(t, "thinkerou", dicts["a"])
|
|
assert.Equal(t, "tianou", dicts["b"])
|
|
|
|
dicts = c.PostFormMap("nokey")
|
|
assert.Equal(t, 0, len(dicts))
|
|
}
|
|
|
|
func TestContextSetCookie(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.SetCookie("user", "gin", 1, "/", "localhost", true, true)
|
|
assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure", c.Writer.Header().Get("Set-Cookie"))
|
|
}
|
|
|
|
func TestContextSetCookiePathEmpty(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.SetCookie("user", "gin", 1, "", "localhost", true, true)
|
|
assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure", c.Writer.Header().Get("Set-Cookie"))
|
|
}
|
|
|
|
func TestContextGetCookie(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("GET", "/get", nil)
|
|
c.Request.Header.Set("Cookie", "user=gin")
|
|
cookie, _ := c.Cookie("user")
|
|
assert.Equal(t, "gin", cookie)
|
|
|
|
_, err := c.Cookie("nokey")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestContextBodyAllowedForStatus(t *testing.T) {
|
|
assert.False(t, false, bodyAllowedForStatus(http.StatusProcessing))
|
|
assert.False(t, false, bodyAllowedForStatus(http.StatusNoContent))
|
|
assert.False(t, false, bodyAllowedForStatus(http.StatusNotModified))
|
|
assert.True(t, true, bodyAllowedForStatus(http.StatusInternalServerError))
|
|
}
|
|
|
|
type TestPanicRender struct {
|
|
}
|
|
|
|
func (*TestPanicRender) Render(http.ResponseWriter) error {
|
|
return errors.New("TestPanicRender")
|
|
}
|
|
|
|
func (*TestPanicRender) WriteContentType(http.ResponseWriter) {}
|
|
|
|
func TestContextRenderPanicIfErr(t *testing.T) {
|
|
defer func() {
|
|
r := recover()
|
|
assert.Equal(t, "TestPanicRender", fmt.Sprint(r))
|
|
}()
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Render(http.StatusOK, &TestPanicRender{})
|
|
|
|
assert.Fail(t, "Panic not detected")
|
|
}
|
|
|
|
// Tests that the response is serialized as JSON
|
|
// and Content-Type is set to application/json
|
|
// and special HTML characters are escaped
|
|
func TestContextRenderJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.JSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}\n", w.Body.String())
|
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that the response is serialized as JSONP
|
|
// and Content-Type is set to application/javascript
|
|
func TestContextRenderJSONP(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
c.Request, _ = http.NewRequest("GET", "http://example.com/?callback=x", nil)
|
|
|
|
c.JSONP(http.StatusCreated, H{"foo": "bar"})
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "x({\"foo\":\"bar\"})", w.Body.String())
|
|
assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that the response is serialized as JSONP
|
|
// and Content-Type is set to application/json
|
|
func TestContextRenderJSONPWithoutCallback(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
c.Request, _ = http.NewRequest("GET", "http://example.com", nil)
|
|
|
|
c.JSONP(http.StatusCreated, H{"foo": "bar"})
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "{\"foo\":\"bar\"}\n", w.Body.String())
|
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that no JSON is rendered if code is 204
|
|
func TestContextRenderNoContentJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.JSON(http.StatusNoContent, H{"foo": "bar"})
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
|
assert.Empty(t, w.Body.String())
|
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that the response is serialized as JSON
|
|
// we change the content-type before
|
|
func TestContextRenderAPIJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Header("Content-Type", "application/vnd.api+json")
|
|
c.JSON(http.StatusCreated, H{"foo": "bar"})
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "{\"foo\":\"bar\"}\n", w.Body.String())
|
|
assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that no Custom JSON is rendered if code is 204
|
|
func TestContextRenderNoContentAPIJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Header("Content-Type", "application/vnd.api+json")
|
|
c.JSON(http.StatusNoContent, H{"foo": "bar"})
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
|
assert.Empty(t, w.Body.String())
|
|
assert.Equal(t, w.Header().Get("Content-Type"), "application/vnd.api+json")
|
|
}
|
|
|
|
// Tests that the response is serialized as JSON
|
|
// and Content-Type is set to application/json
|
|
func TestContextRenderIndentedJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.IndentedJSON(http.StatusCreated, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\",\n \"nested\": {\n \"foo\": \"bar\"\n }\n}", w.Body.String())
|
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that no Custom JSON is rendered if code is 204
|
|
func TestContextRenderNoContentIndentedJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.IndentedJSON(http.StatusNoContent, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
|
assert.Empty(t, w.Body.String())
|
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that the response is serialized as Secure JSON
|
|
// and Content-Type is set to application/json
|
|
func TestContextRenderSecureJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, router := CreateTestContext(w)
|
|
|
|
router.SecureJsonPrefix("&&&START&&&")
|
|
c.SecureJSON(http.StatusCreated, []string{"foo", "bar"})
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "&&&START&&&[\"foo\",\"bar\"]", w.Body.String())
|
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that no Custom JSON is rendered if code is 204
|
|
func TestContextRenderNoContentSecureJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.SecureJSON(http.StatusNoContent, []string{"foo", "bar"})
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
|
assert.Empty(t, w.Body.String())
|
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
func TestContextRenderNoContentAsciiJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.AsciiJSON(http.StatusNoContent, []string{"lang", "Go语言"})
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
|
assert.Empty(t, w.Body.String())
|
|
assert.Equal(t, "application/json", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that the response is serialized as JSON
|
|
// and Content-Type is set to application/json
|
|
// and special HTML characters are preserved
|
|
func TestContextRenderPureJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
|
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that the response executes the templates
|
|
// and responds with Content-Type set to text/html
|
|
func TestContextRenderHTML(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, router := CreateTestContext(w)
|
|
|
|
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
|
router.SetHTMLTemplate(templ)
|
|
|
|
c.HTML(http.StatusCreated, "t", H{"name": "alexandernyquist"})
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "Hello alexandernyquist", w.Body.String())
|
|
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
func TestContextRenderHTML2(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, router := CreateTestContext(w)
|
|
|
|
// print debug warning log when Engine.trees > 0
|
|
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
|
|
assert.Len(t, router.trees, 1)
|
|
|
|
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
|
re := captureOutput(t, func() {
|
|
SetMode(DebugMode)
|
|
router.SetHTMLTemplate(templ)
|
|
SetMode(TestMode)
|
|
})
|
|
|
|
assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", re)
|
|
|
|
c.HTML(http.StatusCreated, "t", H{"name": "alexandernyquist"})
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "Hello alexandernyquist", w.Body.String())
|
|
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that no HTML is rendered if code is 204
|
|
func TestContextRenderNoContentHTML(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, router := CreateTestContext(w)
|
|
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
|
router.SetHTMLTemplate(templ)
|
|
|
|
c.HTML(http.StatusNoContent, "t", H{"name": "alexandernyquist"})
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
|
assert.Empty(t, w.Body.String())
|
|
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// TestContextXML tests that the response is serialized as XML
|
|
// and Content-Type is set to application/xml
|
|
func TestContextRenderXML(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.XML(http.StatusCreated, H{"foo": "bar"})
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
|
|
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that no XML is rendered if code is 204
|
|
func TestContextRenderNoContentXML(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.XML(http.StatusNoContent, H{"foo": "bar"})
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
|
assert.Empty(t, w.Body.String())
|
|
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// TestContextString tests that the response is returned
|
|
// with Content-Type set to text/plain
|
|
func TestContextRenderString(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.String(http.StatusCreated, "test %s %d", "string", 2)
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "test string 2", w.Body.String())
|
|
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that no String is rendered if code is 204
|
|
func TestContextRenderNoContentString(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.String(http.StatusNoContent, "test %s %d", "string", 2)
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
|
assert.Empty(t, w.Body.String())
|
|
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// TestContextString tests that the response is returned
|
|
// with Content-Type set to text/html
|
|
func TestContextRenderHTMLString(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Header("Content-Type", "text/html; charset=utf-8")
|
|
c.String(http.StatusCreated, "<html>%s %d</html>", "string", 3)
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "<html>string 3</html>", w.Body.String())
|
|
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that no HTML String is rendered if code is 204
|
|
func TestContextRenderNoContentHTMLString(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Header("Content-Type", "text/html; charset=utf-8")
|
|
c.String(http.StatusNoContent, "<html>%s %d</html>", "string", 3)
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
|
assert.Empty(t, w.Body.String())
|
|
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// TestContextData tests that the response can be written from `bytesting`
|
|
// with specified MIME type
|
|
func TestContextRenderData(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Data(http.StatusCreated, "text/csv", []byte(`foo,bar`))
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "foo,bar", w.Body.String())
|
|
assert.Equal(t, "text/csv", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// Tests that no Custom Data is rendered if code is 204
|
|
func TestContextRenderNoContentData(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Data(http.StatusNoContent, "text/csv", []byte(`foo,bar`))
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
|
assert.Empty(t, w.Body.String())
|
|
assert.Equal(t, "text/csv", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
func TestContextRenderSSE(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.SSEvent("float", 1.5)
|
|
c.Render(-1, sse.Event{
|
|
Id: "123",
|
|
Data: "text",
|
|
})
|
|
c.SSEvent("chat", H{
|
|
"foo": "bar",
|
|
"bar": "foo",
|
|
})
|
|
|
|
assert.Equal(t, strings.Replace(w.Body.String(), " ", "", -1), strings.Replace("event:float\ndata:1.5\n\nid:123\ndata:text\n\nevent:chat\ndata:{\"bar\":\"foo\",\"foo\":\"bar\"}\n\n", " ", "", -1))
|
|
}
|
|
|
|
func TestContextRenderFile(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
|
c.File("./gin.go")
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "func New() *Engine {")
|
|
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
func TestContextRenderAttachment(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
newFilename := "new_filename.go"
|
|
|
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
|
c.FileAttachment("./gin.go", newFilename)
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
assert.Contains(t, w.Body.String(), "func New() *Engine {")
|
|
assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.HeaderMap.Get("Content-Disposition"))
|
|
}
|
|
|
|
// TestContextRenderYAML tests that the response is serialized as YAML
|
|
// and Content-Type is set to application/x-yaml
|
|
func TestContextRenderYAML(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.YAML(http.StatusCreated, H{"foo": "bar"})
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "foo: bar\n", w.Body.String())
|
|
assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
// TestContextRenderProtoBuf tests that the response is serialized as ProtoBuf
|
|
// and Content-Type is set to application/x-protobuf
|
|
// and we just use the example protobuf to check if the response is correct
|
|
func TestContextRenderProtoBuf(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
reps := []int64{int64(1), int64(2)}
|
|
label := "test"
|
|
data := &testdata.Test{
|
|
Label: &label,
|
|
Reps: reps,
|
|
}
|
|
|
|
c.ProtoBuf(http.StatusCreated, data)
|
|
|
|
protoData, err := proto.Marshal(data)
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, string(protoData), w.Body.String())
|
|
assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
func TestContextHeaders(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Header("Content-Type", "text/plain")
|
|
c.Header("X-Custom", "value")
|
|
|
|
assert.Equal(t, "text/plain", c.Writer.Header().Get("Content-Type"))
|
|
assert.Equal(t, "value", c.Writer.Header().Get("X-Custom"))
|
|
|
|
c.Header("Content-Type", "text/html")
|
|
c.Header("X-Custom", "")
|
|
|
|
assert.Equal(t, "text/html", c.Writer.Header().Get("Content-Type"))
|
|
_, exist := c.Writer.Header()["X-Custom"]
|
|
assert.False(t, exist)
|
|
}
|
|
|
|
// TODO
|
|
func TestContextRenderRedirectWithRelativePath(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
|
|
assert.Panics(t, func() { c.Redirect(299, "/new_path") })
|
|
assert.Panics(t, func() { c.Redirect(309, "/new_path") })
|
|
|
|
c.Redirect(http.StatusMovedPermanently, "/path")
|
|
c.Writer.WriteHeaderNow()
|
|
assert.Equal(t, http.StatusMovedPermanently, w.Code)
|
|
assert.Equal(t, "/path", w.Header().Get("Location"))
|
|
}
|
|
|
|
func TestContextRenderRedirectWithAbsolutePath(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
|
|
c.Redirect(http.StatusFound, "http://google.com")
|
|
c.Writer.WriteHeaderNow()
|
|
|
|
assert.Equal(t, http.StatusFound, w.Code)
|
|
assert.Equal(t, "http://google.com", w.Header().Get("Location"))
|
|
}
|
|
|
|
func TestContextRenderRedirectWith201(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
|
|
c.Redirect(http.StatusCreated, "/resource")
|
|
c.Writer.WriteHeaderNow()
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, "/resource", w.Header().Get("Location"))
|
|
}
|
|
|
|
func TestContextRenderRedirectAll(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
|
|
assert.Panics(t, func() { c.Redirect(http.StatusOK, "/resource") })
|
|
assert.Panics(t, func() { c.Redirect(http.StatusAccepted, "/resource") })
|
|
assert.Panics(t, func() { c.Redirect(299, "/resource") })
|
|
assert.Panics(t, func() { c.Redirect(309, "/resource") })
|
|
assert.NotPanics(t, func() { c.Redirect(http.StatusMultipleChoices, "/resource") })
|
|
assert.NotPanics(t, func() { c.Redirect(http.StatusPermanentRedirect, "/resource") })
|
|
}
|
|
|
|
func TestContextNegotiationWithJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
c.Request, _ = http.NewRequest("POST", "", nil)
|
|
|
|
c.Negotiate(http.StatusOK, Negotiate{
|
|
Offered: []string{MIMEJSON, MIMEXML},
|
|
Data: H{"foo": "bar"},
|
|
})
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "{\"foo\":\"bar\"}\n", w.Body.String())
|
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
func TestContextNegotiationWithXML(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
c.Request, _ = http.NewRequest("POST", "", nil)
|
|
|
|
c.Negotiate(http.StatusOK, Negotiate{
|
|
Offered: []string{MIMEXML, MIMEJSON},
|
|
Data: H{"foo": "bar"},
|
|
})
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
|
|
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
func TestContextNegotiationWithHTML(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, router := CreateTestContext(w)
|
|
c.Request, _ = http.NewRequest("POST", "", nil)
|
|
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
|
router.SetHTMLTemplate(templ)
|
|
|
|
c.Negotiate(http.StatusOK, Negotiate{
|
|
Offered: []string{MIMEHTML},
|
|
Data: H{"name": "gin"},
|
|
HTMLName: "t",
|
|
})
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "Hello gin", w.Body.String())
|
|
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
|
}
|
|
|
|
func TestContextNegotiationNotSupport(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
c.Request, _ = http.NewRequest("POST", "", nil)
|
|
|
|
c.Negotiate(http.StatusOK, Negotiate{
|
|
Offered: []string{MIMEPOSTForm},
|
|
})
|
|
|
|
assert.Equal(t, http.StatusNotAcceptable, w.Code)
|
|
assert.Equal(t, c.index, abortIndex)
|
|
assert.True(t, c.IsAborted())
|
|
}
|
|
|
|
func TestContextNegotiationFormat(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "", nil)
|
|
|
|
assert.Panics(t, func() { c.NegotiateFormat() })
|
|
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
|
|
assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML, MIMEJSON))
|
|
}
|
|
|
|
func TestContextNegotiationFormatWithAccept(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
|
c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8")
|
|
|
|
assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEJSON, MIMEXML))
|
|
assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEXML, MIMEHTML))
|
|
assert.Empty(t, c.NegotiateFormat(MIMEJSON))
|
|
}
|
|
|
|
func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
|
c.Request.Header.Add("Accept", "*/*")
|
|
|
|
assert.Equal(t, c.NegotiateFormat("*/*"), "*/*")
|
|
assert.Equal(t, c.NegotiateFormat("text/*"), "text/*")
|
|
assert.Equal(t, c.NegotiateFormat("application/*"), "application/*")
|
|
assert.Equal(t, c.NegotiateFormat(MIMEJSON), MIMEJSON)
|
|
assert.Equal(t, c.NegotiateFormat(MIMEXML), MIMEXML)
|
|
assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML)
|
|
|
|
c, _ = CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
|
c.Request.Header.Add("Accept", "text/*")
|
|
|
|
assert.Equal(t, c.NegotiateFormat("*/*"), "*/*")
|
|
assert.Equal(t, c.NegotiateFormat("text/*"), "text/*")
|
|
assert.Equal(t, c.NegotiateFormat("application/*"), "")
|
|
assert.Equal(t, c.NegotiateFormat(MIMEJSON), "")
|
|
assert.Equal(t, c.NegotiateFormat(MIMEXML), "")
|
|
assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML)
|
|
}
|
|
|
|
func TestContextNegotiationFormatCustom(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
|
c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8")
|
|
|
|
c.Accepted = nil
|
|
c.SetAccepted(MIMEJSON, MIMEXML)
|
|
|
|
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
|
|
assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEXML, MIMEHTML))
|
|
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON))
|
|
}
|
|
|
|
func TestContextIsAborted(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
assert.False(t, c.IsAborted())
|
|
|
|
c.Abort()
|
|
assert.True(t, c.IsAborted())
|
|
|
|
c.Next()
|
|
assert.True(t, c.IsAborted())
|
|
|
|
c.index++
|
|
assert.True(t, c.IsAborted())
|
|
}
|
|
|
|
// TestContextData tests that the response can be written from `bytesting`
|
|
// with specified MIME type
|
|
func TestContextAbortWithStatus(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.index = 4
|
|
c.AbortWithStatus(http.StatusUnauthorized)
|
|
|
|
assert.Equal(t, abortIndex, c.index)
|
|
assert.Equal(t, http.StatusUnauthorized, c.Writer.Status())
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
assert.True(t, c.IsAborted())
|
|
}
|
|
|
|
type testJSONAbortMsg struct {
|
|
Foo string `json:"foo"`
|
|
Bar string `json:"bar"`
|
|
}
|
|
|
|
func TestContextAbortWithStatusJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
c.index = 4
|
|
|
|
in := new(testJSONAbortMsg)
|
|
in.Bar = "barValue"
|
|
in.Foo = "fooValue"
|
|
|
|
c.AbortWithStatusJSON(http.StatusUnsupportedMediaType, in)
|
|
|
|
assert.Equal(t, abortIndex, c.index)
|
|
assert.Equal(t, http.StatusUnsupportedMediaType, c.Writer.Status())
|
|
assert.Equal(t, http.StatusUnsupportedMediaType, w.Code)
|
|
assert.True(t, c.IsAborted())
|
|
|
|
contentType := w.Header().Get("Content-Type")
|
|
assert.Equal(t, "application/json; charset=utf-8", contentType)
|
|
|
|
buf := new(bytes.Buffer)
|
|
_, err := buf.ReadFrom(w.Body)
|
|
assert.NoError(t, err)
|
|
jsonStringBody := buf.String()
|
|
assert.Equal(t, fmt.Sprint("{\"foo\":\"fooValue\",\"bar\":\"barValue\"}\n"), jsonStringBody)
|
|
}
|
|
|
|
func TestContextError(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
assert.Empty(t, c.Errors)
|
|
|
|
firstErr := errors.New("first error")
|
|
c.Error(firstErr) // nolint: errcheck
|
|
assert.Len(t, c.Errors, 1)
|
|
assert.Equal(t, "Error #01: first error\n", c.Errors.String())
|
|
|
|
secondErr := errors.New("second error")
|
|
c.Error(&Error{ // nolint: errcheck
|
|
Err: secondErr,
|
|
Meta: "some data 2",
|
|
Type: ErrorTypePublic,
|
|
})
|
|
assert.Len(t, c.Errors, 2)
|
|
|
|
assert.Equal(t, firstErr, c.Errors[0].Err)
|
|
assert.Nil(t, c.Errors[0].Meta)
|
|
assert.Equal(t, ErrorTypePrivate, c.Errors[0].Type)
|
|
|
|
assert.Equal(t, secondErr, c.Errors[1].Err)
|
|
assert.Equal(t, "some data 2", c.Errors[1].Meta)
|
|
assert.Equal(t, ErrorTypePublic, c.Errors[1].Type)
|
|
|
|
assert.Equal(t, c.Errors.Last(), c.Errors[1])
|
|
|
|
defer func() {
|
|
if recover() == nil {
|
|
t.Error("didn't panic")
|
|
}
|
|
}()
|
|
c.Error(nil) // nolint: errcheck
|
|
}
|
|
|
|
func TestContextTypedError(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Error(errors.New("externo 0")).SetType(ErrorTypePublic) // nolint: errcheck
|
|
c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate) // nolint: errcheck
|
|
|
|
for _, err := range c.Errors.ByType(ErrorTypePublic) {
|
|
assert.Equal(t, ErrorTypePublic, err.Type)
|
|
}
|
|
for _, err := range c.Errors.ByType(ErrorTypePrivate) {
|
|
assert.Equal(t, ErrorTypePrivate, err.Type)
|
|
}
|
|
assert.Equal(t, []string{"externo 0", "interno 0"}, c.Errors.Errors())
|
|
}
|
|
|
|
func TestContextAbortWithError(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.AbortWithError(http.StatusUnauthorized, errors.New("bad input")).SetMeta("some input") // nolint: errcheck
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
assert.Equal(t, abortIndex, c.index)
|
|
assert.True(t, c.IsAborted())
|
|
}
|
|
|
|
func TestContextClientIP(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
|
|
|
c.Request.Header.Set("X-Real-IP", " 10.10.10.10 ")
|
|
c.Request.Header.Set("X-Forwarded-For", " 20.20.20.20, 30.30.30.30")
|
|
c.Request.Header.Set("X-Appengine-Remote-Addr", "50.50.50.50")
|
|
c.Request.RemoteAddr = " 40.40.40.40:42123 "
|
|
|
|
assert.Equal(t, "20.20.20.20", c.ClientIP())
|
|
|
|
c.Request.Header.Del("X-Forwarded-For")
|
|
assert.Equal(t, "10.10.10.10", c.ClientIP())
|
|
|
|
c.Request.Header.Set("X-Forwarded-For", "30.30.30.30 ")
|
|
assert.Equal(t, "30.30.30.30", c.ClientIP())
|
|
|
|
c.Request.Header.Del("X-Forwarded-For")
|
|
c.Request.Header.Del("X-Real-IP")
|
|
c.engine.AppEngine = true
|
|
assert.Equal(t, "50.50.50.50", c.ClientIP())
|
|
|
|
c.Request.Header.Del("X-Appengine-Remote-Addr")
|
|
assert.Equal(t, "40.40.40.40", c.ClientIP())
|
|
|
|
// no port
|
|
c.Request.RemoteAddr = "50.50.50.50"
|
|
assert.Empty(t, c.ClientIP())
|
|
}
|
|
|
|
func TestContextContentType(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
|
c.Request.Header.Set("Content-Type", "application/json; charset=utf-8")
|
|
|
|
assert.Equal(t, "application/json", c.ContentType())
|
|
}
|
|
|
|
func TestContextAutoBindJSON(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
|
c.Request.Header.Add("Content-Type", MIMEJSON)
|
|
|
|
var obj struct {
|
|
Foo string `json:"foo"`
|
|
Bar string `json:"bar"`
|
|
}
|
|
assert.NoError(t, c.Bind(&obj))
|
|
assert.Equal(t, "foo", obj.Bar)
|
|
assert.Equal(t, "bar", obj.Foo)
|
|
assert.Empty(t, c.Errors)
|
|
}
|
|
|
|
func TestContextBindWithJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
|
|
|
var obj struct {
|
|
Foo string `json:"foo"`
|
|
Bar string `json:"bar"`
|
|
}
|
|
assert.NoError(t, c.BindJSON(&obj))
|
|
assert.Equal(t, "foo", obj.Bar)
|
|
assert.Equal(t, "bar", obj.Foo)
|
|
assert.Equal(t, 0, w.Body.Len())
|
|
}
|
|
func TestContextBindWithXML(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
|
|
<root>
|
|
<foo>FOO</foo>
|
|
<bar>BAR</bar>
|
|
</root>`))
|
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
|
|
|
var obj struct {
|
|
Foo string `xml:"foo"`
|
|
Bar string `xml:"bar"`
|
|
}
|
|
assert.NoError(t, c.BindXML(&obj))
|
|
assert.Equal(t, "FOO", obj.Foo)
|
|
assert.Equal(t, "BAR", obj.Bar)
|
|
assert.Equal(t, 0, w.Body.Len())
|
|
}
|
|
|
|
func TestContextBindHeader(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
|
c.Request.Header.Add("rate", "8000")
|
|
c.Request.Header.Add("domain", "music")
|
|
c.Request.Header.Add("limit", "1000")
|
|
|
|
var testHeader struct {
|
|
Rate int `header:"Rate"`
|
|
Domain string `header:"Domain"`
|
|
Limit int `header:"limit"`
|
|
}
|
|
|
|
assert.NoError(t, c.BindHeader(&testHeader))
|
|
assert.Equal(t, 8000, testHeader.Rate)
|
|
assert.Equal(t, "music", testHeader.Domain)
|
|
assert.Equal(t, 1000, testHeader.Limit)
|
|
assert.Equal(t, 0, w.Body.Len())
|
|
}
|
|
|
|
func TestContextBindWithQuery(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused"))
|
|
|
|
var obj struct {
|
|
Foo string `form:"foo"`
|
|
Bar string `form:"bar"`
|
|
}
|
|
assert.NoError(t, c.BindQuery(&obj))
|
|
assert.Equal(t, "foo", obj.Bar)
|
|
assert.Equal(t, "bar", obj.Foo)
|
|
assert.Equal(t, 0, w.Body.Len())
|
|
}
|
|
|
|
func TestContextBindWithYAML(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
|
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
|
|
|
var obj struct {
|
|
Foo string `yaml:"foo"`
|
|
Bar string `yaml:"bar"`
|
|
}
|
|
assert.NoError(t, c.BindYAML(&obj))
|
|
assert.Equal(t, "foo", obj.Bar)
|
|
assert.Equal(t, "bar", obj.Foo)
|
|
assert.Equal(t, 0, w.Body.Len())
|
|
}
|
|
|
|
func TestContextBadAutoBind(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
|
c.Request.Header.Add("Content-Type", MIMEJSON)
|
|
var obj struct {
|
|
Foo string `json:"foo"`
|
|
Bar string `json:"bar"`
|
|
}
|
|
|
|
assert.False(t, c.IsAborted())
|
|
assert.Error(t, c.Bind(&obj))
|
|
c.Writer.WriteHeaderNow()
|
|
|
|
assert.Empty(t, obj.Bar)
|
|
assert.Empty(t, obj.Foo)
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
assert.True(t, c.IsAborted())
|
|
}
|
|
|
|
func TestContextAutoShouldBindJSON(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
|
c.Request.Header.Add("Content-Type", MIMEJSON)
|
|
|
|
var obj struct {
|
|
Foo string `json:"foo"`
|
|
Bar string `json:"bar"`
|
|
}
|
|
assert.NoError(t, c.ShouldBind(&obj))
|
|
assert.Equal(t, "foo", obj.Bar)
|
|
assert.Equal(t, "bar", obj.Foo)
|
|
assert.Empty(t, c.Errors)
|
|
}
|
|
|
|
func TestContextShouldBindWithJSON(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
|
|
|
var obj struct {
|
|
Foo string `json:"foo"`
|
|
Bar string `json:"bar"`
|
|
}
|
|
assert.NoError(t, c.ShouldBindJSON(&obj))
|
|
assert.Equal(t, "foo", obj.Bar)
|
|
assert.Equal(t, "bar", obj.Foo)
|
|
assert.Equal(t, 0, w.Body.Len())
|
|
}
|
|
|
|
func TestContextShouldBindWithXML(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
|
|
<root>
|
|
<foo>FOO</foo>
|
|
<bar>BAR</bar>
|
|
</root>`))
|
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
|
|
|
var obj struct {
|
|
Foo string `xml:"foo"`
|
|
Bar string `xml:"bar"`
|
|
}
|
|
assert.NoError(t, c.ShouldBindXML(&obj))
|
|
assert.Equal(t, "FOO", obj.Foo)
|
|
assert.Equal(t, "BAR", obj.Bar)
|
|
assert.Equal(t, 0, w.Body.Len())
|
|
}
|
|
|
|
func TestContextShouldBindHeader(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
|
c.Request.Header.Add("rate", "8000")
|
|
c.Request.Header.Add("domain", "music")
|
|
c.Request.Header.Add("limit", "1000")
|
|
|
|
var testHeader struct {
|
|
Rate int `header:"Rate"`
|
|
Domain string `header:"Domain"`
|
|
Limit int `header:"limit"`
|
|
}
|
|
|
|
assert.NoError(t, c.ShouldBindHeader(&testHeader))
|
|
assert.Equal(t, 8000, testHeader.Rate)
|
|
assert.Equal(t, "music", testHeader.Domain)
|
|
assert.Equal(t, 1000, testHeader.Limit)
|
|
assert.Equal(t, 0, w.Body.Len())
|
|
}
|
|
|
|
func TestContextShouldBindWithQuery(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo&Foo=bar1&Bar=foo1", bytes.NewBufferString("foo=unused"))
|
|
|
|
var obj struct {
|
|
Foo string `form:"foo"`
|
|
Bar string `form:"bar"`
|
|
Foo1 string `form:"Foo"`
|
|
Bar1 string `form:"Bar"`
|
|
}
|
|
assert.NoError(t, c.ShouldBindQuery(&obj))
|
|
assert.Equal(t, "foo", obj.Bar)
|
|
assert.Equal(t, "bar", obj.Foo)
|
|
assert.Equal(t, "foo1", obj.Bar1)
|
|
assert.Equal(t, "bar1", obj.Foo1)
|
|
assert.Equal(t, 0, w.Body.Len())
|
|
}
|
|
|
|
func TestContextShouldBindWithYAML(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
|
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
|
|
|
var obj struct {
|
|
Foo string `yaml:"foo"`
|
|
Bar string `yaml:"bar"`
|
|
}
|
|
assert.NoError(t, c.ShouldBindYAML(&obj))
|
|
assert.Equal(t, "foo", obj.Bar)
|
|
assert.Equal(t, "bar", obj.Foo)
|
|
assert.Equal(t, 0, w.Body.Len())
|
|
}
|
|
|
|
func TestContextBadAutoShouldBind(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
|
c.Request.Header.Add("Content-Type", MIMEJSON)
|
|
var obj struct {
|
|
Foo string `json:"foo"`
|
|
Bar string `json:"bar"`
|
|
}
|
|
|
|
assert.False(t, c.IsAborted())
|
|
assert.Error(t, c.ShouldBind(&obj))
|
|
|
|
assert.Empty(t, obj.Bar)
|
|
assert.Empty(t, obj.Foo)
|
|
assert.False(t, c.IsAborted())
|
|
}
|
|
|
|
func TestContextShouldBindBodyWith(t *testing.T) {
|
|
type typeA struct {
|
|
Foo string `json:"foo" xml:"foo" binding:"required"`
|
|
}
|
|
type typeB struct {
|
|
Bar string `json:"bar" xml:"bar" binding:"required"`
|
|
}
|
|
for _, tt := range []struct {
|
|
name string
|
|
bindingA, bindingB binding.BindingBody
|
|
bodyA, bodyB string
|
|
}{
|
|
{
|
|
name: "JSON & JSON",
|
|
bindingA: binding.JSON,
|
|
bindingB: binding.JSON,
|
|
bodyA: `{"foo":"FOO"}`,
|
|
bodyB: `{"bar":"BAR"}`,
|
|
},
|
|
{
|
|
name: "JSON & XML",
|
|
bindingA: binding.JSON,
|
|
bindingB: binding.XML,
|
|
bodyA: `{"foo":"FOO"}`,
|
|
bodyB: `<?xml version="1.0" encoding="UTF-8"?>
|
|
<root>
|
|
<bar>BAR</bar>
|
|
</root>`,
|
|
},
|
|
{
|
|
name: "XML & XML",
|
|
bindingA: binding.XML,
|
|
bindingB: binding.XML,
|
|
bodyA: `<?xml version="1.0" encoding="UTF-8"?>
|
|
<root>
|
|
<foo>FOO</foo>
|
|
</root>`,
|
|
bodyB: `<?xml version="1.0" encoding="UTF-8"?>
|
|
<root>
|
|
<bar>BAR</bar>
|
|
</root>`,
|
|
},
|
|
} {
|
|
t.Logf("testing: %s", tt.name)
|
|
// bodyA to typeA and typeB
|
|
{
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
c.Request, _ = http.NewRequest(
|
|
"POST", "http://example.com", bytes.NewBufferString(tt.bodyA),
|
|
)
|
|
// When it binds to typeA and typeB, it finds the body is
|
|
// not typeB but typeA.
|
|
objA := typeA{}
|
|
assert.NoError(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
|
|
assert.Equal(t, typeA{"FOO"}, objA)
|
|
objB := typeB{}
|
|
assert.Error(t, c.ShouldBindBodyWith(&objB, tt.bindingB))
|
|
assert.NotEqual(t, typeB{"BAR"}, objB)
|
|
}
|
|
// bodyB to typeA and typeB
|
|
{
|
|
// When it binds to typeA and typeB, it finds the body is
|
|
// not typeA but typeB.
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
c.Request, _ = http.NewRequest(
|
|
"POST", "http://example.com", bytes.NewBufferString(tt.bodyB),
|
|
)
|
|
objA := typeA{}
|
|
assert.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
|
|
assert.NotEqual(t, typeA{"FOO"}, objA)
|
|
objB := typeB{}
|
|
assert.NoError(t, c.ShouldBindBodyWith(&objB, tt.bindingB))
|
|
assert.Equal(t, typeB{"BAR"}, objB)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestContextGolangContext(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
|
assert.NoError(t, c.Err())
|
|
assert.Nil(t, c.Done())
|
|
ti, ok := c.Deadline()
|
|
assert.Equal(t, ti, time.Time{})
|
|
assert.False(t, ok)
|
|
assert.Equal(t, c.Value(0), c.Request)
|
|
assert.Nil(t, c.Value("foo"))
|
|
|
|
c.Set("foo", "bar")
|
|
assert.Equal(t, "bar", c.Value("foo"))
|
|
assert.Nil(t, c.Value(1))
|
|
}
|
|
|
|
func TestWebsocketsRequired(t *testing.T) {
|
|
// Example request from spec: https://tools.ietf.org/html/rfc6455#section-1.2
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("GET", "/chat", nil)
|
|
c.Request.Header.Set("Host", "server.example.com")
|
|
c.Request.Header.Set("Upgrade", "websocket")
|
|
c.Request.Header.Set("Connection", "Upgrade")
|
|
c.Request.Header.Set("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==")
|
|
c.Request.Header.Set("Origin", "http://example.com")
|
|
c.Request.Header.Set("Sec-WebSocket-Protocol", "chat, superchat")
|
|
c.Request.Header.Set("Sec-WebSocket-Version", "13")
|
|
|
|
assert.True(t, c.IsWebsocket())
|
|
|
|
// Normal request, no websocket required.
|
|
c, _ = CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("GET", "/chat", nil)
|
|
c.Request.Header.Set("Host", "server.example.com")
|
|
|
|
assert.False(t, c.IsWebsocket())
|
|
}
|
|
|
|
func TestGetRequestHeaderValue(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
c.Request, _ = http.NewRequest("GET", "/chat", nil)
|
|
c.Request.Header.Set("Gin-Version", "1.0.0")
|
|
|
|
assert.Equal(t, "1.0.0", c.GetHeader("Gin-Version"))
|
|
assert.Empty(t, c.GetHeader("Connection"))
|
|
}
|
|
|
|
func TestContextGetRawData(t *testing.T) {
|
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
body := bytes.NewBufferString("Fetch binary post data")
|
|
c.Request, _ = http.NewRequest("POST", "/", body)
|
|
c.Request.Header.Add("Content-Type", MIMEPOSTForm)
|
|
|
|
data, err := c.GetRawData()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, "Fetch binary post data", string(data))
|
|
}
|
|
|
|
func TestContextRenderDataFromReader(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
body := "#!PNG some raw data"
|
|
reader := strings.NewReader(body)
|
|
contentLength := int64(len(body))
|
|
contentType := "image/png"
|
|
extraHeaders := map[string]string{"Content-Disposition": `attachment; filename="gopher.png"`}
|
|
|
|
c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, body, w.Body.String())
|
|
assert.Equal(t, contentType, w.Header().Get("Content-Type"))
|
|
assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length"))
|
|
assert.Equal(t, extraHeaders["Content-Disposition"], w.Header().Get("Content-Disposition"))
|
|
}
|
|
|
|
type TestResponseRecorder struct {
|
|
*httptest.ResponseRecorder
|
|
closeChannel chan bool
|
|
}
|
|
|
|
func (r *TestResponseRecorder) CloseNotify() <-chan bool {
|
|
return r.closeChannel
|
|
}
|
|
|
|
func (r *TestResponseRecorder) closeClient() {
|
|
r.closeChannel <- true
|
|
}
|
|
|
|
func CreateTestResponseRecorder() *TestResponseRecorder {
|
|
return &TestResponseRecorder{
|
|
httptest.NewRecorder(),
|
|
make(chan bool, 1),
|
|
}
|
|
}
|
|
|
|
func TestContextStream(t *testing.T) {
|
|
w := CreateTestResponseRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
stopStream := true
|
|
c.Stream(func(w io.Writer) bool {
|
|
defer func() {
|
|
stopStream = false
|
|
}()
|
|
|
|
_, err := w.Write([]byte("test"))
|
|
assert.NoError(t, err)
|
|
|
|
return stopStream
|
|
})
|
|
|
|
assert.Equal(t, "testtest", w.Body.String())
|
|
}
|
|
|
|
func TestContextStreamWithClientGone(t *testing.T) {
|
|
w := CreateTestResponseRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.Stream(func(writer io.Writer) bool {
|
|
defer func() {
|
|
w.closeClient()
|
|
}()
|
|
|
|
_, err := writer.Write([]byte("test"))
|
|
assert.NoError(t, err)
|
|
|
|
return true
|
|
})
|
|
|
|
assert.Equal(t, "test", w.Body.String())
|
|
}
|
|
|
|
func TestContextResetInHandler(t *testing.T) {
|
|
w := CreateTestResponseRecorder()
|
|
c, _ := CreateTestContext(w)
|
|
|
|
c.handlers = []HandlerFunc{
|
|
func(c *Context) { c.reset() },
|
|
}
|
|
assert.NotPanics(t, func() {
|
|
c.Next()
|
|
})
|
|
}
|
|
|
|
func TestRaceParamsContextCopy(t *testing.T) {
|
|
DefaultWriter = os.Stdout
|
|
router := Default()
|
|
nameGroup := router.Group("/:name")
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
{
|
|
nameGroup.GET("/api", func(c *Context) {
|
|
go func(c *Context, param string) {
|
|
defer wg.Done()
|
|
// First assert must be executed after the second request
|
|
time.Sleep(50 * time.Millisecond)
|
|
assert.Equal(t, c.Param("name"), param)
|
|
}(c.Copy(), c.Param("name"))
|
|
})
|
|
}
|
|
performRequest(router, "GET", "/name1/api")
|
|
performRequest(router, "GET", "/name2/api")
|
|
wg.Wait()
|
|
}
|