github.com/bytedance/go-tagexpr@v2.7.5-0.20210114074101-de5b8743ad85+incompatible/binding/bind_test.go (about)

     1  package binding_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"net/url"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/henrylee2cn/ameda"
    15  	"github.com/henrylee2cn/goutil/httpbody"
    16  	"github.com/stretchr/testify/assert"
    17  
    18  	"github.com/bytedance/go-tagexpr/binding"
    19  )
    20  
    21  func TestRawBody(t *testing.T) {
    22  	type Recv struct {
    23  		S []byte   `raw_body:""`
    24  		F **string `raw_body:"" vd:"@:len($)<3; msg:'f too long'"`
    25  	}
    26  	bodyBytes := []byte("raw_body.............")
    27  	req := newRequest("", nil, nil, bytes.NewReader(bodyBytes))
    28  	recv := new(Recv)
    29  	binder := binding.New(nil)
    30  	err := binder.BindAndValidate(recv, req, nil)
    31  	assert.EqualError(t, err, "validating: expr_path=F, cause=f too long")
    32  	assert.Equal(t, bodyBytes, []byte(recv.S))
    33  	bodyCopied, err := binding.GetBody(req)
    34  	assert.NoError(t, err)
    35  	assert.Equal(t, bodyBytes, bodyCopied.Bytes())
    36  	t.Logf("%s", bodyCopied)
    37  }
    38  
    39  func TestQueryString(t *testing.T) {
    40  	type metric string
    41  	type count int32
    42  
    43  	type Recv struct {
    44  		X **struct {
    45  			A []string  `query:"a"`
    46  			B string    `query:"b"`
    47  			C *[]string `query:"c,required"`
    48  			D *string   `query:"d"`
    49  			E *[]***int `query:"e"`
    50  			F metric    `query:"f"`
    51  			G count     `query:"g"`
    52  		}
    53  		Y string  `query:"y,required"`
    54  		Z *string `query:"z"`
    55  	}
    56  	req := newRequest("http://localhost:8080/?a=a1&a=a2&b=b1&c=c1&c=c2&d=d1&d=d&f=qps&g=1002&e=&e=2&y=y1", nil, nil, nil)
    57  	recv := new(Recv)
    58  	binder := binding.New(nil)
    59  	err := binder.BindAndValidate(recv, req, nil)
    60  	assert.EqualError(t, err, "binding: expr_path=X.E, cause=parameter type does not match binding data")
    61  	binder.SetLooseZeroMode(true)
    62  	err = binder.BindAndValidate(recv, req, nil)
    63  	assert.NoError(t, err)
    64  	assert.Equal(t, 0, ***(*(**recv.X).E)[0])
    65  	assert.Equal(t, 2, ***(*(**recv.X).E)[1])
    66  	assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
    67  	assert.Equal(t, "b1", (**recv.X).B)
    68  	assert.Equal(t, []string{"c1", "c2"}, *(**recv.X).C)
    69  	assert.Equal(t, "d1", *(**recv.X).D)
    70  	assert.Equal(t, metric("qps"), (**recv.X).F)
    71  	assert.Equal(t, count(1002), (**recv.X).G)
    72  	assert.Equal(t, "y1", recv.Y)
    73  	assert.Equal(t, (*string)(nil), recv.Z)
    74  }
    75  
    76  func TestGetBody(t *testing.T) {
    77  	type Recv struct {
    78  		X **struct {
    79  			E string `json:"e,required" query:"e,required"`
    80  		}
    81  	}
    82  	req := newRequest("http://localhost:8080/", nil, nil, nil)
    83  	recv := new(Recv)
    84  	binder := binding.New(nil)
    85  	err := binder.BindAndValidate(recv, req, nil)
    86  	assert.EqualError(t, err, "binding: expr_path=X.e, cause=missing required parameter")
    87  }
    88  
    89  func TestQueryNum(t *testing.T) {
    90  	type Recv struct {
    91  		X **struct {
    92  			A []int     `query:"a"`
    93  			B int32     `query:"b"`
    94  			C *[]uint16 `query:"c,required"`
    95  			D *float32  `query:"d"`
    96  		}
    97  		Y bool   `query:"y,required"`
    98  		Z *int64 `query:"z"`
    99  	}
   100  	req := newRequest("http://localhost:8080/?a=11&a=12&b=21&c=31&c=32&d=41&d=42&y=true", nil, nil, nil)
   101  	recv := new(Recv)
   102  	binder := binding.New(nil)
   103  	err := binder.BindAndValidate(recv, req, nil)
   104  	assert.NoError(t, err)
   105  	assert.Equal(t, []int{11, 12}, (**recv.X).A)
   106  	assert.Equal(t, int32(21), (**recv.X).B)
   107  	assert.Equal(t, &[]uint16{31, 32}, (**recv.X).C)
   108  	assert.Equal(t, float32(41), *(**recv.X).D)
   109  	assert.Equal(t, true, recv.Y)
   110  	assert.Equal(t, (*int64)(nil), recv.Z)
   111  }
   112  
   113  func TestHeaderString(t *testing.T) {
   114  	type Recv struct {
   115  		X **struct {
   116  			A []string  `header:"X-A"`
   117  			B string    `header:"X-B"`
   118  			C *[]string `header:"X-C,required"`
   119  			D *string   `header:"X-D"`
   120  		}
   121  		Y string  `header:"X-Y,required"`
   122  		Z *string `header:"X-Z"`
   123  	}
   124  	header := make(http.Header)
   125  	header.Add("X-A", "a1")
   126  	header.Add("X-A", "a2")
   127  	header.Add("X-B", "b1")
   128  	header.Add("X-C", "c1")
   129  	header.Add("X-C", "c2")
   130  	header.Add("X-D", "d1")
   131  	header.Add("X-D", "d2")
   132  	header.Add("X-Y", "y1")
   133  	req := newRequest("", header, nil, nil)
   134  	recv := new(Recv)
   135  	binder := binding.New(nil)
   136  	err := binder.BindAndValidate(recv, req, nil)
   137  	assert.NoError(t, err)
   138  	assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
   139  	assert.Equal(t, "b1", (**recv.X).B)
   140  	assert.Equal(t, []string{"c1", "c2"}, *(**recv.X).C)
   141  	assert.Equal(t, "d1", *(**recv.X).D)
   142  	assert.Equal(t, "y1", recv.Y)
   143  	assert.Equal(t, (*string)(nil), recv.Z)
   144  }
   145  
   146  func TestHeaderNum(t *testing.T) {
   147  	type Recv struct {
   148  		X **struct {
   149  			A []int     `header:"X-A"`
   150  			B int32     `header:"X-B"`
   151  			C *[]uint16 `header:"X-C,required"`
   152  			D *float32  `header:"X-D"`
   153  		}
   154  		Y bool   `header:"X-Y,required"`
   155  		Z *int64 `header:"X-Z"`
   156  	}
   157  	header := make(http.Header)
   158  	header.Add("X-A", "11")
   159  	header.Add("X-A", "12")
   160  	header.Add("X-B", "21")
   161  	header.Add("X-C", "31")
   162  	header.Add("X-C", "32")
   163  	header.Add("X-D", "41")
   164  	header.Add("X-D", "42")
   165  	header.Add("X-Y", "true")
   166  	req := newRequest("", header, nil, nil)
   167  	recv := new(Recv)
   168  	binder := binding.New(nil)
   169  	err := binder.BindAndValidate(recv, req, nil)
   170  	assert.NoError(t, err)
   171  	assert.Equal(t, []int{11, 12}, (**recv.X).A)
   172  	assert.Equal(t, int32(21), (**recv.X).B)
   173  	assert.Equal(t, &[]uint16{31, 32}, (**recv.X).C)
   174  	assert.Equal(t, float32(41), *(**recv.X).D)
   175  	assert.Equal(t, true, recv.Y)
   176  	assert.Equal(t, (*int64)(nil), recv.Z)
   177  }
   178  
   179  func TestCookieString(t *testing.T) {
   180  	type Recv struct {
   181  		X **struct {
   182  			A []string  `cookie:"a"`
   183  			B string    `cookie:"b"`
   184  			C *[]string `cookie:"c,required"`
   185  			D *string   `cookie:"d"`
   186  		}
   187  		Y string  `cookie:"y,required"`
   188  		Z *string `cookie:"z"`
   189  	}
   190  	cookies := []*http.Cookie{
   191  		{Name: "a", Value: "a1"},
   192  		{Name: "a", Value: "a2"},
   193  		{Name: "b", Value: "b1"},
   194  		{Name: "c", Value: "c1"},
   195  		{Name: "c", Value: "c2"},
   196  		{Name: "d", Value: "d1"},
   197  		{Name: "d", Value: "d2"},
   198  		{Name: "y", Value: "y1"},
   199  	}
   200  	req := newRequest("", nil, cookies, nil)
   201  	recv := new(Recv)
   202  	binder := binding.New(nil)
   203  	err := binder.BindAndValidate(recv, req, nil)
   204  	assert.NoError(t, err)
   205  	assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
   206  	assert.Equal(t, "b1", (**recv.X).B)
   207  	assert.Equal(t, []string{"c1", "c2"}, *(**recv.X).C)
   208  	assert.Equal(t, "d1", *(**recv.X).D)
   209  	assert.Equal(t, "y1", recv.Y)
   210  	assert.Equal(t, (*string)(nil), recv.Z)
   211  }
   212  
   213  func TestCookieNum(t *testing.T) {
   214  	type Recv struct {
   215  		X **struct {
   216  			A []int     `cookie:"a"`
   217  			B int32     `cookie:"b"`
   218  			C *[]uint16 `cookie:"c,required"`
   219  			D *float32  `cookie:"d"`
   220  		}
   221  		Y bool   `cookie:"y,required"`
   222  		Z *int64 `cookie:"z"`
   223  	}
   224  	cookies := []*http.Cookie{
   225  		{Name: "a", Value: "11"},
   226  		{Name: "a", Value: "12"},
   227  		{Name: "b", Value: "21"},
   228  		{Name: "c", Value: "31"},
   229  		{Name: "c", Value: "32"},
   230  		{Name: "d", Value: "41"},
   231  		{Name: "d", Value: "42"},
   232  		{Name: "y", Value: "t"},
   233  	}
   234  	req := newRequest("", nil, cookies, nil)
   235  	recv := new(Recv)
   236  	binder := binding.New(nil)
   237  	err := binder.BindAndValidate(recv, req, nil)
   238  	assert.NoError(t, err)
   239  	assert.Equal(t, []int{11, 12}, (**recv.X).A)
   240  	assert.Equal(t, int32(21), (**recv.X).B)
   241  	assert.Equal(t, &[]uint16{31, 32}, (**recv.X).C)
   242  	assert.Equal(t, float32(41), *(**recv.X).D)
   243  	assert.Equal(t, true, recv.Y)
   244  	assert.Equal(t, (*int64)(nil), recv.Z)
   245  }
   246  
   247  func TestFormString(t *testing.T) {
   248  	type Recv struct {
   249  		X **struct {
   250  			A []string  `form:"a"`
   251  			B string    `form:"b"`
   252  			C *[]string `form:"c,required"`
   253  			D *string   `form:"d"`
   254  		}
   255  		Y string  `form:"y,required"`
   256  		Z *string `form:"z"`
   257  	}
   258  	values := make(url.Values)
   259  	values.Add("a", "a1")
   260  	values.Add("a", "a2")
   261  	values.Add("b", "b1")
   262  	values.Add("c", "c1")
   263  	values.Add("c", "c2")
   264  	values.Add("d", "d1")
   265  	values.Add("d", "d2")
   266  	values.Add("y", "y1")
   267  	for _, f := range []httpbody.Files{nil, {
   268  		"f1": []httpbody.File{
   269  			httpbody.NewFile("txt", strings.NewReader("f11 text.")),
   270  		},
   271  	}} {
   272  		contentType, bodyReader := httpbody.NewFormBody2(values, f)
   273  		header := make(http.Header)
   274  		header.Set("Content-Type", contentType)
   275  		req := newRequest("", header, nil, bodyReader)
   276  		recv := new(Recv)
   277  		binder := binding.New(nil)
   278  		err := binder.BindAndValidate(recv, req, nil)
   279  		assert.NoError(t, err)
   280  		assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
   281  		assert.Equal(t, "b1", (**recv.X).B)
   282  		assert.Equal(t, []string{"c1", "c2"}, *(**recv.X).C)
   283  		assert.Equal(t, "d1", *(**recv.X).D)
   284  		assert.Equal(t, "y1", recv.Y)
   285  		assert.Equal(t, (*string)(nil), recv.Z)
   286  	}
   287  }
   288  
   289  func TestFormNum(t *testing.T) {
   290  	type Recv struct {
   291  		X **struct {
   292  			A []int     `form:"a"`
   293  			B int32     `form:"b"`
   294  			C *[]uint16 `form:"c,required"`
   295  			D *float32  `form:"d"`
   296  		}
   297  		Y bool   `form:"y,required"`
   298  		Z *int64 `form:"z"`
   299  	}
   300  	values := make(url.Values)
   301  	values.Add("a", "11")
   302  	values.Add("a", "12")
   303  	values.Add("b", "-21")
   304  	values.Add("c", "31")
   305  	values.Add("c", "32")
   306  	values.Add("d", "41")
   307  	values.Add("d", "42")
   308  	values.Add("y", "1")
   309  	for _, f := range []httpbody.Files{nil, {
   310  		"f1": []httpbody.File{
   311  			httpbody.NewFile("txt", strings.NewReader("f11 text.")),
   312  		},
   313  	}} {
   314  		contentType, bodyReader := httpbody.NewFormBody2(values, f)
   315  		header := make(http.Header)
   316  		header.Set("Content-Type", contentType)
   317  		req := newRequest("", header, nil, bodyReader)
   318  		recv := new(Recv)
   319  		binder := binding.New(nil)
   320  		err := binder.BindAndValidate(recv, req, nil)
   321  		assert.NoError(t, err)
   322  		assert.Equal(t, []int{11, 12}, (**recv.X).A)
   323  		assert.Equal(t, int32(-21), (**recv.X).B)
   324  		assert.Equal(t, &[]uint16{31, 32}, (**recv.X).C)
   325  		assert.Equal(t, float32(41), *(**recv.X).D)
   326  		assert.Equal(t, true, recv.Y)
   327  		assert.Equal(t, (*int64)(nil), recv.Z)
   328  	}
   329  }
   330  
   331  func TestJSON(t *testing.T) {
   332  	// binding.ResetJSONUnmarshaler(false, json.Unmarshal)
   333  	type metric string
   334  	type count int32
   335  	type ZS struct {
   336  		Z *int64
   337  	}
   338  	type Recv struct {
   339  		X **struct {
   340  			A []string          `json:"a"`
   341  			B int32             `json:""`
   342  			C *[]uint16         `json:",required"`
   343  			D *float32          `json:"d"`
   344  			E metric            `json:"e"`
   345  			F count             `json:"f"`
   346  			M map[string]string `json:"m"`
   347  		}
   348  		Y string `json:"y,required"`
   349  		ZS
   350  	}
   351  
   352  	bodyReader := strings.NewReader(`{
   353  		"X": {
   354  			"a": ["a1","a2"],
   355  			"B": 21,
   356  			"C": [31,32],
   357  			"d": 41,
   358  			"e": "qps",
   359  			"f": 100,
   360  			"m": {"a":"x"}
   361  		},
   362  		"Z": 6
   363  	}`)
   364  
   365  	header := make(http.Header)
   366  	header.Set("Content-Type", "application/json")
   367  	req := newRequest("", header, nil, bodyReader)
   368  	recv := new(Recv)
   369  	binder := binding.New(nil)
   370  	err := binder.BindAndValidate(recv, req, nil)
   371  	assert.Error(t, err)
   372  	assert.Equal(t, &binding.Error{ErrType: "binding", FailField: "y", Msg: "missing required parameter"}, err)
   373  	assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
   374  	assert.Equal(t, int32(21), (**recv.X).B)
   375  	assert.Equal(t, &[]uint16{31, 32}, (**recv.X).C)
   376  	assert.Equal(t, float32(41), *(**recv.X).D)
   377  	assert.Equal(t, metric("qps"), (**recv.X).E)
   378  	assert.Equal(t, count(100), (**recv.X).F)
   379  	assert.Equal(t, map[string]string{"a": "x"}, (**recv.X).M)
   380  	assert.Equal(t, "", recv.Y)
   381  	assert.Equal(t, (int64)(6), *recv.Z)
   382  }
   383  
   384  func TestNonstruct(t *testing.T) {
   385  	bodyReader := strings.NewReader(`{
   386  		"X": {
   387  			"a": ["a1","a2"],
   388  			"B": 21,
   389  			"C": [31,32],
   390  			"d": 41,
   391  			"e": "qps",
   392  			"f": 100
   393  		},
   394  		"Z": 6
   395  	}`)
   396  
   397  	header := make(http.Header)
   398  	header.Set("Content-Type", "application/json")
   399  	req := newRequest("", header, nil, bodyReader)
   400  	var recv interface{}
   401  	binder := binding.New(nil)
   402  	err := binder.BindAndValidate(&recv, req, nil)
   403  	assert.NoError(t, err)
   404  	b, err := json.Marshal(recv)
   405  	assert.NoError(t, err)
   406  	t.Logf("%s", b)
   407  
   408  	bodyReader = strings.NewReader("b=334ddddd&token=yoMba34uspjVQEbhflgTRe2ceeDFUK32&type=url_verification")
   409  	header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
   410  	req = newRequest("", header, nil, bodyReader)
   411  	recv = nil
   412  	err = binder.BindAndValidate(&recv, req, nil)
   413  	assert.NoError(t, err)
   414  	b, err = json.Marshal(recv)
   415  	assert.NoError(t, err)
   416  	t.Logf("%s", b)
   417  }
   418  
   419  func BenchmarkBindJSON(b *testing.B) {
   420  	type Recv struct {
   421  		X **struct {
   422  			A []string `json:"a"`
   423  			B int32
   424  			C *[]uint16
   425  			D *float32 `json:"d"`
   426  		}
   427  		Y string `json:"y"`
   428  	}
   429  	binder := binding.New(nil)
   430  	header := make(http.Header)
   431  	header.Set("Content-Type", "application/json")
   432  	test := func() {
   433  		bodyReader := strings.NewReader(`{
   434  		"X": {
   435  			"a": ["a1","a2"],
   436  			"B": 21,
   437  			"C": [31,32],
   438  			"d": 41
   439  		},
   440  		"y": "y1"
   441  	}`)
   442  		req := newRequest("", header, nil, bodyReader)
   443  		recv := new(Recv)
   444  		err := binder.Bind(recv, req, nil)
   445  		if err != nil {
   446  			b.Fatal(err)
   447  		}
   448  	}
   449  	test()
   450  
   451  	b.ReportAllocs()
   452  	b.ResetTimer()
   453  
   454  	for i := 0; i < b.N; i++ {
   455  		test()
   456  	}
   457  }
   458  
   459  func BenchmarkStdJSON(b *testing.B) {
   460  	type Recv struct {
   461  		X **struct {
   462  			A []string `json:"a"`
   463  			B int32
   464  			C *[]uint16
   465  			D *float32 `json:"d"`
   466  		}
   467  		Y string `json:"y"`
   468  	}
   469  	header := make(http.Header)
   470  	header.Set("Content-Type", "application/json")
   471  
   472  	b.ReportAllocs()
   473  	b.ResetTimer()
   474  
   475  	for i := 0; i < b.N; i++ {
   476  		bodyReader := strings.NewReader(`{
   477  			"X": {
   478  				"a": ["a1","a2"],
   479  				"B": 21,
   480  				"C": [31,32],
   481  				"d": 41
   482  			},
   483  			"y": "y1"
   484  		}`)
   485  
   486  		req := newRequest("", header, nil, bodyReader)
   487  		recv := new(Recv)
   488  		body, err := ioutil.ReadAll(req.Body)
   489  		req.Body.Close()
   490  		if err != nil {
   491  			b.Fatal(err)
   492  		}
   493  		err = json.Unmarshal(body, recv)
   494  		if err != nil {
   495  			b.Fatal(err)
   496  		}
   497  	}
   498  }
   499  
   500  type testPathParams struct{}
   501  
   502  func (testPathParams) Get(name string) (string, bool) {
   503  	switch name {
   504  	case "a":
   505  		return "a1", true
   506  	case "b":
   507  		return "-21", true
   508  	case "c":
   509  		return "31", true
   510  	case "d":
   511  		return "41", true
   512  	case "y":
   513  		return "y1", true
   514  	case "name":
   515  		return "henrylee2cn", true
   516  	default:
   517  		return "", false
   518  	}
   519  }
   520  
   521  func TestPath(t *testing.T) {
   522  	type Recv struct {
   523  		X **struct {
   524  			A []string  `path:"a"`
   525  			B int32     `path:"b"`
   526  			C *[]uint16 `path:"c,required"`
   527  			D *float32  `path:"d"`
   528  		}
   529  		Y string `path:"y,required"`
   530  		Z *int64
   531  	}
   532  
   533  	req := newRequest("", nil, nil, nil)
   534  	recv := new(Recv)
   535  	binder := binding.New(nil)
   536  	err := binder.BindAndValidate(recv, req, new(testPathParams))
   537  	assert.NoError(t, err)
   538  	assert.Equal(t, []string{"a1"}, (**recv.X).A)
   539  	assert.Equal(t, int32(-21), (**recv.X).B)
   540  	assert.Equal(t, &[]uint16{31}, (**recv.X).C)
   541  	assert.Equal(t, float32(41), *(**recv.X).D)
   542  	assert.Equal(t, "y1", recv.Y)
   543  	assert.Equal(t, (*int64)(nil), recv.Z)
   544  }
   545  
   546  type testPathParams2 struct{}
   547  
   548  func (testPathParams2) Get(name string) (string, bool) {
   549  	switch name {
   550  	case "e":
   551  		return "123", true
   552  	default:
   553  		return "", false
   554  	}
   555  }
   556  
   557  func TestDefault(t *testing.T) {
   558  	type S struct {
   559  		SS string `json:"ss"`
   560  	}
   561  
   562  	type Recv struct {
   563  		X **struct {
   564  			A          []string           `path:"a" json:"a"`
   565  			B          int32              `path:"b" default:"32"`
   566  			C          bool               `json:"c" default:"true"`
   567  			D          *float32           `default:"123.4"`
   568  			E          *[]string          `default:"['a','b','c','d,e,f']"`
   569  			F          map[string]string  `default:"{'a':'\"\\'1','\"b':'c','c':'2'}"`
   570  			G          map[string]int64   `default:"{'a':1,'b':2,'c':3}"`
   571  			H          map[string]float64 `default:"{'a':0.1,'b':1.2,'c':2.3}"`
   572  			I          map[string]float64 `default:"{'\"a\"':0.1,'b':1.2,'c':2.3}"`
   573  			Empty      string             `default:""`
   574  			Null       string             `default:""`
   575  			CommaSpace string             `default:",a:c "`
   576  			Dash       string             `default:"-"`
   577  			// InvalidInt int                `default:"abc"`
   578  			// InvalidMap map[string]string  `default:"abc"`
   579  		}
   580  		Y       string `json:"y" default:"y1"`
   581  		Z       int64
   582  		W       string                          `json:"w"`
   583  		V       []int64                         `json:"u" default:"[1,2,3]"`
   584  		U       []float32                       `json:"u" default:"[1.1,2,3]"`
   585  		T       *string                         `json:"t" default:"t1"`
   586  		S       S                               `default:"{'ss':'test'}"`
   587  		O       *S                              `default:"{'ss':'test2'}"`
   588  		Complex map[string][]map[string][]int64 `default:"{'a':[{'aa':[1,2,3], 'bb':[4,5]}],'b':[{}]}"`
   589  	}
   590  
   591  	bodyReader := strings.NewReader(`{
   592  		"X": {
   593  			"a": ["a1","a2"]
   594  		},
   595  		"Z": 6
   596  	}`)
   597  
   598  	// var nilMap map[string]string
   599  	header := make(http.Header)
   600  	header.Set("Content-Type", "application/json")
   601  	req := newRequest("", header, nil, bodyReader)
   602  	recv := new(Recv)
   603  	binder := binding.New(nil)
   604  	err := binder.BindAndValidate(recv, req, new(testPathParams2))
   605  	assert.NoError(t, err)
   606  	assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
   607  	assert.Equal(t, int32(32), (**recv.X).B)
   608  	assert.Equal(t, true, (**recv.X).C)
   609  	assert.Equal(t, float32(123.4), *(**recv.X).D)
   610  	assert.Equal(t, []string{"a", "b", "c", "d,e,f"}, *(**recv.X).E)
   611  	assert.Equal(t, map[string]string{"a": "\"'1", "\"b": "c", "c": "2"}, (**recv.X).F)
   612  	assert.Equal(t, map[string]int64{"a": 1, "b": 2, "c": 3}, (**recv.X).G)
   613  	assert.Equal(t, map[string]float64{"a": 0.1, "b": 1.2, "c": 2.3}, (**recv.X).H)
   614  	assert.Equal(t, map[string]float64{"\"a\"": 0.1, "b": 1.2, "c": 2.3}, (**recv.X).I)
   615  	assert.Equal(t, "", (**recv.X).Empty)
   616  	assert.Equal(t, "", (**recv.X).Null)
   617  	assert.Equal(t, ",a:c ", (**recv.X).CommaSpace)
   618  	assert.Equal(t, "-", (**recv.X).Dash)
   619  	// assert.Equal(t, 0, (**recv.X).InvalidInt)
   620  	// assert.Equal(t, nilMap, (**recv.X).InvalidMap)
   621  	assert.Equal(t, "y1", recv.Y)
   622  	assert.Equal(t, "t1", *recv.T)
   623  	assert.Equal(t, int64(6), recv.Z)
   624  	assert.Equal(t, []int64{1, 2, 3}, recv.V)
   625  	assert.Equal(t, []float32{1.1, 2, 3}, recv.U)
   626  	assert.Equal(t, S{SS: "test"}, recv.S)
   627  	assert.Equal(t, &S{SS: "test2"}, recv.O)
   628  	assert.Equal(t, map[string][]map[string][]int64{"a": {{"aa": {1, 2, 3}, "bb": []int64{4, 5}}}, "b": {map[string][]int64{}}}, recv.Complex)
   629  }
   630  
   631  func TestAuto(t *testing.T) {
   632  	type Recv struct {
   633  		A string `vd:"$!=''"`
   634  		B string
   635  		C string
   636  		D string `query:"D,required" form:"D,required"`
   637  		E string `cookie:"e" json:"e"`
   638  	}
   639  	query := make(url.Values)
   640  	query.Add("A", "a")
   641  	query.Add("B", "b")
   642  	query.Add("C", "c")
   643  	query.Add("D", "d-from-query")
   644  	contentType, bodyReader, err := httpbody.NewJSONBody(map[string]string{"e": "e-from-jsonbody"})
   645  	assert.NoError(t, err)
   646  	header := make(http.Header)
   647  	header.Set("Content-Type", contentType)
   648  	req := newRequest("http://localhost/?"+query.Encode(), header, []*http.Cookie{
   649  		{Name: "e", Value: "e-from-cookie"},
   650  	}, bodyReader)
   651  	recv := new(Recv)
   652  	binder := binding.New(nil)
   653  	err = binder.BindAndValidate(recv, req, nil)
   654  	assert.NoError(t, err)
   655  	assert.Equal(t, "a", recv.A)
   656  	assert.Equal(t, "b", recv.B)
   657  	assert.Equal(t, "c", recv.C)
   658  	assert.Equal(t, "d-from-query", recv.D)
   659  	assert.Equal(t, "e-from-cookie", recv.E)
   660  
   661  	query = make(url.Values)
   662  	query.Add("D", "d-from-query")
   663  	form := make(url.Values)
   664  	form.Add("B", "b")
   665  	form.Add("C", "c")
   666  	form.Add("D", "d-from-form")
   667  	contentType, bodyReader = httpbody.NewFormBody2(form, nil)
   668  	header = make(http.Header)
   669  	header.Set("Content-Type", contentType)
   670  	req = newRequest("http://localhost/?"+query.Encode(), header, nil, bodyReader)
   671  	recv = new(Recv)
   672  	err = binder.Bind(recv, req, nil)
   673  	assert.NoError(t, err)
   674  	assert.Equal(t, "", recv.A)
   675  	assert.Equal(t, "b", recv.B)
   676  	assert.Equal(t, "c", recv.C)
   677  	assert.Equal(t, "d-from-form", recv.D)
   678  	err = binder.Validate(recv)
   679  	assert.EqualError(t, err, "validating: expr_path=A, cause=invalid")
   680  }
   681  
   682  func TestTypeUnmarshal(t *testing.T) {
   683  	type Recv struct {
   684  		A time.Time   `form:"t1"`
   685  		B *time.Time  `query:"t2"`
   686  		C []time.Time `query:"t2"`
   687  	}
   688  	query := make(url.Values)
   689  	query.Add("t2", "2019-09-04T14:05:24+08:00")
   690  	query.Add("t2", "2019-09-04T18:05:24+08:00")
   691  	form := make(url.Values)
   692  	form.Add("t1", "2019-09-03T18:05:24+08:00")
   693  	contentType, bodyReader := httpbody.NewFormBody2(form, nil)
   694  	header := make(http.Header)
   695  	header.Set("Content-Type", contentType)
   696  	req := newRequest("http://localhost/?"+query.Encode(), header, nil, bodyReader)
   697  	recv := new(Recv)
   698  	binder := binding.New(nil)
   699  	err := binder.BindAndValidate(recv, req, nil)
   700  	assert.NoError(t, err)
   701  	t1, err := time.Parse(time.RFC3339, "2019-09-03T18:05:24+08:00")
   702  	assert.NoError(t, err)
   703  	assert.Equal(t, t1, recv.A)
   704  	t21, err := time.Parse(time.RFC3339, "2019-09-04T14:05:24+08:00")
   705  	assert.NoError(t, err)
   706  	assert.Equal(t, t21, *recv.B)
   707  	t22, err := time.Parse(time.RFC3339, "2019-09-04T18:05:24+08:00")
   708  	assert.NoError(t, err)
   709  	assert.Equal(t, []time.Time{t21, t22}, recv.C)
   710  	t.Logf("%v", recv)
   711  }
   712  
   713  func TestOption(t *testing.T) {
   714  	type Recv struct {
   715  		X *struct {
   716  			C int `json:"c,required"`
   717  			D int `json:"d"`
   718  		} `json:"X"`
   719  		Y string `json:"y"`
   720  	}
   721  	header := make(http.Header)
   722  	header.Set("Content-Type", "application/json")
   723  
   724  	bodyReader := strings.NewReader(`{
   725  			"X": {
   726  				"c": 21,
   727  				"d": 41
   728  			},
   729  			"y": "y1"
   730  		}`)
   731  	req := newRequest("", header, nil, bodyReader)
   732  	recv := new(Recv)
   733  	binder := binding.New(nil)
   734  	err := binder.BindAndValidate(recv, req, nil)
   735  	assert.NoError(t, err)
   736  	assert.Equal(t, 21, recv.X.C)
   737  	assert.Equal(t, 41, recv.X.D)
   738  	assert.Equal(t, "y1", recv.Y)
   739  
   740  	bodyReader = strings.NewReader(`{
   741  			"X": {
   742  			},
   743  			"y": "y1"
   744  		}`)
   745  	req = newRequest("", header, nil, bodyReader)
   746  	recv = new(Recv)
   747  	binder = binding.New(nil)
   748  	err = binder.BindAndValidate(recv, req, nil)
   749  	assert.EqualError(t, err, "binding: expr_path=X.c, cause=missing required parameter")
   750  	assert.Equal(t, 0, recv.X.C)
   751  	assert.Equal(t, 0, recv.X.D)
   752  	assert.Equal(t, "y1", recv.Y)
   753  
   754  	bodyReader = strings.NewReader(`{
   755  			"y": "y1"
   756  		}`)
   757  	req = newRequest("", header, nil, bodyReader)
   758  	recv = new(Recv)
   759  	binder = binding.New(nil)
   760  	err = binder.BindAndValidate(recv, req, nil)
   761  	assert.NoError(t, err)
   762  	assert.True(t, recv.X == nil)
   763  	assert.Equal(t, "y1", recv.Y)
   764  
   765  	type Recv2 struct {
   766  		X *struct {
   767  			C int `json:"c,required"`
   768  			D int `json:"d"`
   769  		} `json:"X,required"`
   770  		Y string `json:"y"`
   771  	}
   772  	bodyReader = strings.NewReader(`{
   773  			"y": "y1"
   774  		}`)
   775  	req = newRequest("", header, nil, bodyReader)
   776  	recv2 := new(Recv2)
   777  	binder = binding.New(nil)
   778  	err = binder.BindAndValidate(recv2, req, nil)
   779  	assert.EqualError(t, err, "binding: expr_path=X, cause=missing required parameter")
   780  	assert.True(t, recv2.X == nil)
   781  	assert.Equal(t, "y1", recv2.Y)
   782  }
   783  
   784  func newRequest(u string, header http.Header, cookies []*http.Cookie, bodyReader io.Reader) *http.Request {
   785  	if header == nil {
   786  		header = make(http.Header)
   787  	}
   788  	var method = "GET"
   789  	var body io.ReadCloser
   790  	if bodyReader != nil {
   791  		method = "POST"
   792  		body = ioutil.NopCloser(bodyReader)
   793  	}
   794  	if u == "" {
   795  		u = "http://localhost"
   796  	}
   797  	urlObj, _ := url.Parse(u)
   798  	req := &http.Request{
   799  		Method: method,
   800  		URL:    urlObj,
   801  		Body:   body,
   802  		Header: header,
   803  	}
   804  	for _, c := range cookies {
   805  		req.AddCookie(c)
   806  	}
   807  	return req
   808  }
   809  
   810  func TestQueryStringIssue(t *testing.T) {
   811  	type Timestamp struct {
   812  		time.Time
   813  	}
   814  	type Recv struct {
   815  		Name *string    `query:"name"`
   816  		T    *Timestamp `query:"t"`
   817  	}
   818  	req := newRequest("http://localhost:8080/?name=test", nil, nil, nil)
   819  	recv := new(Recv)
   820  	binder := binding.New(nil)
   821  	binder.SetLooseZeroMode(true)
   822  	err := binder.BindAndValidate(recv, req, nil)
   823  	assert.NoError(t, err)
   824  	assert.Equal(t, ameda.StringToStringPtr("test"), recv.Name)
   825  	assert.Equal(t, (*Timestamp)(nil), recv.T)
   826  }
   827  
   828  func TestQueryTypes(t *testing.T) {
   829  	type metric string
   830  	type count int32
   831  	type metrics []string
   832  	type filter struct {
   833  		Col1 string
   834  	}
   835  
   836  	type Recv struct {
   837  		A metric `vd:"$!=''"`
   838  		B count
   839  		C *count
   840  		D metrics `query:"D,required" form:"D,required"`
   841  		E metric  `cookie:"e" json:"e"`
   842  		F filter  `json:"f"`
   843  	}
   844  	query := make(url.Values)
   845  	query.Add("A", "qps")
   846  	query.Add("B", "123")
   847  	query.Add("C", "321")
   848  	query.Add("D", "dau")
   849  	query.Add("D", "dnu")
   850  	contentType, bodyReader, err := httpbody.NewJSONBody(
   851  		map[string]interface{}{
   852  			"e": "e-from-jsonbody",
   853  			"f": filter{Col1: "abc"},
   854  		},
   855  	)
   856  	assert.NoError(t, err)
   857  	header := make(http.Header)
   858  	header.Set("Content-Type", contentType)
   859  	req := newRequest("http://localhost/?"+query.Encode(), header, []*http.Cookie{
   860  		{Name: "e", Value: "e-from-cookie"},
   861  	}, bodyReader)
   862  	recv := new(Recv)
   863  	binder := binding.New(nil)
   864  	err = binder.BindAndValidate(recv, req, nil)
   865  	assert.NoError(t, err)
   866  	assert.Equal(t, metric("qps"), recv.A)
   867  	assert.Equal(t, count(123), recv.B)
   868  	assert.Equal(t, count(321), *recv.C)
   869  	assert.Equal(t, metrics{"dau", "dnu"}, recv.D)
   870  	assert.Equal(t, metric("e-from-cookie"), recv.E)
   871  	assert.Equal(t, filter{Col1: "abc"}, recv.F)
   872  }
   873  
   874  func TestNoTagIssue(t *testing.T) {
   875  	type x int
   876  	type T struct {
   877  		x
   878  		x2 x
   879  		a  int
   880  		B  int
   881  	}
   882  	req := newRequest("http://localhost:8080/?x=11&x2=12&a=1&B=2", nil, nil, nil)
   883  	recv := new(T)
   884  	binder := binding.New(nil)
   885  	binder.SetLooseZeroMode(true)
   886  	err := binder.BindAndValidate(recv, req, nil)
   887  	assert.NoError(t, err)
   888  	assert.Equal(t, x(0), recv.x)
   889  	assert.Equal(t, x(0), recv.x2)
   890  	assert.Equal(t, 0, recv.a)
   891  	assert.Equal(t, 2, recv.B)
   892  }