Add some test cases and run test cases on binding/render dir (#1168)

* Travis run test cases on binding and render dir and add some test cases for binding and render
This commit is contained in:
田欧 2018-01-26 11:46:11 +08:00 committed by Bo-Yi Wu
parent 2fbb97117c
commit 783c7ee9c1
5 changed files with 957 additions and 2 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ vendor/*
!vendor/vendor.json
coverage.out
count.out
test

View File

@ -9,7 +9,7 @@ install: deps
.PHONY: test
test:
go test -v -covermode=count -coverprofile=coverage.out
sh coverage.sh
.PHONY: fmt
fmt:

View File

@ -6,9 +6,13 @@ package binding
import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
"mime/multipart"
"net/http"
"testing"
"time"
"github.com/gin-gonic/gin/binding/example"
"github.com/golang/protobuf/proto"
@ -25,6 +29,116 @@ type FooBarStruct struct {
Bar string `msgpack:"bar" json:"bar" form:"bar" xml:"bar" binding:"required"`
}
type FooStructUseNumber struct {
Foo interface{} `json:"foo" binding:"required"`
}
type FooBarStructForTimeType struct {
TimeFoo time.Time `form:"time_foo" time_format:"2006-01-02" time_utc:"1" time_location:"Asia/Chongqing"`
TimeBar time.Time `form:"time_bar" time_format:"2006-01-02" time_utc:"1"`
}
type FooStructForTimeTypeNotFormat struct {
TimeFoo time.Time `form:"time_foo"`
}
type FooStructForTimeTypeFailFormat struct {
TimeFoo time.Time `form:"time_foo" time_format:"2017-11-15"`
}
type FooStructForTimeTypeFailLocation struct {
TimeFoo time.Time `form:"time_foo" time_format:"2006-01-02" time_location:"/asia/chongqing"`
}
type FooStructForMapType struct {
// Unknown type: not support map
MapFoo map[string]interface{} `form:"map_foo"`
}
type InvalidNameType struct {
TestName string `invalid_name:"test_name"`
}
type InvalidNameMapType struct {
TestName struct {
MapFoo map[string]interface{} `form:"map_foo"`
}
}
type FooStructForSliceType struct {
SliceFoo []int `form:"slice_foo"`
}
type FooStructForSliceMapType struct {
// Unknown type: not support map
SliceMapFoo []map[string]interface{} `form:"slice_map_foo"`
}
type FooBarStructForIntType struct {
IntFoo int `form:"int_foo"`
IntBar int `form:"int_bar" binding:"required"`
}
type FooBarStructForInt8Type struct {
Int8Foo int8 `form:"int8_foo"`
Int8Bar int8 `form:"int8_bar" binding:"required"`
}
type FooBarStructForInt16Type struct {
Int16Foo int16 `form:"int16_foo"`
Int16Bar int16 `form:"int16_bar" binding:"required"`
}
type FooBarStructForInt32Type struct {
Int32Foo int32 `form:"int32_foo"`
Int32Bar int32 `form:"int32_bar" binding:"required"`
}
type FooBarStructForInt64Type struct {
Int64Foo int64 `form:"int64_foo"`
Int64Bar int64 `form:"int64_bar" binding:"required"`
}
type FooBarStructForUintType struct {
UintFoo uint `form:"uint_foo"`
UintBar uint `form:"uint_bar" binding:"required"`
}
type FooBarStructForUint8Type struct {
Uint8Foo uint8 `form:"uint8_foo"`
Uint8Bar uint8 `form:"uint8_bar" binding:"required"`
}
type FooBarStructForUint16Type struct {
Uint16Foo uint16 `form:"uint16_foo"`
Uint16Bar uint16 `form:"uint16_bar" binding:"required"`
}
type FooBarStructForUint32Type struct {
Uint32Foo uint32 `form:"uint32_foo"`
Uint32Bar uint32 `form:"uint32_bar" binding:"required"`
}
type FooBarStructForUint64Type struct {
Uint64Foo uint64 `form:"uint64_foo"`
Uint64Bar uint64 `form:"uint64_bar" binding:"required"`
}
type FooBarStructForBoolType struct {
BoolFoo bool `form:"bool_foo"`
BoolBar bool `form:"bool_bar" binding:"required"`
}
type FooBarStructForFloat32Type struct {
Float32Foo float32 `form:"float32_foo"`
Float32Bar float32 `form:"float32_bar" binding:"required"`
}
type FooBarStructForFloat64Type struct {
Float64Foo float64 `form:"float64_foo"`
Float64Bar float64 `form:"float64_bar" binding:"required"`
}
func TestBindingDefault(t *testing.T) {
assert.Equal(t, Default("GET", ""), Form)
assert.Equal(t, Default("GET", MIMEJSON), Form)
@ -55,6 +169,20 @@ func TestBindingJSON(t *testing.T) {
`{"foo": "bar"}`, `{"bar": "foo"}`)
}
func TestBindingJSONUseNumber(t *testing.T) {
testBodyBindingUseNumber(t,
JSON, "json",
"/", "/",
`{"foo": 123}`, `{"bar": "foo"}`)
}
func TestBindingJSONUseNumber2(t *testing.T) {
testBodyBindingUseNumber2(t,
JSON, "json",
"/", "/",
`{"foo": 123}`, `{"bar": "foo"}`)
}
func TestBindingForm(t *testing.T) {
testFormBinding(t, "POST",
"/", "/",
@ -67,6 +195,174 @@ func TestBindingForm2(t *testing.T) {
"", "")
}
func TestBindingFormForTime(t *testing.T) {
testFormBindingForTime(t, "POST",
"/", "/",
"time_foo=2017-11-15&time_bar=", "bar2=foo")
testFormBindingForTimeNotFormat(t, "POST",
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
testFormBindingForTimeFailFormat(t, "POST",
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
testFormBindingForTimeFailLocation(t, "POST",
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
}
func TestBindingFormForTime2(t *testing.T) {
testFormBindingForTime(t, "GET",
"/?time_foo=2017-11-15&time_bar=", "/?bar2=foo",
"", "")
testFormBindingForTimeNotFormat(t, "GET",
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
testFormBindingForTimeFailFormat(t, "GET",
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
testFormBindingForTimeFailLocation(t, "GET",
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
}
func TestBindingFormInvalidName(t *testing.T) {
testFormBindingInvalidName(t, "POST",
"/", "/",
"test_name=bar", "bar2=foo")
}
func TestBindingFormInvalidName2(t *testing.T) {
testFormBindingInvalidName2(t, "POST",
"/", "/",
"map_foo=bar", "bar2=foo")
}
func TestBindingFormForType(t *testing.T) {
testFormBindingForType(t, "POST",
"/", "/",
"map_foo=", "bar2=1", "Map")
testFormBindingForType(t, "POST",
"/", "/",
"slice_foo=1&slice_foo=2", "bar2=1&bar2=2", "Slice")
testFormBindingForType(t, "GET",
"/?slice_foo=1&slice_foo=2", "/?bar2=1&bar2=2",
"", "", "Slice")
testFormBindingForType(t, "POST",
"/", "/",
"slice_map_foo=1&slice_map_foo=2", "bar2=1&bar2=2", "SliceMap")
testFormBindingForType(t, "GET",
"/?slice_map_foo=1&slice_map_foo=2", "/?bar2=1&bar2=2",
"", "", "SliceMap")
testFormBindingForType(t, "POST",
"/", "/",
"int_foo=&int_bar=-12", "bar2=-123", "Int")
testFormBindingForType(t, "GET",
"/?int_foo=&int_bar=-12", "/?bar2=-123",
"", "", "Int")
testFormBindingForType(t, "POST",
"/", "/",
"int8_foo=&int8_bar=-12", "bar2=-123", "Int8")
testFormBindingForType(t, "GET",
"/?int8_foo=&int8_bar=-12", "/?bar2=-123",
"", "", "Int8")
testFormBindingForType(t, "POST",
"/", "/",
"int16_foo=&int16_bar=-12", "bar2=-123", "Int16")
testFormBindingForType(t, "GET",
"/?int16_foo=&int16_bar=-12", "/?bar2=-123",
"", "", "Int16")
testFormBindingForType(t, "POST",
"/", "/",
"int32_foo=&int32_bar=-12", "bar2=-123", "Int32")
testFormBindingForType(t, "GET",
"/?int32_foo=&int32_bar=-12", "/?bar2=-123",
"", "", "Int32")
testFormBindingForType(t, "POST",
"/", "/",
"int64_foo=&int64_bar=-12", "bar2=-123", "Int64")
testFormBindingForType(t, "GET",
"/?int64_foo=&int64_bar=-12", "/?bar2=-123",
"", "", "Int64")
testFormBindingForType(t, "POST",
"/", "/",
"uint_foo=&uint_bar=12", "bar2=123", "Uint")
testFormBindingForType(t, "GET",
"/?uint_foo=&uint_bar=12", "/?bar2=123",
"", "", "Uint")
testFormBindingForType(t, "POST",
"/", "/",
"uint8_foo=&uint8_bar=12", "bar2=123", "Uint8")
testFormBindingForType(t, "GET",
"/?uint8_foo=&uint8_bar=12", "/?bar2=123",
"", "", "Uint8")
testFormBindingForType(t, "POST",
"/", "/",
"uint16_foo=&uint16_bar=12", "bar2=123", "Uint16")
testFormBindingForType(t, "GET",
"/?uint16_foo=&uint16_bar=12", "/?bar2=123",
"", "", "Uint16")
testFormBindingForType(t, "POST",
"/", "/",
"uint32_foo=&uint32_bar=12", "bar2=123", "Uint32")
testFormBindingForType(t, "GET",
"/?uint32_foo=&uint32_bar=12", "/?bar2=123",
"", "", "Uint32")
testFormBindingForType(t, "POST",
"/", "/",
"uint64_foo=&uint64_bar=12", "bar2=123", "Uint64")
testFormBindingForType(t, "GET",
"/?uint64_foo=&uint64_bar=12", "/?bar2=123",
"", "", "Uint64")
testFormBindingForType(t, "POST",
"/", "/",
"bool_foo=&bool_bar=true", "bar2=true", "Bool")
testFormBindingForType(t, "GET",
"/?bool_foo=&bool_bar=true", "/?bar2=true",
"", "", "Bool")
testFormBindingForType(t, "POST",
"/", "/",
"float32_foo=&float32_bar=-12.34", "bar2=12.3", "Float32")
testFormBindingForType(t, "GET",
"/?float32_foo=&float32_bar=-12.34", "/?bar2=12.3",
"", "", "Float32")
testFormBindingForType(t, "POST",
"/", "/",
"float64_foo=&float64_bar=-12.34", "bar2=12.3", "Float64")
testFormBindingForType(t, "GET",
"/?float64_foo=&float64_bar=-12.34", "/?bar2=12.3",
"", "", "Float64")
}
func TestBindingQuery(t *testing.T) {
testQueryBinding(t, "POST",
"/?foo=bar&bar=foo", "/",
@ -79,6 +375,18 @@ func TestBindingQuery2(t *testing.T) {
"foo=unused", "")
}
func TestBindingQueryFail(t *testing.T) {
testQueryBindingFail(t, "POST",
"/?map_foo=", "/",
"map_foo=unused", "bar2=foo")
}
func TestBindingQueryFail2(t *testing.T) {
testQueryBindingFail(t, "GET",
"/?map_foo=", "/?bar2=foo",
"map_foo=unused", "")
}
func TestBindingXML(t *testing.T) {
testBodyBinding(t,
XML, "xml",
@ -86,12 +394,25 @@ func TestBindingXML(t *testing.T) {
"<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>")
}
func TestBindingXMLFail(t *testing.T) {
testBodyBindingFail(t,
XML, "xml",
"/", "/",
"<map><foo>bar<foo></map>", "<map><bar>foo</bar></map>")
}
func createFormPostRequest() *http.Request {
req, _ := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
}
func createFormPostRequestFail() *http.Request {
req, _ := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo=bar"))
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
}
func createFormMultipartRequest() *http.Request {
boundary := "--testboundary"
body := new(bytes.Buffer)
@ -106,24 +427,53 @@ func createFormMultipartRequest() *http.Request {
return req
}
func createFormMultipartRequestFail() *http.Request {
boundary := "--testboundary"
body := new(bytes.Buffer)
mw := multipart.NewWriter(body)
defer mw.Close()
mw.SetBoundary(boundary)
mw.WriteField("map_foo", "bar")
req, _ := http.NewRequest("POST", "/?map_foo=getfoo", body)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
func TestBindingFormPost(t *testing.T) {
req := createFormPostRequest()
var obj FooBarStruct
FormPost.Bind(req, &obj)
assert.Equal(t, FormPost.Name(), "form-urlencoded")
assert.Equal(t, obj.Foo, "bar")
assert.Equal(t, obj.Bar, "foo")
}
func TestBindingFormPostFail(t *testing.T) {
req := createFormPostRequestFail()
var obj FooStructForMapType
err := FormPost.Bind(req, &obj)
assert.Error(t, err)
}
func TestBindingFormMultipart(t *testing.T) {
req := createFormMultipartRequest()
var obj FooBarStruct
FormMultipart.Bind(req, &obj)
assert.Equal(t, FormMultipart.Name(), "multipart/form-data")
assert.Equal(t, obj.Foo, "bar")
assert.Equal(t, obj.Bar, "foo")
}
func TestBindingFormMultipartFail(t *testing.T) {
req := createFormMultipartRequestFail()
var obj FooStructForMapType
err := FormMultipart.Bind(req, &obj)
assert.Error(t, err)
}
func TestBindingProtoBuf(t *testing.T) {
test := &example.Test{
Label: proto.String("yes"),
@ -136,6 +486,18 @@ func TestBindingProtoBuf(t *testing.T) {
string(data), string(data[1:]))
}
func TestBindingProtoBufFail(t *testing.T) {
test := &example.Test{
Label: proto.String("yes"),
}
data, _ := proto.Marshal(test)
testProtoBodyBindingFail(t,
ProtoBuf, "protobuf",
"/", "/",
string(data), string(data[1:]))
}
func TestBindingMsgPack(t *testing.T) {
test := FooStruct{
Foo: "bar",
@ -216,6 +578,323 @@ func testFormBinding(t *testing.T, method, path, badPath, body, badBody string)
assert.Error(t, err)
}
func TestFormBindingFail(t *testing.T) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func TestFormPostBindingFail(t *testing.T) {
b := FormPost
assert.Equal(t, b.Name(), "form-urlencoded")
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func TestFormMultipartBindingFail(t *testing.T) {
b := FormMultipart
assert.Equal(t, b.Name(), "multipart/form-data")
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := FooBarStructForTimeType{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.TimeFoo.Unix(), int64(1510675200))
assert.Equal(t, obj.TimeFoo.Location().String(), "Asia/Chongqing")
assert.Equal(t, obj.TimeBar.Unix(), int64(-62135596800))
assert.Equal(t, obj.TimeBar.Location().String(), "UTC")
obj = FooBarStructForTimeType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTimeNotFormat(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := FooStructForTimeTypeNotFormat{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeNotFormat{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTimeFailFormat(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := FooStructForTimeTypeFailFormat{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeFailFormat{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := FooStructForTimeTypeFailLocation{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeFailLocation{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := InvalidNameType{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.TestName, "")
obj = InvalidNameType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := InvalidNameMapType{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = InvalidNameMapType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody string, typ string) {
b := Form
assert.Equal(t, b.Name(), "form")
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
switch typ {
case "Int":
obj := FooBarStructForIntType{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.IntFoo, int(0))
assert.Equal(t, obj.IntBar, int(-12))
obj = FooBarStructForIntType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Int8":
obj := FooBarStructForInt8Type{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.Int8Foo, int8(0))
assert.Equal(t, obj.Int8Bar, int8(-12))
obj = FooBarStructForInt8Type{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Int16":
obj := FooBarStructForInt16Type{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.Int16Foo, int16(0))
assert.Equal(t, obj.Int16Bar, int16(-12))
obj = FooBarStructForInt16Type{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Int32":
obj := FooBarStructForInt32Type{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.Int32Foo, int32(0))
assert.Equal(t, obj.Int32Bar, int32(-12))
obj = FooBarStructForInt32Type{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Int64":
obj := FooBarStructForInt64Type{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.Int64Foo, int64(0))
assert.Equal(t, obj.Int64Bar, int64(-12))
obj = FooBarStructForInt64Type{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Uint":
obj := FooBarStructForUintType{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.UintFoo, uint(0x0))
assert.Equal(t, obj.UintBar, uint(0xc))
obj = FooBarStructForUintType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Uint8":
obj := FooBarStructForUint8Type{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.Uint8Foo, uint8(0x0))
assert.Equal(t, obj.Uint8Bar, uint8(0xc))
obj = FooBarStructForUint8Type{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Uint16":
obj := FooBarStructForUint16Type{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.Uint16Foo, uint16(0x0))
assert.Equal(t, obj.Uint16Bar, uint16(0xc))
obj = FooBarStructForUint16Type{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Uint32":
obj := FooBarStructForUint32Type{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.Uint32Foo, uint32(0x0))
assert.Equal(t, obj.Uint32Bar, uint32(0xc))
obj = FooBarStructForUint32Type{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Uint64":
obj := FooBarStructForUint64Type{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.Uint64Foo, uint64(0x0))
assert.Equal(t, obj.Uint64Bar, uint64(0xc))
obj = FooBarStructForUint64Type{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Float32":
obj := FooBarStructForFloat32Type{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.Float32Foo, float32(0.0))
assert.Equal(t, obj.Float32Bar, float32(-12.34))
obj = FooBarStructForFloat32Type{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Float64":
obj := FooBarStructForFloat64Type{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.Float64Foo, float64(0.0))
assert.Equal(t, obj.Float64Bar, float64(-12.34))
obj = FooBarStructForFloat64Type{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Bool":
obj := FooBarStructForBoolType{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.BoolFoo, false)
assert.Equal(t, obj.BoolBar, true)
obj = FooBarStructForBoolType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Slice":
obj := FooStructForSliceType{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.SliceFoo, []int{1, 2})
obj = FooStructForSliceType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Map":
obj := FooStructForMapType{}
err := b.Bind(req, &obj)
assert.Error(t, err)
case "SliceMap":
obj := FooStructForSliceMapType{}
err := b.Bind(req, &obj)
assert.Error(t, err)
}
}
func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string) {
b := Query
assert.Equal(t, b.Name(), "query")
@ -231,6 +910,19 @@ func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string)
assert.Equal(t, obj.Bar, "foo")
}
func testQueryBindingFail(t *testing.T, method, path, badPath, body, badBody string) {
b := Query
assert.Equal(t, b.Name(), "query")
obj := FooStructForMapType{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name)
@ -246,6 +938,58 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody
assert.Error(t, err)
}
func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name)
obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body)
EnableDecoderUseNumber = true
err := b.Bind(req, &obj)
assert.NoError(t, err)
// we hope it is int64(123)
v, e := obj.Foo.(json.Number).Int64()
assert.NoError(t, e)
assert.Equal(t, v, int64(123))
obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name)
obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body)
EnableDecoderUseNumber = false
err := b.Bind(req, &obj)
assert.NoError(t, err)
// it will return float64(123) if not use EnableDecoderUseNumber
// maybe it is not hoped
assert.Equal(t, obj.Foo, float64(123))
obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name)
obj := FooStruct{}
req := requestWithBody("POST", path, body)
err := b.Bind(req, &obj)
assert.Error(t, err)
assert.Equal(t, obj.Foo, "")
obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name)
@ -263,6 +1007,30 @@ func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, ba
assert.Error(t, err)
}
type hook struct{}
func (h hook) Read([]byte) (int, error) {
return 0, errors.New("error")
}
func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name)
obj := example.Test{}
req := requestWithBody("POST", path, body)
req.Body = ioutil.NopCloser(&hook{})
req.Header.Add("Content-Type", MIMEPROTOBUF)
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = example.Test{}
req = requestWithBody("POST", badPath, badBody)
req.Header.Add("Content-Type", MIMEPROTOBUF)
err = ProtoBuf.Bind(req, &obj)
assert.Error(t, err)
}
func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name)

13
coverage.sh Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -e
echo "mode: count" > coverage.out
for d in $(go list ./... | grep -E 'gin$|binding$|render$'); do
go test -v -covermode=count -coverprofile=profile.out $d
if [ -f profile.out ]; then
cat profile.out | grep -v "mode:" >> coverage.out
rm profile.out
fi
done

View File

@ -7,7 +7,9 @@ package render
import (
"bytes"
"encoding/xml"
"errors"
"html/template"
"net/http"
"net/http/httptest"
"testing"
@ -24,6 +26,9 @@ func TestRenderMsgPack(t *testing.T) {
"foo": "bar",
}
(MsgPack{data}).WriteContentType(w)
assert.Equal(t, w.Header().Get("Content-Type"), "application/msgpack; charset=utf-8")
err := (MsgPack{data}).Render(w)
assert.NoError(t, err)
@ -45,6 +50,9 @@ func TestRenderJSON(t *testing.T) {
"foo": "bar",
}
(JSON{data}).WriteContentType(w)
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
err := (JSON{data}).Render(w)
assert.NoError(t, err)
@ -52,6 +60,14 @@ func TestRenderJSON(t *testing.T) {
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
}
func TestRenderJSONPanics(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
assert.Panics(t, func() { (JSON{data}).Render(w) })
}
func TestRenderIndentedJSON(t *testing.T) {
w := httptest.NewRecorder()
data := map[string]interface{}{
@ -66,12 +82,24 @@ func TestRenderIndentedJSON(t *testing.T) {
assert.Equal(t, w.Header().Get("Content-Type"), "application/json; charset=utf-8")
}
func TestRenderIndentedJSONPanics(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
err := (IndentedJSON{data}).Render(w)
assert.Error(t, err)
}
func TestRenderSecureJSON(t *testing.T) {
w1 := httptest.NewRecorder()
data := map[string]interface{}{
"foo": "bar",
}
(SecureJSON{"while(1);", data}).WriteContentType(w1)
assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
err1 := (SecureJSON{"while(1);", data}).Render(w1)
assert.NoError(t, err1)
@ -91,6 +119,15 @@ func TestRenderSecureJSON(t *testing.T) {
assert.Equal(t, "application/json; charset=utf-8", w2.Header().Get("Content-Type"))
}
func TestRenderSecureJSONFail(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
err := (SecureJSON{"while(1);", data}).Render(w)
assert.Error(t, err)
}
type xmlmap map[string]interface{}
// Allows type H to be used with xml.Marshal
@ -115,12 +152,45 @@ func (h xmlmap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.EncodeToken(xml.EndElement{Name: start.Name})
}
func TestRenderYAML(t *testing.T) {
w := httptest.NewRecorder()
data := `
a : Easy!
b:
c: 2
d: [3, 4]
`
(YAML{data}).WriteContentType(w)
assert.Equal(t, w.Header().Get("Content-Type"), "application/x-yaml; charset=utf-8")
err := (YAML{data}).Render(w)
assert.NoError(t, err)
assert.Equal(t, w.Body.String(), "\"\\na : Easy!\\nb:\\n\\tc: 2\\n\\td: [3, 4]\\n\\t\"\n")
assert.Equal(t, w.Header().Get("Content-Type"), "application/x-yaml; charset=utf-8")
}
type fail struct{}
// Hook MarshalYAML
func (ft *fail) MarshalYAML() (interface{}, error) {
return nil, errors.New("fail")
}
func TestRenderYAMLFail(t *testing.T) {
w := httptest.NewRecorder()
err := (YAML{&fail{}}).Render(w)
assert.Error(t, err)
}
func TestRenderXML(t *testing.T) {
w := httptest.NewRecorder()
data := xmlmap{
"foo": "bar",
}
(XML{data}).WriteContentType(w)
assert.Equal(t, w.Header().Get("Content-Type"), "application/xml; charset=utf-8")
err := (XML{data}).Render(w)
assert.NoError(t, err)
@ -129,7 +199,30 @@ func TestRenderXML(t *testing.T) {
}
func TestRenderRedirect(t *testing.T) {
// TODO
req, err := http.NewRequest("GET", "/test-redirect", nil)
assert.NoError(t, err)
data1 := Redirect{
Code: 301,
Request: req,
Location: "/new/location",
}
w := httptest.NewRecorder()
err = data1.Render(w)
assert.NoError(t, err)
data2 := Redirect{
Code: 200,
Request: req,
Location: "/new/location",
}
w = httptest.NewRecorder()
assert.Panics(t, func() { data2.Render(w) })
// only improve coverage
data2.WriteContentType(w)
}
func TestRenderData(t *testing.T) {
@ -149,6 +242,12 @@ func TestRenderData(t *testing.T) {
func TestRenderString(t *testing.T) {
w := httptest.NewRecorder()
(String{
Format: "hello %s %d",
Data: []interface{}{},
}).WriteContentType(w)
assert.Equal(t, w.Header().Get("Content-Type"), "text/plain; charset=utf-8")
err := (String{
Format: "hola %s %d",
Data: []interface{}{"manu", 2},
@ -159,6 +258,19 @@ func TestRenderString(t *testing.T) {
assert.Equal(t, w.Header().Get("Content-Type"), "text/plain; charset=utf-8")
}
func TestRenderStringLenZero(t *testing.T) {
w := httptest.NewRecorder()
err := (String{
Format: "hola %s %d",
Data: []interface{}{},
}).Render(w)
assert.NoError(t, err)
assert.Equal(t, w.Body.String(), "hola %s %d")
assert.Equal(t, w.Header().Get("Content-Type"), "text/plain; charset=utf-8")
}
func TestRenderHTMLTemplate(t *testing.T) {
w := httptest.NewRecorder()
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
@ -174,3 +286,64 @@ func TestRenderHTMLTemplate(t *testing.T) {
assert.Equal(t, w.Body.String(), "Hello alexandernyquist")
assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8")
}
func TestRenderHTMLTemplateEmptyName(t *testing.T) {
w := httptest.NewRecorder()
templ := template.Must(template.New("").Parse(`Hello {{.name}}`))
htmlRender := HTMLProduction{Template: templ}
instance := htmlRender.Instance("", map[string]interface{}{
"name": "alexandernyquist",
})
err := instance.Render(w)
assert.NoError(t, err)
assert.Equal(t, w.Body.String(), "Hello alexandernyquist")
assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8")
}
func TestRenderHTMLDebugFiles(t *testing.T) {
w := httptest.NewRecorder()
htmlRender := HTMLDebug{Files: []string{"../fixtures/basic/hello.tmpl"},
Glob: "",
Delims: Delims{Left: "{[{", Right: "}]}"},
FuncMap: nil,
}
instance := htmlRender.Instance("hello.tmpl", map[string]interface{}{
"name": "thinkerou",
})
err := instance.Render(w)
assert.NoError(t, err)
assert.Equal(t, w.Body.String(), "<h1>Hello thinkerou</h1>")
assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8")
}
func TestRenderHTMLDebugGlob(t *testing.T) {
w := httptest.NewRecorder()
htmlRender := HTMLDebug{Files: nil,
Glob: "../fixtures/basic/hello*",
Delims: Delims{Left: "{[{", Right: "}]}"},
FuncMap: nil,
}
instance := htmlRender.Instance("hello.tmpl", map[string]interface{}{
"name": "thinkerou",
})
err := instance.Render(w)
assert.NoError(t, err)
assert.Equal(t, w.Body.String(), "<h1>Hello thinkerou</h1>")
assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8")
}
func TestRenderHTMLDebugPanics(t *testing.T) {
htmlRender := HTMLDebug{Files: nil,
Glob: "",
Delims: Delims{"{{", "}}"},
FuncMap: nil,
}
assert.Panics(t, func() { htmlRender.Instance("", nil) })
}