github.com/bytedance/go-tagexpr/v2@v2.9.8/binding/bind_test.go (about)

     1  package binding_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  	"io/ioutil"
     8  	"mime/multipart"
     9  	"net/http"
    10  	"net/url"
    11  	"reflect"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	// "github.com/bytedance/go-tagexpr/v2/binding/gjson"
    17  	"github.com/andeya/ameda"
    18  	"github.com/andeya/goutil/httpbody"
    19  	vd "github.com/bytedance/go-tagexpr/v2/validator"
    20  	"github.com/davecgh/go-spew/spew"
    21  	"github.com/stretchr/testify/assert"
    22  
    23  	"github.com/bytedance/go-tagexpr/v2/binding"
    24  )
    25  
    26  func init() {
    27  	// gjson.UseJSONUnmarshaler()
    28  }
    29  
    30  func TestRawBody(t *testing.T) {
    31  	type Recv struct {
    32  		S []byte   `raw_body:""`
    33  		F **string `raw_body:"" vd:"@:len($)<3; msg:'f too long'"`
    34  	}
    35  	bodyBytes := []byte("raw_body.............")
    36  	req := newRequest("", nil, nil, bytes.NewReader(bodyBytes))
    37  	recv := new(Recv)
    38  	binder := binding.New(nil)
    39  	err := binder.BindAndValidate(recv, req, nil)
    40  	assert.EqualError(t, err, "validating: expr_path=F, cause=f too long")
    41  	assert.Equal(t, bodyBytes, []byte(recv.S))
    42  	bodyCopied, err := binding.GetBody(req)
    43  	assert.NoError(t, err)
    44  	assert.Equal(t, bodyBytes, bodyCopied.Bytes())
    45  	t.Logf("%s", bodyCopied)
    46  }
    47  
    48  func TestQueryString(t *testing.T) {
    49  	type metric string
    50  	type count int32
    51  
    52  	type Recv struct {
    53  		X **struct {
    54  			A []string  `query:"a"`
    55  			B string    `query:"b"`
    56  			C *[]string `query:"c,required"`
    57  			D *string   `query:"d"`
    58  			E *[]***int `query:"e"`
    59  			F metric    `query:"f"`
    60  			G []count   `query:"g"`
    61  		}
    62  		Y string  `query:"y,required"`
    63  		Z *string `query:"z"`
    64  	}
    65  	req := newRequest("http://localhost:8080/?a=a1&a=a2&b=b1&c=c1&c=c2&d=d1&d=d&f=qps&g=1002&g=1003&e=&e=2&y=y1", nil, nil, nil)
    66  	recv := new(Recv)
    67  	binder := binding.New(nil)
    68  	err := binder.BindAndValidate(recv, req, nil)
    69  	assert.EqualError(t, err, "binding: expr_path=X.E, cause=parameter type does not match binding data")
    70  	binder.SetLooseZeroMode(true)
    71  	err = binder.BindAndValidate(recv, req, nil)
    72  	assert.NoError(t, err)
    73  	assert.Equal(t, 0, ***(*(**recv.X).E)[0])
    74  	assert.Equal(t, 2, ***(*(**recv.X).E)[1])
    75  	assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
    76  	assert.Equal(t, "b1", (**recv.X).B)
    77  	assert.Equal(t, []string{"c1", "c2"}, *(**recv.X).C)
    78  	assert.Equal(t, "d1", *(**recv.X).D)
    79  	assert.Equal(t, metric("qps"), (**recv.X).F)
    80  	assert.Equal(t, []count{1002, 1003}, (**recv.X).G)
    81  	assert.Equal(t, "y1", recv.Y)
    82  	assert.Equal(t, (*string)(nil), recv.Z)
    83  }
    84  
    85  func TestGetBody(t *testing.T) {
    86  	type Recv struct {
    87  		X **struct {
    88  			E string `json:"e,required" query:"e,required"`
    89  		}
    90  	}
    91  	req := newRequest("http://localhost:8080/", nil, nil, nil)
    92  	recv := new(Recv)
    93  	binder := binding.New(nil)
    94  	err := binder.BindAndValidate(recv, req, nil)
    95  	assert.EqualError(t, err, "binding: expr_path=X.e, cause=missing required parameter")
    96  }
    97  
    98  func TestQueryNum(t *testing.T) {
    99  	type Recv struct {
   100  		X **struct {
   101  			A []int     `query:"a"`
   102  			B int32     `query:"b"`
   103  			C *[]uint16 `query:"c,required"`
   104  			D *float32  `query:"d"`
   105  		}
   106  		Y bool   `query:"y,required"`
   107  		Z *int64 `query:"z"`
   108  	}
   109  	req := newRequest("http://localhost:8080/?a=11&a=12&b=21&c=31&c=32&d=41&d=42&y=true", nil, nil, nil)
   110  	recv := new(Recv)
   111  	binder := binding.New(nil)
   112  	err := binder.BindAndValidate(recv, req, nil)
   113  	assert.NoError(t, err)
   114  	assert.Equal(t, []int{11, 12}, (**recv.X).A)
   115  	assert.Equal(t, int32(21), (**recv.X).B)
   116  	assert.Equal(t, &[]uint16{31, 32}, (**recv.X).C)
   117  	assert.Equal(t, float32(41), *(**recv.X).D)
   118  	assert.Equal(t, true, recv.Y)
   119  	assert.Equal(t, (*int64)(nil), recv.Z)
   120  }
   121  
   122  func TestHeaderString(t *testing.T) {
   123  	type Recv struct {
   124  		X **struct {
   125  			A []string  `header:"X-A"`
   126  			B string    `header:"X-B"`
   127  			C *[]string `header:"X-C,required"`
   128  			D *string   `header:"X-D"`
   129  		}
   130  		Y string  `header:"X-Y,required"`
   131  		Z *string `header:"X-Z"`
   132  	}
   133  	header := make(http.Header)
   134  	header.Add("X-A", "a1")
   135  	header.Add("X-A", "a2")
   136  	header.Add("X-B", "b1")
   137  	header.Add("X-C", "c1")
   138  	header.Add("X-C", "c2")
   139  	header.Add("X-D", "d1")
   140  	header.Add("X-D", "d2")
   141  	header.Add("X-Y", "y1")
   142  	req := newRequest("", header, nil, nil)
   143  	recv := new(Recv)
   144  	binder := binding.New(nil)
   145  	err := binder.BindAndValidate(recv, req, nil)
   146  	assert.NoError(t, err)
   147  	assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
   148  	assert.Equal(t, "b1", (**recv.X).B)
   149  	assert.Equal(t, []string{"c1", "c2"}, *(**recv.X).C)
   150  	assert.Equal(t, "d1", *(**recv.X).D)
   151  	assert.Equal(t, "y1", recv.Y)
   152  	assert.Equal(t, (*string)(nil), recv.Z)
   153  }
   154  
   155  func TestHeaderNum(t *testing.T) {
   156  	type Recv struct {
   157  		X **struct {
   158  			A []int     `header:"X-A"`
   159  			B int32     `header:"X-B"`
   160  			C *[]uint16 `header:"X-C,required"`
   161  			D *float32  `header:"X-D"`
   162  		}
   163  		Y bool   `header:"X-Y,required"`
   164  		Z *int64 `header:"X-Z"`
   165  	}
   166  	header := make(http.Header)
   167  	header.Add("X-A", "11")
   168  	header.Add("X-A", "12")
   169  	header.Add("X-B", "21")
   170  	header.Add("X-C", "31")
   171  	header.Add("X-C", "32")
   172  	header.Add("X-D", "41")
   173  	header.Add("X-D", "42")
   174  	header.Add("X-Y", "true")
   175  	req := newRequest("", header, nil, nil)
   176  	recv := new(Recv)
   177  	binder := binding.New(nil)
   178  	err := binder.BindAndValidate(recv, req, nil)
   179  	assert.NoError(t, err)
   180  	assert.Equal(t, []int{11, 12}, (**recv.X).A)
   181  	assert.Equal(t, int32(21), (**recv.X).B)
   182  	assert.Equal(t, &[]uint16{31, 32}, (**recv.X).C)
   183  	assert.Equal(t, float32(41), *(**recv.X).D)
   184  	assert.Equal(t, true, recv.Y)
   185  	assert.Equal(t, (*int64)(nil), recv.Z)
   186  }
   187  
   188  func TestCookieString(t *testing.T) {
   189  	type Recv struct {
   190  		X **struct {
   191  			A []string  `cookie:"a"`
   192  			B string    `cookie:"b"`
   193  			C *[]string `cookie:"c,required"`
   194  			D *string   `cookie:"d"`
   195  		}
   196  		Y string  `cookie:"y,required"`
   197  		Z *string `cookie:"z"`
   198  	}
   199  	cookies := []*http.Cookie{
   200  		{Name: "a", Value: "a1"},
   201  		{Name: "a", Value: "a2"},
   202  		{Name: "b", Value: "b1"},
   203  		{Name: "c", Value: "c1"},
   204  		{Name: "c", Value: "c2"},
   205  		{Name: "d", Value: "d1"},
   206  		{Name: "d", Value: "d2"},
   207  		{Name: "y", Value: "y1"},
   208  	}
   209  	req := newRequest("", nil, cookies, nil)
   210  	recv := new(Recv)
   211  	binder := binding.New(nil)
   212  	err := binder.BindAndValidate(recv, req, nil)
   213  	assert.NoError(t, err)
   214  	assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
   215  	assert.Equal(t, "b1", (**recv.X).B)
   216  	assert.Equal(t, []string{"c1", "c2"}, *(**recv.X).C)
   217  	assert.Equal(t, "d1", *(**recv.X).D)
   218  	assert.Equal(t, "y1", recv.Y)
   219  	assert.Equal(t, (*string)(nil), recv.Z)
   220  }
   221  
   222  func TestCookieNum(t *testing.T) {
   223  	type Recv struct {
   224  		X **struct {
   225  			A []int     `cookie:"a"`
   226  			B int32     `cookie:"b"`
   227  			C *[]uint16 `cookie:"c,required"`
   228  			D *float32  `cookie:"d"`
   229  		}
   230  		Y bool   `cookie:"y,required"`
   231  		Z *int64 `cookie:"z"`
   232  	}
   233  	cookies := []*http.Cookie{
   234  		{Name: "a", Value: "11"},
   235  		{Name: "a", Value: "12"},
   236  		{Name: "b", Value: "21"},
   237  		{Name: "c", Value: "31"},
   238  		{Name: "c", Value: "32"},
   239  		{Name: "d", Value: "41"},
   240  		{Name: "d", Value: "42"},
   241  		{Name: "y", Value: "t"},
   242  	}
   243  	req := newRequest("", nil, cookies, nil)
   244  	recv := new(Recv)
   245  	binder := binding.New(nil)
   246  	err := binder.BindAndValidate(recv, req, nil)
   247  	assert.NoError(t, err)
   248  	assert.Equal(t, []int{11, 12}, (**recv.X).A)
   249  	assert.Equal(t, int32(21), (**recv.X).B)
   250  	assert.Equal(t, &[]uint16{31, 32}, (**recv.X).C)
   251  	assert.Equal(t, float32(41), *(**recv.X).D)
   252  	assert.Equal(t, true, recv.Y)
   253  	assert.Equal(t, (*int64)(nil), recv.Z)
   254  }
   255  
   256  func TestFormString(t *testing.T) {
   257  	type Recv struct {
   258  		X **struct {
   259  			A []string  `form:"a"`
   260  			B string    `form:"b"`
   261  			C *[]string `form:"c,required"`
   262  			D *string   `form:"d"`
   263  		}
   264  		Y   string                `form:"y,required"`
   265  		Z   *string               `form:"z"`
   266  		F   *multipart.FileHeader `form:"F1"`
   267  		F1  multipart.FileHeader
   268  		Fs  []multipart.FileHeader  `form:"F1"`
   269  		Fs1 []*multipart.FileHeader `form:"F1"`
   270  	}
   271  	values := make(url.Values)
   272  	values.Add("a", "a1")
   273  	values.Add("a", "a2")
   274  	values.Add("b", "b1")
   275  	values.Add("c", "c1")
   276  	values.Add("c", "c2")
   277  	values.Add("d", "d1")
   278  	values.Add("d", "d2")
   279  	values.Add("y", "y1")
   280  	for i, f := range []httpbody.Files{nil, {
   281  		"F1": []httpbody.File{
   282  			httpbody.NewFile("txt", strings.NewReader("0123")),
   283  		},
   284  	}} {
   285  		contentType, bodyReader := httpbody.NewFormBody2(values, f)
   286  		header := make(http.Header)
   287  		header.Set("Content-Type", contentType)
   288  		req := newRequest("", header, nil, bodyReader)
   289  		recv := new(Recv)
   290  		binder := binding.New(nil)
   291  		err := binder.BindAndValidate(recv, req, nil)
   292  		assert.NoError(t, err)
   293  		assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
   294  		assert.Equal(t, "b1", (**recv.X).B)
   295  		assert.Equal(t, []string{"c1", "c2"}, *(**recv.X).C)
   296  		assert.Equal(t, "d1", *(**recv.X).D)
   297  		assert.Equal(t, "y1", recv.Y)
   298  		assert.Equal(t, (*string)(nil), recv.Z)
   299  		t.Logf("[%d] F: %#v", i, recv.F)
   300  		t.Logf("[%d] F1: %#v", i, recv.F1)
   301  		t.Logf("[%d] Fs: %#v", i, recv.Fs)
   302  		t.Logf("[%d] Fs1: %#v", i, recv.Fs1)
   303  		if len(recv.Fs1) > 0 {
   304  			t.Logf("[%d] Fs1[0]: %#v", i, recv.Fs1[0])
   305  		}
   306  	}
   307  }
   308  
   309  func TestFormNum(t *testing.T) {
   310  	type Recv struct {
   311  		X **struct {
   312  			A []int     `form:"a"`
   313  			B int32     `form:"b"`
   314  			C *[]uint16 `form:"c,required"`
   315  			D *float32  `form:"d"`
   316  		}
   317  		Y bool   `form:"y,required"`
   318  		Z *int64 `form:"z"`
   319  	}
   320  	values := make(url.Values)
   321  	values.Add("a", "11")
   322  	values.Add("a", "12")
   323  	values.Add("b", "-21")
   324  	values.Add("c", "31")
   325  	values.Add("c", "32")
   326  	values.Add("d", "41")
   327  	values.Add("d", "42")
   328  	values.Add("y", "1")
   329  	for _, f := range []httpbody.Files{nil, {
   330  		"f1": []httpbody.File{
   331  			httpbody.NewFile("txt", strings.NewReader("f11 text.")),
   332  		},
   333  	}} {
   334  		contentType, bodyReader := httpbody.NewFormBody2(values, f)
   335  		header := make(http.Header)
   336  		header.Set("Content-Type", contentType)
   337  		req := newRequest("", header, nil, bodyReader)
   338  		recv := new(Recv)
   339  		binder := binding.New(nil)
   340  		err := binder.BindAndValidate(recv, req, nil)
   341  		assert.NoError(t, err)
   342  		assert.Equal(t, []int{11, 12}, (**recv.X).A)
   343  		assert.Equal(t, int32(-21), (**recv.X).B)
   344  		assert.Equal(t, &[]uint16{31, 32}, (**recv.X).C)
   345  		assert.Equal(t, float32(41), *(**recv.X).D)
   346  		assert.Equal(t, true, recv.Y)
   347  		assert.Equal(t, (*int64)(nil), recv.Z)
   348  	}
   349  }
   350  
   351  func TestJSON(t *testing.T) {
   352  	// binding.ResetJSONUnmarshaler(false, json.Unmarshal)
   353  	type metric string
   354  	type count int32
   355  	type ZS struct {
   356  		Z *int64
   357  	}
   358  	type Recv struct {
   359  		X **struct {
   360  			A []string          `json:"a"`
   361  			B int32             `json:""`
   362  			C *[]uint16         `json:",required"`
   363  			D *float32          `json:"d"`
   364  			E metric            `json:"e"`
   365  			F count             `json:"f"`
   366  			M map[string]string `json:"m"`
   367  		}
   368  		Y string `json:"y,required"`
   369  		ZS
   370  	}
   371  
   372  	bodyReader := strings.NewReader(`{
   373  		"X": {
   374  			"a": ["a1","a2"],
   375  			"B": 21,
   376  			"C": [31,32],
   377  			"d": 41,
   378  			"e": "qps",
   379  			"f": 100,
   380  			"m": {"a":"x"}
   381  		},
   382  		"Z": 6
   383  	}`)
   384  
   385  	header := make(http.Header)
   386  	header.Set("Content-Type", "application/json")
   387  	req := newRequest("", header, nil, bodyReader)
   388  	recv := new(Recv)
   389  	binder := binding.New(nil)
   390  	err := binder.BindAndValidate(recv, req, nil)
   391  	assert.Error(t, err)
   392  	assert.Equal(t, &binding.Error{ErrType: "binding", FailField: "y", Msg: "missing required parameter"}, err)
   393  	assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
   394  	assert.Equal(t, int32(21), (**recv.X).B)
   395  	assert.Equal(t, &[]uint16{31, 32}, (**recv.X).C)
   396  	assert.Equal(t, float32(41), *(**recv.X).D)
   397  	assert.Equal(t, metric("qps"), (**recv.X).E)
   398  	assert.Equal(t, count(100), (**recv.X).F)
   399  	assert.Equal(t, map[string]string{"a": "x"}, (**recv.X).M)
   400  	assert.Equal(t, "", recv.Y)
   401  	assert.Equal(t, (int64)(6), *recv.Z)
   402  }
   403  
   404  func TestNonstruct(t *testing.T) {
   405  	bodyReader := strings.NewReader(`{
   406  		"X": {
   407  			"a": ["a1","a2"],
   408  			"B": 21,
   409  			"C": [31,32],
   410  			"d": 41,
   411  			"e": "qps",
   412  			"f": 100
   413  		},
   414  		"Z": 6
   415  	}`)
   416  
   417  	header := make(http.Header)
   418  	header.Set("Content-Type", "application/json")
   419  	req := newRequest("", header, nil, bodyReader)
   420  	var recv interface{}
   421  	binder := binding.New(nil)
   422  	err := binder.BindAndValidate(&recv, req, nil)
   423  	assert.NoError(t, err)
   424  	b, err := json.Marshal(recv)
   425  	assert.NoError(t, err)
   426  	t.Logf("%s", b)
   427  
   428  	bodyReader = strings.NewReader("b=334ddddd&token=yoMba34uspjVQEbhflgTRe2ceeDFUK32&type=url_verification")
   429  	header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
   430  	req = newRequest("", header, nil, bodyReader)
   431  	recv = nil
   432  	err = binder.BindAndValidate(&recv, req, nil)
   433  	assert.NoError(t, err)
   434  	b, err = json.Marshal(recv)
   435  	assert.NoError(t, err)
   436  	t.Logf("%s", b)
   437  }
   438  
   439  func BenchmarkBindJSON(b *testing.B) {
   440  	type Recv struct {
   441  		X **struct {
   442  			A []string `json:"a"`
   443  			B int32
   444  			C *[]uint16
   445  			D *float32 `json:"d"`
   446  		}
   447  		Y string `json:"y"`
   448  	}
   449  	binder := binding.New(nil)
   450  	header := make(http.Header)
   451  	header.Set("Content-Type", "application/json")
   452  	test := func() {
   453  		bodyReader := strings.NewReader(`{
   454  		"X": {
   455  			"a": ["a1","a2"],
   456  			"B": 21,
   457  			"C": [31,32],
   458  			"d": 41
   459  		},
   460  		"y": "y1"
   461  	}`)
   462  		req := newRequest("", header, nil, bodyReader)
   463  		recv := new(Recv)
   464  		err := binder.Bind(recv, req, nil)
   465  		if err != nil {
   466  			b.Fatal(err)
   467  		}
   468  	}
   469  	test()
   470  
   471  	b.ReportAllocs()
   472  	b.ResetTimer()
   473  
   474  	for i := 0; i < b.N; i++ {
   475  		test()
   476  	}
   477  }
   478  
   479  func BenchmarkStdJSON(b *testing.B) {
   480  	type Recv struct {
   481  		X **struct {
   482  			A []string `json:"a"`
   483  			B int32
   484  			C *[]uint16
   485  			D *float32 `json:"d"`
   486  		}
   487  		Y string `json:"y"`
   488  	}
   489  	header := make(http.Header)
   490  	header.Set("Content-Type", "application/json")
   491  
   492  	b.ReportAllocs()
   493  	b.ResetTimer()
   494  
   495  	for i := 0; i < b.N; i++ {
   496  		bodyReader := strings.NewReader(`{
   497  			"X": {
   498  				"a": ["a1","a2"],
   499  				"B": 21,
   500  				"C": [31,32],
   501  				"d": 41
   502  			},
   503  			"y": "y1"
   504  		}`)
   505  
   506  		req := newRequest("", header, nil, bodyReader)
   507  		recv := new(Recv)
   508  		body, err := ioutil.ReadAll(req.Body)
   509  		req.Body.Close()
   510  		if err != nil {
   511  			b.Fatal(err)
   512  		}
   513  		err = json.Unmarshal(body, recv)
   514  		if err != nil {
   515  			b.Fatal(err)
   516  		}
   517  	}
   518  }
   519  
   520  type testPathParams struct{}
   521  
   522  func (testPathParams) Get(name string) (string, bool) {
   523  	switch name {
   524  	case "a":
   525  		return "a1", true
   526  	case "b":
   527  		return "-21", true
   528  	case "c":
   529  		return "31", true
   530  	case "d":
   531  		return "41", true
   532  	case "y":
   533  		return "y1", true
   534  	case "name":
   535  		return "henrylee2cn", true
   536  	default:
   537  		return "", false
   538  	}
   539  }
   540  
   541  func TestPath(t *testing.T) {
   542  	type Recv struct {
   543  		X **struct {
   544  			A []string  `path:"a"`
   545  			B int32     `path:"b"`
   546  			C *[]uint16 `path:"c,required"`
   547  			D *float32  `path:"d"`
   548  		}
   549  		Y string `path:"y,required"`
   550  		Z *int64
   551  	}
   552  
   553  	req := newRequest("", nil, nil, nil)
   554  	recv := new(Recv)
   555  	binder := binding.New(nil)
   556  	err := binder.BindAndValidate(recv, req, new(testPathParams))
   557  	assert.NoError(t, err)
   558  	assert.Equal(t, []string{"a1"}, (**recv.X).A)
   559  	assert.Equal(t, int32(-21), (**recv.X).B)
   560  	assert.Equal(t, &[]uint16{31}, (**recv.X).C)
   561  	assert.Equal(t, float32(41), *(**recv.X).D)
   562  	assert.Equal(t, "y1", recv.Y)
   563  	assert.Equal(t, (*int64)(nil), recv.Z)
   564  }
   565  
   566  type testPathParams2 struct{}
   567  
   568  func (testPathParams2) Get(name string) (string, bool) {
   569  	switch name {
   570  	case "e":
   571  		return "123", true
   572  	default:
   573  		return "", false
   574  	}
   575  }
   576  
   577  func TestDefault(t *testing.T) {
   578  	type S struct {
   579  		SS string `json:"ss"`
   580  	}
   581  
   582  	type Recv struct {
   583  		X **struct {
   584  			A          []string           `path:"a" json:"a"`
   585  			B          int32              `path:"b" default:"32"`
   586  			C          bool               `json:"c" default:"true"`
   587  			D          *float32           `default:"123.4"`
   588  			E          *[]string          `default:"['a','b','c','d,e,f']"`
   589  			F          map[string]string  `default:"{'a':'\"\\'1','\"b':'c','c':'2'}"`
   590  			G          map[string]int64   `default:"{'a':1,'b':2,'c':3}"`
   591  			H          map[string]float64 `default:"{'a':0.1,'b':1.2,'c':2.3}"`
   592  			I          map[string]float64 `default:"{'\"a\"':0.1,'b':1.2,'c':2.3}"`
   593  			Empty      string             `default:""`
   594  			Null       string             `default:""`
   595  			CommaSpace string             `default:",a:c "`
   596  			Dash       string             `default:"-"`
   597  			// InvalidInt int                `default:"abc"`
   598  			// InvalidMap map[string]string  `default:"abc"`
   599  		}
   600  		Y       string `json:"y" default:"y1"`
   601  		Z       int64
   602  		W       string                          `json:"w"`
   603  		V       []int64                         `json:"u" default:"[1,2,3]"`
   604  		U       []float32                       `json:"u" default:"[1.1,2,3]"`
   605  		T       *string                         `json:"t" default:"t1"`
   606  		S       S                               `default:"{'ss':'test'}"`
   607  		O       *S                              `default:"{'ss':'test2'}"`
   608  		Complex map[string][]map[string][]int64 `default:"{'a':[{'aa':[1,2,3], 'bb':[4,5]}],'b':[{}]}"`
   609  	}
   610  
   611  	bodyReader := strings.NewReader(`{
   612  		"X": {
   613  			"a": ["a1","a2"]
   614  		},
   615  		"Z": 6
   616  	}`)
   617  
   618  	// var nilMap map[string]string
   619  	header := make(http.Header)
   620  	header.Set("Content-Type", "application/json")
   621  	req := newRequest("", header, nil, bodyReader)
   622  	recv := new(Recv)
   623  	binder := binding.New(nil)
   624  	err := binder.BindAndValidate(recv, req, new(testPathParams2))
   625  	assert.NoError(t, err)
   626  	assert.Equal(t, []string{"a1", "a2"}, (**recv.X).A)
   627  	assert.Equal(t, int32(32), (**recv.X).B)
   628  	assert.Equal(t, true, (**recv.X).C)
   629  	assert.Equal(t, float32(123.4), *(**recv.X).D)
   630  	assert.Equal(t, []string{"a", "b", "c", "d,e,f"}, *(**recv.X).E)
   631  	assert.Equal(t, map[string]string{"a": "\"'1", "\"b": "c", "c": "2"}, (**recv.X).F)
   632  	assert.Equal(t, map[string]int64{"a": 1, "b": 2, "c": 3}, (**recv.X).G)
   633  	assert.Equal(t, map[string]float64{"a": 0.1, "b": 1.2, "c": 2.3}, (**recv.X).H)
   634  	assert.Equal(t, map[string]float64{"\"a\"": 0.1, "b": 1.2, "c": 2.3}, (**recv.X).I)
   635  	assert.Equal(t, "", (**recv.X).Empty)
   636  	assert.Equal(t, "", (**recv.X).Null)
   637  	assert.Equal(t, ",a:c ", (**recv.X).CommaSpace)
   638  	assert.Equal(t, "-", (**recv.X).Dash)
   639  	// assert.Equal(t, 0, (**recv.X).InvalidInt)
   640  	// assert.Equal(t, nilMap, (**recv.X).InvalidMap)
   641  	assert.Equal(t, "y1", recv.Y)
   642  	assert.Equal(t, "t1", *recv.T)
   643  	assert.Equal(t, int64(6), recv.Z)
   644  	assert.Equal(t, []int64{1, 2, 3}, recv.V)
   645  	assert.Equal(t, []float32{1.1, 2, 3}, recv.U)
   646  	assert.Equal(t, S{SS: "test"}, recv.S)
   647  	assert.Equal(t, &S{SS: "test2"}, recv.O)
   648  	assert.Equal(t, map[string][]map[string][]int64{"a": {{"aa": {1, 2, 3}, "bb": []int64{4, 5}}}, "b": {map[string][]int64{}}}, recv.Complex)
   649  }
   650  
   651  func TestAuto(t *testing.T) {
   652  	type Recv struct {
   653  		A string `vd:"$!=''"`
   654  		B string
   655  		C string
   656  		D string `query:"D,required" form:"D,required"`
   657  		E string `cookie:"e" json:"e"`
   658  	}
   659  	query := make(url.Values)
   660  	query.Add("A", "a")
   661  	query.Add("B", "b")
   662  	query.Add("C", "c")
   663  	query.Add("D", "d-from-query")
   664  	contentType, bodyReader, err := httpbody.NewJSONBody(map[string]string{"e": "e-from-jsonbody"})
   665  	assert.NoError(t, err)
   666  	header := make(http.Header)
   667  	header.Set("Content-Type", contentType)
   668  	req := newRequest("http://localhost/?"+query.Encode(), header, []*http.Cookie{
   669  		{Name: "e", Value: "e-from-cookie"},
   670  	}, bodyReader)
   671  	recv := new(Recv)
   672  	binder := binding.New(nil)
   673  	err = binder.BindAndValidate(recv, req, nil)
   674  	assert.NoError(t, err)
   675  	assert.Equal(t, "a", recv.A)
   676  	assert.Equal(t, "b", recv.B)
   677  	assert.Equal(t, "c", recv.C)
   678  	assert.Equal(t, "d-from-query", recv.D)
   679  	assert.Equal(t, "e-from-cookie", recv.E)
   680  
   681  	query = make(url.Values)
   682  	query.Add("D", "d-from-query")
   683  	form := make(url.Values)
   684  	form.Add("B", "b")
   685  	form.Add("C", "c")
   686  	form.Add("D", "d-from-form")
   687  	contentType, bodyReader = httpbody.NewFormBody2(form, nil)
   688  	header = make(http.Header)
   689  	header.Set("Content-Type", contentType)
   690  	req = newRequest("http://localhost/?"+query.Encode(), header, nil, bodyReader)
   691  	recv = new(Recv)
   692  	err = binder.Bind(recv, req, nil)
   693  	assert.NoError(t, err)
   694  	assert.Equal(t, "", recv.A)
   695  	assert.Equal(t, "b", recv.B)
   696  	assert.Equal(t, "c", recv.C)
   697  	assert.Equal(t, "d-from-form", recv.D)
   698  	err = binder.Validate(recv)
   699  	assert.EqualError(t, err, "validating: expr_path=A, cause=invalid")
   700  }
   701  
   702  func TestTypeUnmarshal(t *testing.T) {
   703  	type Recv struct {
   704  		A time.Time   `form:"t1"`
   705  		B *time.Time  `query:"t2"`
   706  		C []time.Time `query:"t2"`
   707  	}
   708  	query := make(url.Values)
   709  	query.Add("t2", "2019-09-04T14:05:24+08:00")
   710  	query.Add("t2", "2019-09-04T18:05:24+08:00")
   711  	form := make(url.Values)
   712  	form.Add("t1", "2019-09-03T18:05:24+08:00")
   713  	contentType, bodyReader := httpbody.NewFormBody2(form, nil)
   714  	header := make(http.Header)
   715  	header.Set("Content-Type", contentType)
   716  	req := newRequest("http://localhost/?"+query.Encode(), header, nil, bodyReader)
   717  	recv := new(Recv)
   718  	binder := binding.New(nil)
   719  	err := binder.BindAndValidate(recv, req, nil)
   720  	assert.NoError(t, err)
   721  	t1, err := time.Parse(time.RFC3339, "2019-09-03T18:05:24+08:00")
   722  	assert.NoError(t, err)
   723  	assert.Equal(t, t1, recv.A)
   724  	t21, err := time.Parse(time.RFC3339, "2019-09-04T14:05:24+08:00")
   725  	assert.NoError(t, err)
   726  	assert.Equal(t, t21, *recv.B)
   727  	t22, err := time.Parse(time.RFC3339, "2019-09-04T18:05:24+08:00")
   728  	assert.NoError(t, err)
   729  	assert.Equal(t, []time.Time{t21, t22}, recv.C)
   730  	t.Logf("%v", recv)
   731  }
   732  
   733  func TestOption(t *testing.T) {
   734  	type Recv struct {
   735  		X *struct {
   736  			C int `json:"c,required"`
   737  			D int `json:"d"`
   738  		} `json:"X"`
   739  		Y string `json:"y"`
   740  	}
   741  	header := make(http.Header)
   742  	header.Set("Content-Type", "application/json")
   743  
   744  	bodyReader := strings.NewReader(`{
   745  			"X": {
   746  				"c": 21,
   747  				"d": 41
   748  			},
   749  			"y": "y1"
   750  		}`)
   751  	req := newRequest("", header, nil, bodyReader)
   752  	recv := new(Recv)
   753  	binder := binding.New(nil)
   754  	err := binder.BindAndValidate(recv, req, nil)
   755  	assert.NoError(t, err)
   756  	assert.Equal(t, 21, recv.X.C)
   757  	assert.Equal(t, 41, recv.X.D)
   758  	assert.Equal(t, "y1", recv.Y)
   759  
   760  	bodyReader = strings.NewReader(`{
   761  			"X": {
   762  			},
   763  			"y": "y1"
   764  		}`)
   765  	req = newRequest("", header, nil, bodyReader)
   766  	recv = new(Recv)
   767  	binder = binding.New(nil)
   768  	err = binder.BindAndValidate(recv, req, nil)
   769  	assert.EqualError(t, err, "binding: expr_path=X.c, cause=missing required parameter")
   770  	assert.Equal(t, 0, recv.X.C)
   771  	assert.Equal(t, 0, recv.X.D)
   772  	assert.Equal(t, "y1", recv.Y)
   773  
   774  	bodyReader = strings.NewReader(`{
   775  			"y": "y1"
   776  		}`)
   777  	req = newRequest("", header, nil, bodyReader)
   778  	recv = new(Recv)
   779  	binder = binding.New(nil)
   780  	err = binder.BindAndValidate(recv, req, nil)
   781  	assert.NoError(t, err)
   782  	assert.True(t, recv.X == nil)
   783  	assert.Equal(t, "y1", recv.Y)
   784  
   785  	type Recv2 struct {
   786  		X *struct {
   787  			C int `json:"c,required"`
   788  			D int `json:"d"`
   789  		} `json:"X,required"`
   790  		Y string `json:"y"`
   791  	}
   792  	bodyReader = strings.NewReader(`{
   793  			"y": "y1"
   794  		}`)
   795  	req = newRequest("", header, nil, bodyReader)
   796  	recv2 := new(Recv2)
   797  	binder = binding.New(nil)
   798  	err = binder.BindAndValidate(recv2, req, nil)
   799  	assert.EqualError(t, err, "binding: expr_path=X, cause=missing required parameter")
   800  	assert.True(t, recv2.X == nil)
   801  	assert.Equal(t, "y1", recv2.Y)
   802  }
   803  
   804  func newRequest(u string, header http.Header, cookies []*http.Cookie, bodyReader io.Reader) *http.Request {
   805  	if header == nil {
   806  		header = make(http.Header)
   807  	}
   808  	var method = "GET"
   809  	var body io.ReadCloser
   810  	if bodyReader != nil {
   811  		method = "POST"
   812  		body = ioutil.NopCloser(bodyReader)
   813  	}
   814  	if u == "" {
   815  		u = "http://localhost"
   816  	}
   817  	urlObj, _ := url.Parse(u)
   818  	req := &http.Request{
   819  		Method: method,
   820  		URL:    urlObj,
   821  		Body:   body,
   822  		Header: header,
   823  	}
   824  	for _, c := range cookies {
   825  		req.AddCookie(c)
   826  	}
   827  	return req
   828  }
   829  
   830  func TestQueryStringIssue(t *testing.T) {
   831  	type Timestamp struct {
   832  		time.Time
   833  	}
   834  	type Recv struct {
   835  		Name *string    `query:"name"`
   836  		T    *Timestamp `query:"t"`
   837  	}
   838  	req := newRequest("http://localhost:8080/?name=test", nil, nil, nil)
   839  	recv := new(Recv)
   840  	binder := binding.New(nil)
   841  	binder.SetLooseZeroMode(true)
   842  	err := binder.BindAndValidate(recv, req, nil)
   843  	assert.NoError(t, err)
   844  	assert.Equal(t, ameda.StringToStringPtr("test"), recv.Name)
   845  	assert.Equal(t, (*Timestamp)(nil), recv.T)
   846  }
   847  
   848  func TestQueryTypes(t *testing.T) {
   849  	type metric string
   850  	type count int32
   851  	type metrics []string
   852  	type filter struct {
   853  		Col1 string
   854  	}
   855  
   856  	type Recv struct {
   857  		A metric `vd:"$!=''"`
   858  		B count
   859  		C *count
   860  		D metrics `query:"D,required" form:"D,required"`
   861  		E metric  `cookie:"e" json:"e"`
   862  		F filter  `json:"f"`
   863  	}
   864  	query := make(url.Values)
   865  	query.Add("A", "qps")
   866  	query.Add("B", "123")
   867  	query.Add("C", "321")
   868  	query.Add("D", "dau")
   869  	query.Add("D", "dnu")
   870  	contentType, bodyReader, err := httpbody.NewJSONBody(
   871  		map[string]interface{}{
   872  			"e": "e-from-jsonbody",
   873  			"f": filter{Col1: "abc"},
   874  		},
   875  	)
   876  	assert.NoError(t, err)
   877  	header := make(http.Header)
   878  	header.Set("Content-Type", contentType)
   879  	req := newRequest("http://localhost/?"+query.Encode(), header, []*http.Cookie{
   880  		{Name: "e", Value: "e-from-cookie"},
   881  	}, bodyReader)
   882  	recv := new(Recv)
   883  	binder := binding.New(nil)
   884  	err = binder.BindAndValidate(recv, req, nil)
   885  	assert.NoError(t, err)
   886  	assert.Equal(t, metric("qps"), recv.A)
   887  	assert.Equal(t, count(123), recv.B)
   888  	assert.Equal(t, count(321), *recv.C)
   889  	assert.Equal(t, metrics{"dau", "dnu"}, recv.D)
   890  	assert.Equal(t, metric("e-from-cookie"), recv.E)
   891  	assert.Equal(t, filter{Col1: "abc"}, recv.F)
   892  }
   893  
   894  func TestNoTagIssue(t *testing.T) {
   895  	type x int
   896  	type T struct {
   897  		x
   898  		x2 x
   899  		a  int
   900  		B  int
   901  	}
   902  	req := newRequest("http://localhost:8080/?x=11&x2=12&a=1&B=2", nil, nil, nil)
   903  	recv := new(T)
   904  	binder := binding.New(nil)
   905  	binder.SetLooseZeroMode(true)
   906  	err := binder.BindAndValidate(recv, req, nil)
   907  	assert.NoError(t, err)
   908  	assert.Equal(t, x(0), recv.x)
   909  	assert.Equal(t, x(0), recv.x2)
   910  	assert.Equal(t, 0, recv.a)
   911  	assert.Equal(t, 2, recv.B)
   912  }
   913  
   914  func TestRegTypeUnmarshal(t *testing.T) {
   915  	type Q struct {
   916  		A int
   917  		B string
   918  	}
   919  	type T struct {
   920  		Q  Q    `query:"q"`
   921  		Qs []*Q `query:"qs"`
   922  	}
   923  	var values = url.Values{}
   924  	b, err := json.Marshal(Q{A: 2, B: "y"})
   925  	assert.NoError(t, err)
   926  	values.Add("q", string(b))
   927  	bs, err := json.Marshal([]Q{{A: 1, B: "x"}, {A: 2, B: "y"}})
   928  	values.Add("qs", string(bs))
   929  	req := newRequest("http://localhost:8080/?"+values.Encode(), nil, nil, nil)
   930  	recv := new(T)
   931  	binder := binding.New(nil)
   932  	err = binder.BindAndValidate(recv, req, nil)
   933  	if assert.NoError(t, err) {
   934  		assert.Equal(t, 2, recv.Q.A)
   935  		assert.Equal(t, "y", recv.Q.B)
   936  		assert.Equal(t, 1, recv.Qs[0].A)
   937  		assert.Equal(t, "x", recv.Qs[0].B)
   938  		assert.Equal(t, 2, recv.Qs[1].A)
   939  		assert.Equal(t, "y", recv.Qs[1].B)
   940  	}
   941  }
   942  
   943  func TestPathnameBUG(t *testing.T) {
   944  	type Currency struct {
   945  		CurrencyName   *string `form:"currency_name,required" json:"currency_name,required" protobuf:"bytes,1,req,name=currency_name,json=currencyName" query:"currency_name,required"`
   946  		CurrencySymbol *string `form:"currency_symbol,required" json:"currency_symbol,required" protobuf:"bytes,2,req,name=currency_symbol,json=currencySymbol" query:"currency_symbol,required"`
   947  		SymbolPosition *int32  `form:"symbol_position,required" json:"symbol_position,required" protobuf:"varint,3,req,name=symbol_position,json=symbolPosition" query:"symbol_position,required"`
   948  		DecimalPlaces  *int32  `form:"decimal_places,required" json:"decimal_places,required" protobuf:"varint,4,req,name=decimal_places,json=decimalPlaces" query:"decimal_places,required"` // 56x56
   949  		DecimalSymbol  *string `form:"decimal_symbol,required" json:"decimal_symbol,required" protobuf:"bytes,5,req,name=decimal_symbol,json=decimalSymbol" query:"decimal_symbol,required"`
   950  		Separator      *string `form:"separator,required" json:"separator,required" protobuf:"bytes,6,req,name=separator" query:"separator,required"`
   951  		SeparatorIndex *string `form:"separator_index,required" json:"separator_index,required" protobuf:"bytes,7,req,name=separator_index,json=separatorIndex" query:"separator_index,required"`
   952  		Between        *string `form:"between,required" json:"between,required" protobuf:"bytes,8,req,name=between" query:"between,required"`
   953  		MinPrice       *string `form:"min_price" json:"min_price,omitempty" protobuf:"bytes,9,opt,name=min_price,json=minPrice" query:"min_price"`
   954  		MaxPrice       *string `form:"max_price" json:"max_price,omitempty" protobuf:"bytes,10,opt,name=max_price,json=maxPrice" query:"max_price"`
   955  	}
   956  
   957  	type CurrencyData struct {
   958  		Amount   *string   `form:"amount,required" json:"amount,required" protobuf:"bytes,1,req,name=amount" query:"amount,required"`
   959  		Currency *Currency `form:"currency,required" json:"currency,required" protobuf:"bytes,2,req,name=currency" query:"currency,required"`
   960  	}
   961  
   962  	type ExchangeCurrencyRequest struct {
   963  		PromotionRegion *string       `form:"promotion_region,required" json:"promotion_region,required" protobuf:"bytes,1,req,name=promotion_region,json=promotionRegion" query:"promotion_region,required"`
   964  		Currency        *CurrencyData `form:"currency,required" json:"currency,required" protobuf:"bytes,2,req,name=currency" query:"currency,required"`
   965  		Version         *int32        `json:"version,omitempty" path:"version" protobuf:"varint,100,opt,name=version"`
   966  	}
   967  
   968  	z := &ExchangeCurrencyRequest{}
   969  	v := ameda.InitSampleValue(reflect.TypeOf(z), 10).Interface().(*ExchangeCurrencyRequest)
   970  	b, err := json.MarshalIndent(v, "", "  ")
   971  	assert.NoError(t, err)
   972  	t.Log(string(b))
   973  	header := make(http.Header)
   974  	header.Set("Content-Type", "application/json;charset=utf-8")
   975  	req := newRequest("http://localhost", header, nil, bytes.NewReader(b))
   976  	recv := new(ExchangeCurrencyRequest)
   977  	binder := binding.New(nil)
   978  	err = binder.BindAndValidate(recv, req, nil)
   979  	assert.NoError(t, err)
   980  
   981  	assert.Equal(t, v, recv)
   982  }
   983  
   984  func TestPathnameBUG2(t *testing.T) {
   985  	type CurrencyData struct {
   986  		z      int
   987  		Amount *string `form:"amount,required" json:"amount,required" protobuf:"bytes,1,req,name=amount" query:"amount,required"`
   988  		Name   *string `form:"name,required" json:"name,required" protobuf:"bytes,2,req,name=name" query:"name,required"`
   989  		Symbol *string `form:"symbol" json:"symbol,omitempty" protobuf:"bytes,3,opt,name=symbol" query:"symbol"`
   990  	}
   991  	type TimeRange struct {
   992  		z         int
   993  		StartTime *int64 `form:"start_time,required" json:"start_time,required" protobuf:"varint,1,req,name=start_time,json=startTime" query:"start_time,required"`
   994  		EndTime   *int64 `form:"end_time,required" json:"end_time,required" protobuf:"varint,2,req,name=end_time,json=endTime" query:"end_time,required"`
   995  	}
   996  	type CreateFreeShippingRequest struct {
   997  		z                int
   998  		PromotionName    *string       `form:"promotion_name,required" json:"promotion_name,required" protobuf:"bytes,1,req,name=promotion_name,json=promotionName" query:"promotion_name,required"`
   999  		PromotionRegion  *string       `form:"promotion_region,required" json:"promotion_region,required" protobuf:"bytes,2,req,name=promotion_region,json=promotionRegion" query:"promotion_region,required"`
  1000  		TimeRange        *TimeRange    `form:"time_range,required" json:"time_range,required" protobuf:"bytes,3,req,name=time_range,json=timeRange" query:"time_range,required"`
  1001  		PromotionBudget  *CurrencyData `form:"promotion_budget,required" json:"promotion_budget,required" protobuf:"bytes,4,req,name=promotion_budget,json=promotionBudget" query:"promotion_budget,required"`
  1002  		Loaded_SellerIds []string      `form:"loaded_Seller_ids" json:"loaded_Seller_ids,omitempty" protobuf:"bytes,5,rep,name=loaded_Seller_ids,json=loadedSellerIds" query:"loaded_Seller_ids"`
  1003  		Version          *int32        `json:"version,omitempty" path:"version" protobuf:"varint,100,opt,name=version"`
  1004  	}
  1005  
  1006  	// z := &CreateFreeShippingRequest{}
  1007  	// v := ameda.InitSampleValue(reflect.TypeOf(z), 10).Interface().(*CreateFreeShippingRequest)
  1008  	// b, err := json.MarshalIndent(v, "", "  ")
  1009  	// assert.NoError(t, err)
  1010  	// t.Log(string(b))
  1011  	b := []byte(`{
  1012      "promotion_name": "mu",
  1013      "promotion_region": "ID",
  1014      "time_range": {
  1015          "start_time": 1616420139,
  1016          "end_time": 1616520139
  1017      },
  1018      "promotion_budget": {
  1019          "amount":"10000000",
  1020          "name":"USD",
  1021          "symbol":"$"
  1022      },
  1023      "loaded_Seller_ids": [
  1024          "7493989780026655762","11111","111212121"
  1025      ]
  1026  }`)
  1027  	var v = new(CreateFreeShippingRequest)
  1028  	err := json.Unmarshal(b, v)
  1029  	assert.NoError(t, err)
  1030  
  1031  	header := make(http.Header)
  1032  	header.Set("Content-Type", "application/json;charset=utf-8")
  1033  	req := newRequest("http://localhost", header, nil, bytes.NewReader(b))
  1034  	recv := new(CreateFreeShippingRequest)
  1035  	binder := binding.New(nil)
  1036  	err = binder.BindAndValidate(recv, req, nil)
  1037  	assert.NoError(t, err)
  1038  
  1039  	assert.Equal(t, v, recv)
  1040  }
  1041  
  1042  func TestRequiredBUG(t *testing.T) {
  1043  	type Currency struct {
  1044  		currencyName   *string `vd:"$=='x'" form:"currency_name,required" json:"currency_name,required" protobuf:"bytes,1,req,name=currency_name,json=currencyName" query:"currency_name,required"`
  1045  		CurrencySymbol *string `vd:"$=='x'" form:"currency_symbol,required" json:"currency_symbol,required" protobuf:"bytes,2,req,name=currency_symbol,json=currencySymbol" query:"currency_symbol,required"`
  1046  	}
  1047  
  1048  	type CurrencyData struct {
  1049  		Amount *string              `form:"amount,required" json:"amount,required" protobuf:"bytes,1,req,name=amount" query:"amount,required"`
  1050  		Slice  []*Currency          `form:"slice,required" json:"slice,required" protobuf:"bytes,2,req,name=slice" query:"slice,required"`
  1051  		Map    map[string]*Currency `form:"map,required" json:"map,required" protobuf:"bytes,2,req,name=map" query:"map,required"`
  1052  	}
  1053  
  1054  	type ExchangeCurrencyRequest struct {
  1055  		PromotionRegion *string       `form:"promotion_region,required" json:"promotion_region,required" protobuf:"bytes,1,req,name=promotion_region,json=promotionRegion" query:"promotion_region,required"`
  1056  		Currency        *CurrencyData `form:"currency,required" json:"currency,required" protobuf:"bytes,2,req,name=currency" query:"currency,required"`
  1057  	}
  1058  
  1059  	z := &ExchangeCurrencyRequest{}
  1060  	// v := ameda.InitSampleValue(reflect.TypeOf(z), 10).Interface().(*ExchangeCurrencyRequest)
  1061  	b := []byte(`{
  1062            "promotion_region": "?",
  1063            "currency": {
  1064              "amount": "?",
  1065              "slice": [
  1066                {
  1067                  "currency_symbol": "?"
  1068                }
  1069              ],
  1070              "map": {
  1071                "?": {
  1072                  "currency_name": "?"
  1073                }
  1074              }
  1075            }
  1076          }`)
  1077  	t.Log(string(b))
  1078  	json.Unmarshal(b, z)
  1079  	header := make(http.Header)
  1080  	header.Set("Content-Type", "application/json;charset=utf-8")
  1081  	req := newRequest("http://localhost", header, nil, bytes.NewReader(b))
  1082  	recv := new(ExchangeCurrencyRequest)
  1083  	binder := binding.New(nil)
  1084  	err := binder.BindAndValidate(recv, req, nil)
  1085  	assert.EqualError(t, err, "validating: expr_path=Currency.Slice[0].currencyName, cause=invalid")
  1086  	assert.Equal(t, z, recv)
  1087  }
  1088  
  1089  func TestIssue25(t *testing.T) {
  1090  	type Recv struct {
  1091  		A string
  1092  	}
  1093  	header := make(http.Header)
  1094  	header.Set("A", "from header")
  1095  	cookies := []*http.Cookie{
  1096  		{Name: "A", Value: "from cookie"},
  1097  	}
  1098  	req := newRequest("/1", header, cookies, nil)
  1099  	recv := new(Recv)
  1100  	binder := binding.New(nil)
  1101  	err := binder.BindAndValidate(recv, req, nil)
  1102  	assert.NoError(t, err)
  1103  	assert.Equal(t, "from cookie", recv.A)
  1104  
  1105  	header2 := make(http.Header)
  1106  	header2.Set("A", "from header")
  1107  	cookies2 := []*http.Cookie{}
  1108  	req2 := newRequest("/2", header2, cookies2, nil)
  1109  	recv2 := new(Recv)
  1110  	err2 := binder.BindAndValidate(recv2, req2, nil)
  1111  	assert.NoError(t, err2)
  1112  	assert.Equal(t, "from header", recv2.A)
  1113  }
  1114  
  1115  func TestIssue26(t *testing.T) {
  1116  	type Recv struct {
  1117  		Type            string `json:"type,required" vd:"($=='update_target_threshold' && (TargetThreshold)$!='-1') || ($=='update_status' && (Status)$!='-1')"`
  1118  		RuleName        string `json:"rule_name,required" vd:"regexp('^rule[0-9]+$')"`
  1119  		TargetThreshold string `json:"target_threshold" vd:"regexp('^-?[0-9]+(\\.[0-9]+)?$')"`
  1120  		Status          string `json:"status" vd:"$=='0' || $=='1'"`
  1121  		Operator        string `json:"operator,required" vd:"len($)>0"`
  1122  	}
  1123  
  1124  	b := []byte(`{
  1125      "status": "1",
  1126      "adv": "11520",
  1127      "target_deep_external_action": "39",
  1128      "package": "test.bytedance.com",
  1129      "previous_target_threshold": "0.6",
  1130      "deep_external_action": "675",
  1131      "rule_name": "rule2",
  1132      "deep_bid_type": "54",
  1133      "modify_time": "2021-08-24:14:35:20",
  1134      "aid": "111",
  1135      "operator": "yanghaoze",
  1136      "external_action": "76",
  1137      "target_threshold": "0.1",
  1138      "type": "update_status"
  1139  }`)
  1140  
  1141  	recv := new(Recv)
  1142  	err := json.Unmarshal(b, recv)
  1143  	assert.NoError(t, err)
  1144  	err = vd.Validate(&recv, true)
  1145  	assert.NoError(t, err)
  1146  	t.Log(spew.Sdump(recv))
  1147  
  1148  	header := make(http.Header)
  1149  	header.Set("Content-Type", "application/json")
  1150  	header.Set("A", "from header")
  1151  	cookies := []*http.Cookie{
  1152  		{Name: "A", Value: "from cookie"},
  1153  	}
  1154  
  1155  	req := newRequest("/1", header, cookies, bytes.NewReader(b))
  1156  	binder := binding.New(nil)
  1157  	recv2 := new(Recv)
  1158  	err = binder.BindAndValidate(&recv2, req, nil)
  1159  	assert.NoError(t, err)
  1160  	t.Log(spew.Sdump(recv2))
  1161  	assert.Equal(t, recv, recv2)
  1162  }
  1163  
  1164  func TestDefault2(t *testing.T) {
  1165  	type Recv struct {
  1166  		X **struct {
  1167  			Dash string `default:"xxxx"`
  1168  		}
  1169  	}
  1170  	bodyReader := strings.NewReader(`{
  1171  		"X": {
  1172  			"Dash": "hello Dash"
  1173  		}
  1174  	}`)
  1175  	header := make(http.Header)
  1176  	header.Set("Content-Type", "application/json")
  1177  	req := newRequest("", header, nil, bodyReader)
  1178  	recv := new(Recv)
  1179  	binder := binding.New(nil)
  1180  	err := binder.BindAndValidate(recv, req, new(testPathParams2))
  1181  	assert.NoError(t, err)
  1182  	assert.Equal(t, "hello Dash", (**recv.X).Dash)
  1183  }
  1184  
  1185  func TestVdTagRecursion(t *testing.T) {
  1186  	type Node struct {
  1187  		N1 *Node
  1188  		N2 *Node
  1189  		N3 *Node
  1190  	}
  1191  	recv := &Node{}
  1192  	req, _ := http.NewRequest("get", "http://localhost/", bytes.NewReader([]byte{}))
  1193  	start := time.Now()
  1194  	binder := binding.New(nil)
  1195  	err := binder.BindAndValidate(recv, req, new(testPathParams2))
  1196  	assert.NoError(t, err)
  1197  	assert.Less(t, int64(time.Since(start)), int64(time.Second))
  1198  }