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 }