github.com/sandwich-go/boost@v1.3.29/validator/validator_test.go (about) 1 package validator 2 3 import ( 4 "bytes" 5 "context" 6 "database/sql" 7 "database/sql/driver" 8 "fmt" 9 "github.com/go-playground/locales/en" 10 "github.com/go-playground/locales/fr" 11 "github.com/go-playground/locales/nl" 12 ut "github.com/go-playground/universal-translator" 13 . "github.com/smartystreets/goconvey/convey" 14 "reflect" 15 "strings" 16 "testing" 17 "time" 18 ) 19 20 type StructLevelInvalidErr struct { 21 Value string 22 } 23 24 type SubTest struct { 25 Test string `validate:"required"` 26 } 27 28 type TestStruct struct { 29 String string `validate:"required" json:"StringVal"` 30 } 31 32 type TestPartial struct { 33 NoTag string 34 BlankTag string `validate:""` 35 Required string `validate:"required"` 36 SubSlice []*SubTest `validate:"required,dive"` 37 Sub *SubTest 38 SubIgnore *SubTest `validate:"-"` 39 Anonymous struct { 40 A string `validate:"required"` 41 ASubSlice []*SubTest `validate:"required,dive"` 42 43 SubAnonStruct []struct { 44 Test string `validate:"required"` 45 OtherTest string `validate:"required"` 46 } `validate:"required,dive"` 47 } 48 } 49 50 func StructLevelInvalidError(_ context.Context, sl StructLevel) { 51 top := sl.Top().Interface().(StructLevelInvalidErr) 52 s := sl.Current().Interface().(StructLevelInvalidErr) 53 54 if top.Value == s.Value { 55 sl.ReportError(nil, "Value", "Value", "required", "") 56 } 57 } 58 59 type valuer struct { 60 Name string 61 } 62 63 func (v valuer) Value() (driver.Value, error) { 64 if v.Name == "errorme" { 65 panic("SQL Driver Valuer error: some kind of error") 66 // return nil, errors.New("some kind of error") 67 } 68 69 if len(v.Name) == 0 { 70 return nil, nil 71 } 72 73 return v.Name, nil 74 } 75 76 func ValidateValuerType(field reflect.Value) interface{} { 77 if valuer, ok := field.Interface().(driver.Valuer); ok { 78 79 val, err := valuer.Value() 80 if err != nil { 81 // handle the error how you want 82 return nil 83 } 84 85 return val 86 } 87 88 return nil 89 } 90 91 type MadeUpCustomType struct { 92 FirstName string 93 LastName string 94 } 95 96 func ValidateCustomType(field reflect.Value) interface{} { 97 if cust, ok := field.Interface().(MadeUpCustomType); ok { 98 99 if len(cust.FirstName) == 0 || len(cust.LastName) == 0 { 100 return "" 101 } 102 103 return cust.FirstName + " " + cust.LastName 104 } 105 106 return "" 107 } 108 109 type CustomMadeUpStruct struct { 110 MadeUp MadeUpCustomType `validate:"required"` 111 OverriddenInt int `validate:"gt=1"` 112 } 113 114 func OverrideIntTypeForSomeReason(field reflect.Value) interface{} { 115 if i, ok := field.Interface().(int); ok { 116 if i == 1 { 117 return "1" 118 } 119 120 if i == 2 { 121 return "12" 122 } 123 } 124 125 return "" 126 } 127 128 func TestValidator(t *testing.T) { 129 Convey(`test validator.SetTagName`, t, func() { 130 Default.SetTagName("val") 131 type Test struct { 132 Name string `val:"len=4"` 133 } 134 s := &Test{ 135 Name: "TEST", 136 } 137 So(Default.Struct(context.Background(), s), ShouldBeNil) 138 s.Name = "" 139 err := Default.Struct(context.Background(), s) 140 So(err, ShouldNotBeNil) 141 So(err.Error(), ShouldEqual, "Key: 'Test.Name' Error:Field validation for 'Name' failed on the 'len' tag") 142 }) 143 144 Convey(`test validator.ValidateMap`, t, func() { 145 type args struct { 146 data map[string]interface{} 147 rules map[string]interface{} 148 } 149 tests := []struct { 150 name string 151 args args 152 want int 153 }{ 154 { 155 name: "test nested map in slice", 156 args: args{ 157 data: map[string]interface{}{ 158 "Test_A": map[string]interface{}{ 159 "Test_B": "Test_B", 160 "Test_C": []map[string]interface{}{ 161 { 162 "Test_D": "Test_D", 163 }, 164 }, 165 "Test_E": map[string]interface{}{ 166 "Test_F": "Test_F", 167 }, 168 }, 169 }, 170 rules: map[string]interface{}{ 171 "Test_A": map[string]interface{}{ 172 "Test_B": "min=2", 173 "Test_C": map[string]interface{}{ 174 "Test_D": "min=2", 175 }, 176 "Test_E": map[string]interface{}{ 177 "Test_F": "min=2", 178 }, 179 }, 180 }, 181 }, 182 want: 0, 183 }, 184 185 { 186 name: "test nested map error", 187 args: args{ 188 data: map[string]interface{}{ 189 "Test_A": map[string]interface{}{ 190 "Test_B": "Test_B", 191 "Test_C": []interface{}{"Test_D"}, 192 "Test_E": map[string]interface{}{ 193 "Test_F": "Test_F", 194 }, 195 "Test_G": "Test_G", 196 "Test_I": []map[string]interface{}{ 197 { 198 "Test_J": "Test_J", 199 }, 200 }, 201 }, 202 }, 203 rules: map[string]interface{}{ 204 "Test_A": map[string]interface{}{ 205 "Test_B": "min=2", 206 "Test_C": map[string]interface{}{ 207 "Test_D": "min=2", 208 }, 209 "Test_E": map[string]interface{}{ 210 "Test_F": "min=100", 211 }, 212 "Test_G": map[string]interface{}{ 213 "Test_H": "min=2", 214 }, 215 "Test_I": map[string]interface{}{ 216 "Test_J": "min=100", 217 }, 218 }, 219 }, 220 }, 221 want: 1, 222 }, 223 } 224 for _, tt := range tests { 225 got := Validate.ValidateMap(context.Background(), tt.args.data, tt.args.rules) 226 So(len(got), ShouldEqual, tt.want) 227 } 228 }) 229 230 Convey(`test validator.RegisterTagNameFunc`, t, func() { 231 v := New() 232 v.RegisterTagNameFunc(func(fld reflect.StructField) string { 233 name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] 234 235 if name == "-" { 236 return "" 237 } 238 239 return name 240 }) 241 242 type Test struct { 243 A interface{} 244 } 245 246 tst := &Test{ 247 A: struct { 248 A string `validate:"required"` 249 }{ 250 A: "", 251 }, 252 } 253 254 err := v.Struct(context.Background(), tst) 255 So(err, ShouldNotBeNil) 256 257 errs := err.(ValidationErrors) 258 259 So(len(errs), ShouldEqual, 1) 260 So(errs.Error(), ShouldEqual, "Key: 'Test.A.A' Error:Field validation for 'A' failed on the 'required' tag") 261 262 tst = &Test{ 263 A: struct { 264 A string `validate:"omitempty,required"` 265 }{ 266 A: "", 267 }, 268 } 269 270 err = v.Struct(context.Background(), tst) 271 So(err, ShouldBeNil) 272 }) 273 274 Convey(`test validator.RegisterAlias`, t, func() { 275 v := New() 276 v.RegisterAlias("iscoloralias", "hexcolor|rgb|rgba|hsl|hsla") 277 278 s := "rgb(255,255,255)" 279 errs := v.Var(context.Background(), s, "iscoloralias") 280 So(errs, ShouldBeNil) 281 282 s = "" 283 errs = v.Var(context.Background(), s, "omitempty,iscoloralias") 284 So(errs, ShouldBeNil) 285 286 s = "rgb(255,255,0)" 287 errs = v.Var(context.Background(), s, "iscoloralias,len=5") 288 So(errs, ShouldNotBeNil) 289 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'len' tag") 290 291 type Test struct { 292 Color string `validate:"iscoloralias"` 293 } 294 295 tst := &Test{ 296 Color: "#000", 297 } 298 299 errs = v.Struct(context.Background(), tst) 300 So(errs, ShouldBeNil) 301 302 tst.Color = "cfvre" 303 errs = v.Struct(context.Background(), tst) 304 So(errs, ShouldNotBeNil) 305 So(errs.Error(), ShouldEqual, "Key: 'Test.Color' Error:Field validation for 'Color' failed on the 'iscoloralias' tag") 306 307 v.RegisterAlias("req", "required,dive,iscoloralias") 308 arr := []string{"val1", "#fff", "#000"} 309 310 errs = v.Var(context.Background(), arr, "req") 311 So(errs, ShouldNotBeNil) 312 So(errs.Error(), ShouldEqual, "Key: '[0]' Error:Field validation for '[0]' failed on the 'iscoloralias' tag") 313 314 So(func() { v.RegisterAlias("exists!", "gt=5,lt=10") }, ShouldPanic) 315 }) 316 317 Convey(`test validator.RegisterValidation`, t, func() { 318 var tag string 319 320 type Test struct { 321 String string `validate:"mytag"` 322 } 323 324 val := New() 325 _ = val.RegisterValidation("mytag", func(ctx context.Context, fl FieldLevel) bool { 326 tag = fl.GetTag() 327 return true 328 }) 329 330 var test Test 331 errs := val.Struct(context.Background(), test) 332 So(errs, ShouldBeNil) 333 So(tag, ShouldEqual, "mytag") 334 }) 335 336 Convey(`test validator.RegisterStructValidation`, t, func() { 337 v := New() 338 v.RegisterStructValidation(StructLevelInvalidError, StructLevelInvalidErr{}) 339 340 var test StructLevelInvalidErr 341 342 err := v.Struct(context.Background(), test) 343 So(err, ShouldNotBeNil) 344 345 errs, ok := err.(ValidationErrors) 346 So(ok, ShouldBeTrue) 347 348 fe := errs[0] 349 So(fe.Field(), ShouldEqual, "Value") 350 So(fe.StructField(), ShouldEqual, "Value") 351 So(fe.Namespace(), ShouldEqual, "StructLevelInvalidErr.Value") 352 So(fe.StructNamespace(), ShouldEqual, "StructLevelInvalidErr.Value") 353 So(fe.Tag(), ShouldEqual, "required") 354 So(fe.ActualTag(), ShouldEqual, "required") 355 So(fe.Kind(), ShouldEqual, reflect.Invalid) 356 So(fe.Type(), ShouldEqual, reflect.TypeOf(nil)) 357 }) 358 359 Convey(`test validator.RegisterStructValidationMapRules`, t, func() { 360 type Data struct { 361 Name string 362 Age uint32 363 } 364 365 data := Data{ 366 Name: "leo", 367 Age: 1000, 368 } 369 370 rules := map[string]string{ 371 "Name": "min=4,max=6", 372 "Age": "min=4,max=6", 373 } 374 375 v := New() 376 v.RegisterStructValidationMapRules(rules, Data{}) 377 378 err := v.Struct(context.Background(), data) 379 errs, ok := err.(ValidationErrors) 380 So(ok, ShouldBeTrue) 381 So(len(errs), ShouldEqual, 2) 382 So(errs[0].Error(), ShouldEqual, "Key: 'Data.Name' Error:Field validation for 'Name' failed on the 'min' tag") 383 So(errs[1].Error(), ShouldEqual, "Key: 'Data.Age' Error:Field validation for 'Age' failed on the 'max' tag") 384 }) 385 386 Convey(`test validator.RegisterCustomTypeFunc`, t, func() { 387 v := New() 388 v.RegisterCustomTypeFunc(ValidateValuerType, valuer{}, (*driver.Valuer)(nil), sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) 389 v.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{}) 390 v.RegisterCustomTypeFunc(OverrideIntTypeForSomeReason, 1) 391 392 val := valuer{ 393 Name: "", 394 } 395 396 errs := v.Var(context.Background(), val, "required") 397 So(errs, ShouldNotBeNil) 398 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'required' tag") 399 400 val.Name = "Valid Name" 401 errs = v.Var(context.Background(), val, "required") 402 So(errs, ShouldBeNil) 403 404 val.Name = "errorme" 405 errs = v.Var(context.Background(), val, "required") 406 So(errs, ShouldNotBeNil) 407 So(errs.Error(), ShouldEqual, "validate panic with val: {errorme}, tag: required, reason: SQL Driver Valuer error: some kind of error") 408 409 myVal := valuer{ 410 Name: "", 411 } 412 413 errs = v.Var(context.Background(), myVal, "required") 414 So(errs, ShouldNotBeNil) 415 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'required' tag") 416 417 cust := MadeUpCustomType{ 418 FirstName: "Joey", 419 LastName: "Bloggs", 420 } 421 422 c := CustomMadeUpStruct{MadeUp: cust, OverriddenInt: 2} 423 424 errs = v.Struct(context.Background(), c) 425 So(errs, ShouldBeNil) 426 427 c.MadeUp.FirstName = "" 428 c.OverriddenInt = 1 429 430 err := v.Struct(context.Background(), c) 431 So(err, ShouldNotBeNil) 432 errs0, ok := err.(ValidationErrors) 433 So(ok, ShouldBeTrue) 434 So(len(errs0), ShouldEqual, 2) 435 So(errs0[0].Error(), ShouldEqual, "Key: 'CustomMadeUpStruct.MadeUp' Error:Field validation for 'MadeUp' failed on the 'required' tag") 436 So(errs0[1].Error(), ShouldEqual, "Key: 'CustomMadeUpStruct.OverriddenInt' Error:Field validation for 'OverriddenInt' failed on the 'gt' tag") 437 }) 438 439 Convey(`test validator.RegisterTranslation`, t, func() { 440 en0 := en.New() 441 uni := ut.New(en0, en0, fr.New()) 442 443 trans, _ := uni.GetTranslator("en") 444 fr0, _ := uni.GetTranslator("fr") 445 446 v := New() 447 err := v.RegisterTranslation("required", trans, 448 func(ut ut.Translator) (err error) { 449 // using this stype because multiple translation may have to be added for the full translation 450 if err = ut.Add("required", "{0} is a required field", false); err != nil { 451 return 452 } 453 454 return 455 }, func(ut ut.Translator, fe FieldError) string { 456 t0, err := ut.T(fe.Tag(), fe.Field()) 457 if err != nil { 458 fmt.Printf("warning: error translating FieldError: %#v", fe) 459 return fe.Error() 460 } 461 462 return t0 463 }) 464 So(err, ShouldBeNil) 465 466 err = v.RegisterTranslation("required", fr0, 467 func(ut ut.Translator) (err error) { 468 // using this stype because multiple translation may have to be added for the full translation 469 if err = ut.Add("required", "{0} est un champ obligatoire", false); err != nil { 470 return 471 } 472 473 return 474 }, func(ut ut.Translator, fe FieldError) string { 475 t0, transErr := ut.T(fe.Tag(), fe.Field()) 476 if transErr != nil { 477 fmt.Printf("warning: error translating FieldError: %#v", fe) 478 return fe.Error() 479 } 480 481 return t0 482 }) 483 484 So(err, ShouldBeNil) 485 486 type Test struct { 487 Value interface{} `validate:"required"` 488 } 489 490 var test Test 491 492 err = v.Struct(context.Background(), test) 493 So(err, ShouldNotBeNil) 494 495 errs := err.(ValidationErrors) 496 So(len(errs), ShouldEqual, 1) 497 498 fe := errs[0] 499 So(fe.Tag(), ShouldEqual, "required") 500 So(fe.Namespace(), ShouldEqual, "Test.Value") 501 So(fe.Translate(trans), ShouldEqual, fmt.Sprintf("%s is a required field", fe.Field())) 502 So(fe.Translate(fr0), ShouldEqual, fmt.Sprintf("%s est un champ obligatoire", fe.Field())) 503 504 nl0 := nl.New() 505 uni2 := ut.New(nl0, nl0) 506 trans2, _ := uni2.GetTranslator("nl") 507 So(fe.Translate(trans2), ShouldEqual, "Key: 'Test.Value' Error:Field validation for 'Value' failed on the 'required' tag") 508 509 terrs := errs.Translate(trans) 510 So(len(terrs), ShouldEqual, 1) 511 512 v0, ok := terrs["Test.Value"] 513 So(ok, ShouldBeTrue) 514 So(v0, ShouldEqual, fmt.Sprintf("%s is a required field", fe.Field())) 515 516 terrs = errs.Translate(fr0) 517 So(len(terrs), ShouldEqual, 1) 518 519 v0, ok = terrs["Test.Value"] 520 So(ok, ShouldBeTrue) 521 So(v0, ShouldEqual, fmt.Sprintf("%s est un champ obligatoire", fe.Field())) 522 523 type Test2 struct { 524 Value string `validate:"gt=1"` 525 } 526 527 var t2 Test2 528 529 err = v.Struct(context.Background(), t2) 530 So(err, ShouldNotBeNil) 531 532 errs = err.(ValidationErrors) 533 So(len(errs), ShouldEqual, 1) 534 535 fe = errs[0] 536 So(fe.Tag(), ShouldEqual, "gt") 537 So(fe.Namespace(), ShouldEqual, "Test2.Value") 538 So(fe.Translate(trans), ShouldEqual, "Key: 'Test2.Value' Error:Field validation for 'Value' failed on the 'gt' tag") 539 }) 540 541 Convey(`test validator.Struct`, t, func() { 542 var ctxVal string 543 544 fnCtx := func(ctx context.Context, fl FieldLevel) bool { 545 ctxVal = ctx.Value(&ctxVal).(string) 546 return true 547 } 548 549 var ctxSlVal string 550 slFn := func(ctx context.Context, sl StructLevel) { 551 ctxSlVal = ctx.Value(&ctxSlVal).(string) 552 } 553 554 type Test struct { 555 Field string `validate:"val"` 556 } 557 558 var tst Test 559 560 v := New() 561 err := v.RegisterValidation("val", fnCtx) 562 So(err, ShouldBeNil) 563 564 v.RegisterStructValidation(slFn, Test{}) 565 566 ctx := context.WithValue(context.Background(), &ctxVal, "testval") 567 ctx = context.WithValue(ctx, &ctxSlVal, "slVal") 568 errs := v.Struct(ctx, tst) 569 So(errs, ShouldBeNil) 570 So(ctxVal, ShouldEqual, "testval") 571 So(ctxSlVal, ShouldEqual, "slVal") 572 }) 573 574 Convey(`test validator.StructFiltered`, t, func() { 575 p1 := func(ns []byte) bool { 576 if bytes.HasSuffix(ns, []byte("NoTag")) || bytes.HasSuffix(ns, []byte("Required")) { 577 return false 578 } 579 580 return true 581 } 582 583 p2 := func(ns []byte) bool { 584 if bytes.HasSuffix(ns, []byte("SubSlice[0].Test")) || 585 bytes.HasSuffix(ns, []byte("SubSlice[0]")) || 586 bytes.HasSuffix(ns, []byte("SubSlice")) || 587 bytes.HasSuffix(ns, []byte("Sub")) || 588 bytes.HasSuffix(ns, []byte("SubIgnore")) || 589 bytes.HasSuffix(ns, []byte("Anonymous")) || 590 bytes.HasSuffix(ns, []byte("Anonymous.A")) { 591 return false 592 } 593 594 return true 595 } 596 597 p3 := func(ns []byte) bool { 598 return !bytes.HasSuffix(ns, []byte("SubTest.Test")) 599 } 600 601 // p4 := []string{ 602 // "A", 603 // } 604 605 tPartial := &TestPartial{ 606 NoTag: "NoTag", 607 Required: "Required", 608 609 SubSlice: []*SubTest{ 610 { 611 612 Test: "Required", 613 }, 614 { 615 616 Test: "Required", 617 }, 618 }, 619 620 Sub: &SubTest{ 621 Test: "1", 622 }, 623 SubIgnore: &SubTest{ 624 Test: "", 625 }, 626 Anonymous: struct { 627 A string `validate:"required"` 628 ASubSlice []*SubTest `validate:"required,dive"` 629 SubAnonStruct []struct { 630 Test string `validate:"required"` 631 OtherTest string `validate:"required"` 632 } `validate:"required,dive"` 633 }{ 634 A: "1", 635 ASubSlice: []*SubTest{ 636 { 637 Test: "Required", 638 }, 639 { 640 Test: "Required", 641 }, 642 }, 643 644 SubAnonStruct: []struct { 645 Test string `validate:"required"` 646 OtherTest string `validate:"required"` 647 }{ 648 {"Required", "RequiredOther"}, 649 {"Required", "RequiredOther"}, 650 }, 651 }, 652 } 653 654 v := New() 655 656 // the following should all return no errors as everything is valid in 657 // the default state 658 errs := v.StructFiltered(context.Background(), tPartial, p1) 659 So(errs, ShouldBeNil) 660 661 errs = v.StructFiltered(context.Background(), tPartial, p2) 662 So(errs, ShouldBeNil) 663 664 // this isn't really a robust test, but is ment to illustrate the ANON CASE below 665 errs = v.StructFiltered(context.Background(), tPartial.SubSlice[0], p3) 666 So(errs, ShouldBeNil) 667 668 // mod tParial for required feild and re-test making sure invalid fields are NOT required: 669 tPartial.Required = "" 670 671 // inversion and retesting Partial to generate failures: 672 errs = v.StructFiltered(context.Background(), tPartial, p1) 673 So(errs, ShouldNotBeNil) 674 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.Required' Error:Field validation for 'Required' failed on the 'required' tag") 675 676 // reset Required field, and set nested struct 677 tPartial.Required = "Required" 678 tPartial.Anonymous.A = "" 679 680 // will pass as unset feilds is not going to be tested 681 errs = v.StructFiltered(context.Background(), tPartial, p1) 682 So(errs, ShouldBeNil) 683 684 // will fail as unset feild is tested 685 errs = v.StructFiltered(context.Background(), tPartial, p2) 686 So(errs, ShouldNotBeNil) 687 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.Anonymous.A' Error:Field validation for 'A' failed on the 'required' tag") 688 689 // reset nested struct and unset struct in slice 690 tPartial.Anonymous.A = "Required" 691 tPartial.SubSlice[0].Test = "" 692 693 // these will pass as unset item is NOT tested 694 errs = v.StructFiltered(context.Background(), tPartial, p1) 695 So(errs, ShouldBeNil) 696 697 errs = v.StructFiltered(context.Background(), tPartial, p2) 698 So(errs, ShouldNotBeNil) 699 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.SubSlice[0].Test' Error:Field validation for 'Test' failed on the 'required' tag") 700 So(len(errs.(ValidationErrors)), ShouldEqual, 1) 701 702 // Unset second slice member concurrently to test dive behavior: 703 tPartial.SubSlice[1].Test = "" 704 705 errs = v.StructFiltered(context.Background(), tPartial, p1) 706 So(errs, ShouldBeNil) 707 708 errs = v.StructFiltered(context.Background(), tPartial, p2) 709 So(errs, ShouldNotBeNil) 710 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.SubSlice[0].Test' Error:Field validation for 'Test' failed on the 'required' tag") 711 So(len(errs.(ValidationErrors)), ShouldEqual, 1) 712 713 // reset struct in slice, and unset struct in slice in unset posistion 714 tPartial.SubSlice[0].Test = "Required" 715 716 // these will pass as the unset item is NOT tested 717 errs = v.StructFiltered(context.Background(), tPartial, p1) 718 So(errs, ShouldBeNil) 719 720 errs = v.StructFiltered(context.Background(), tPartial, p2) 721 So(errs, ShouldBeNil) 722 723 tPartial.SubSlice[1].Test = "Required" 724 tPartial.Anonymous.SubAnonStruct[0].Test = "" 725 726 // these will pass as the unset item is NOT tested 727 errs = v.StructFiltered(context.Background(), tPartial, p1) 728 So(errs, ShouldBeNil) 729 730 errs = v.StructFiltered(context.Background(), tPartial, p2) 731 So(errs, ShouldBeNil) 732 733 dt := time.Now() 734 err := v.StructFiltered(context.Background(), &dt, func(ns []byte) bool { return true }) 735 So(err, ShouldNotBeNil) 736 So(err.Error(), ShouldEqual, "validator: (nil *time.Time)") 737 }) 738 739 Convey(`test validator.StructPartial/validator.StructExcept`, t, func() { 740 p1 := []string{ 741 "NoTag", 742 "Required", 743 } 744 745 p2 := []string{ 746 "SubSlice[0].Test", 747 "Sub", 748 "SubIgnore", 749 "Anonymous.A", 750 } 751 752 p3 := []string{ 753 "SubTest.Test", 754 } 755 756 p4 := []string{ 757 "A", 758 } 759 760 tPartial := &TestPartial{ 761 NoTag: "NoTag", 762 Required: "Required", 763 764 SubSlice: []*SubTest{ 765 { 766 767 Test: "Required", 768 }, 769 { 770 771 Test: "Required", 772 }, 773 }, 774 775 Sub: &SubTest{ 776 Test: "1", 777 }, 778 SubIgnore: &SubTest{ 779 Test: "", 780 }, 781 Anonymous: struct { 782 A string `validate:"required"` 783 ASubSlice []*SubTest `validate:"required,dive"` 784 SubAnonStruct []struct { 785 Test string `validate:"required"` 786 OtherTest string `validate:"required"` 787 } `validate:"required,dive"` 788 }{ 789 A: "1", 790 ASubSlice: []*SubTest{ 791 { 792 Test: "Required", 793 }, 794 { 795 Test: "Required", 796 }, 797 }, 798 799 SubAnonStruct: []struct { 800 Test string `validate:"required"` 801 OtherTest string `validate:"required"` 802 }{ 803 {"Required", "RequiredOther"}, 804 {"Required", "RequiredOther"}, 805 }, 806 }, 807 } 808 809 v := New() 810 811 // the following should all return no errors as everything is valid in 812 // the default state 813 errs := v.StructPartial(context.Background(), tPartial, p1...) 814 So(errs, ShouldBeNil) 815 816 errs = v.StructPartial(context.Background(), tPartial, p2...) 817 So(errs, ShouldBeNil) 818 819 // this isn't really a robust test, but is ment to illustrate the ANON CASE below 820 errs = v.StructPartial(context.Background(), tPartial.SubSlice[0], p3...) 821 So(errs, ShouldBeNil) 822 823 errs = v.StructExcept(context.Background(), tPartial, p1...) 824 So(errs, ShouldBeNil) 825 826 errs = v.StructExcept(context.Background(), tPartial, p2...) 827 So(errs, ShouldBeNil) 828 829 // mod tParial for required feild and re-test making sure invalid fields are NOT required: 830 tPartial.Required = "" 831 832 errs = v.StructExcept(context.Background(), tPartial, p1...) 833 So(errs, ShouldBeNil) 834 835 errs = v.StructPartial(context.Background(), tPartial, p2...) 836 So(errs, ShouldBeNil) 837 838 // inversion and retesting Partial to generate failures: 839 errs = v.StructPartial(context.Background(), tPartial, p1...) 840 So(errs, ShouldNotBeNil) 841 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.Required' Error:Field validation for 'Required' failed on the 'required' tag") 842 843 errs = v.StructExcept(context.Background(), tPartial, p2...) 844 So(errs, ShouldNotBeNil) 845 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.Required' Error:Field validation for 'Required' failed on the 'required' tag") 846 847 // reset Required field, and set nested struct 848 tPartial.Required = "Required" 849 tPartial.Anonymous.A = "" 850 851 // will pass as unset feilds is not going to be tested 852 errs = v.StructPartial(context.Background(), tPartial, p1...) 853 So(errs, ShouldBeNil) 854 855 errs = v.StructExcept(context.Background(), tPartial, p2...) 856 So(errs, ShouldBeNil) 857 858 // ANON CASE the response here is strange, it clearly does what it is being told to 859 errs = v.StructExcept(context.Background(), tPartial.Anonymous, p4...) 860 So(errs, ShouldBeNil) 861 862 // will fail as unset feild is tested 863 errs = v.StructPartial(context.Background(), tPartial, p2...) 864 So(errs, ShouldNotBeNil) 865 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.Anonymous.A' Error:Field validation for 'A' failed on the 'required' tag") 866 867 errs = v.StructExcept(context.Background(), tPartial, p1...) 868 So(errs, ShouldNotBeNil) 869 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.Anonymous.A' Error:Field validation for 'A' failed on the 'required' tag") 870 871 // reset nested struct and unset struct in slice 872 tPartial.Anonymous.A = "Required" 873 tPartial.SubSlice[0].Test = "" 874 875 // these will pass as unset item is NOT tested 876 errs = v.StructPartial(context.Background(), tPartial, p1...) 877 So(errs, ShouldBeNil) 878 879 errs = v.StructExcept(context.Background(), tPartial, p2...) 880 So(errs, ShouldBeNil) 881 882 // these will fail as unset item IS tested 883 errs = v.StructExcept(context.Background(), tPartial, p1...) 884 So(errs, ShouldNotBeNil) 885 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.SubSlice[0].Test' Error:Field validation for 'Test' failed on the 'required' tag") 886 So(len(errs.(ValidationErrors)), ShouldEqual, 1) 887 888 errs = v.StructPartial(context.Background(), tPartial, p2...) 889 So(errs, ShouldNotBeNil) 890 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.SubSlice[0].Test' Error:Field validation for 'Test' failed on the 'required' tag") 891 So(len(errs.(ValidationErrors)), ShouldEqual, 1) 892 893 // Unset second slice member concurrently to test dive behavior: 894 tPartial.SubSlice[1].Test = "" 895 896 errs = v.StructPartial(context.Background(), tPartial, p1...) 897 So(errs, ShouldBeNil) 898 899 // NOTE: When specifying nested items, it is still the users responsibility 900 // to specify the dive tag, the library does not override this. 901 errs = v.StructExcept(context.Background(), tPartial, p2...) 902 So(errs, ShouldNotBeNil) 903 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.SubSlice[1].Test' Error:Field validation for 'Test' failed on the 'required' tag") 904 905 errs = v.StructExcept(context.Background(), tPartial, p1...) 906 So(errs, ShouldNotBeNil) 907 So(len(errs.(ValidationErrors)), ShouldEqual, 2) 908 So(errs.(ValidationErrors)[0].Error(), ShouldEqual, "Key: 'TestPartial.SubSlice[0].Test' Error:Field validation for 'Test' failed on the 'required' tag") 909 So(errs.(ValidationErrors)[1].Error(), ShouldEqual, "Key: 'TestPartial.SubSlice[1].Test' Error:Field validation for 'Test' failed on the 'required' tag") 910 911 errs = v.StructPartial(context.Background(), tPartial, p2...) 912 So(errs, ShouldNotBeNil) 913 So(len(errs.(ValidationErrors)), ShouldEqual, 1) 914 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.SubSlice[0].Test' Error:Field validation for 'Test' failed on the 'required' tag") 915 916 // reset struct in slice, and unset struct in slice in unset posistion 917 tPartial.SubSlice[0].Test = "Required" 918 919 // these will pass as the unset item is NOT tested 920 errs = v.StructPartial(context.Background(), tPartial, p1...) 921 So(errs, ShouldBeNil) 922 923 errs = v.StructPartial(context.Background(), tPartial, p2...) 924 So(errs, ShouldBeNil) 925 926 // testing for missing item by exception, yes it dives and fails 927 errs = v.StructExcept(context.Background(), tPartial, p1...) 928 So(errs, ShouldNotBeNil) 929 So(len(errs.(ValidationErrors)), ShouldEqual, 1) 930 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.SubSlice[1].Test' Error:Field validation for 'Test' failed on the 'required' tag") 931 932 errs = v.StructExcept(context.Background(), tPartial, p2...) 933 So(errs, ShouldNotBeNil) 934 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.SubSlice[1].Test' Error:Field validation for 'Test' failed on the 'required' tag") 935 936 tPartial.SubSlice[1].Test = "Required" 937 938 tPartial.Anonymous.SubAnonStruct[0].Test = "" 939 // these will pass as the unset item is NOT tested 940 errs = v.StructPartial(context.Background(), tPartial, p1...) 941 So(errs, ShouldBeNil) 942 943 errs = v.StructPartial(context.Background(), tPartial, p2...) 944 So(errs, ShouldBeNil) 945 946 errs = v.StructExcept(context.Background(), tPartial, p1...) 947 So(errs, ShouldNotBeNil) 948 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.Anonymous.SubAnonStruct[0].Test' Error:Field validation for 'Test' failed on the 'required' tag") 949 950 errs = v.StructExcept(context.Background(), tPartial, p2...) 951 So(errs, ShouldNotBeNil) 952 So(errs.Error(), ShouldEqual, "Key: 'TestPartial.Anonymous.SubAnonStruct[0].Test' Error:Field validation for 'Test' failed on the 'required' tag") 953 954 // Test for unnamed struct 955 testStruct := &TestStruct{ 956 String: "test", 957 } 958 unnamedStruct := struct { 959 String string `validate:"required" json:"StringVal"` 960 }{String: "test"} 961 composedUnnamedStruct := struct{ *TestStruct }{&TestStruct{String: "test"}} 962 963 errs = v.StructPartial(context.Background(), testStruct, "String") 964 So(errs, ShouldBeNil) 965 966 errs = v.StructPartial(context.Background(), unnamedStruct, "String") 967 So(errs, ShouldBeNil) 968 969 errs = v.StructPartial(context.Background(), composedUnnamedStruct, "TestStruct.String") 970 So(errs, ShouldBeNil) 971 972 testStruct.String = "" 973 errs = v.StructPartial(context.Background(), testStruct, "String") 974 So(errs, ShouldNotBeNil) 975 So(errs.Error(), ShouldEqual, "Key: 'TestStruct.String' Error:Field validation for 'String' failed on the 'required' tag") 976 977 unnamedStruct.String = "" 978 errs = v.StructPartial(context.Background(), unnamedStruct, "String") 979 So(errs, ShouldNotBeNil) 980 So(errs.Error(), ShouldEqual, "Key: 'String' Error:Field validation for 'String' failed on the 'required' tag") 981 982 composedUnnamedStruct.String = "" 983 errs = v.StructPartial(context.Background(), composedUnnamedStruct, "TestStruct.String") 984 So(errs, ShouldNotBeNil) 985 So(errs.Error(), ShouldEqual, "Key: 'TestStruct.String' Error:Field validation for 'String' failed on the 'required' tag") 986 }) 987 988 Convey(`test validator.VarWithValue`, t, func() { 989 var errs error 990 v := New() 991 992 type TimeTest struct { 993 Start *time.Time `validate:"required,gt"` 994 End *time.Time `validate:"required,gt,gtfield=Start"` 995 } 996 997 now := time.Now() 998 start := now.Add(time.Hour * 24) 999 end := start.Add(time.Hour * 24) 1000 1001 timeTest := &TimeTest{ 1002 Start: &start, 1003 End: &end, 1004 } 1005 1006 errs = v.Struct(context.Background(), timeTest) 1007 So(errs, ShouldBeNil) 1008 1009 timeTest = &TimeTest{ 1010 Start: &end, 1011 End: &start, 1012 } 1013 1014 errs = v.Struct(context.Background(), timeTest) 1015 So(errs, ShouldNotBeNil) 1016 So(errs.Error(), ShouldEqual, "Key: 'TimeTest.End' Error:Field validation for 'End' failed on the 'gtfield' tag") 1017 1018 errs = v.VarWithValue(context.Background(), &end, &start, "gtfield") 1019 So(errs, ShouldBeNil) 1020 1021 errs = v.VarWithValue(context.Background(), &start, &end, "gtfield") 1022 So(errs, ShouldNotBeNil) 1023 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'gtfield' tag") 1024 1025 errs = v.VarWithValue(context.Background(), &end, &start, "gtfield") 1026 So(errs, ShouldBeNil) 1027 1028 errs = v.VarWithValue(context.Background(), &timeTest, &end, "gtfield") 1029 So(errs, ShouldNotBeNil) 1030 So(errs.Error(), ShouldEqual, "Key: 'TimeTest.End' Error:Field validation for 'End' failed on the 'gtfield' tag") 1031 1032 errs = v.VarWithValue(context.Background(), "test bigger", "test", "gtfield") 1033 So(errs, ShouldBeNil) 1034 1035 // Tests for time.Duration type. 1036 1037 // -- Validations for variables of time.Duration type. 1038 1039 errs = v.VarWithValue(context.Background(), time.Hour, time.Hour-time.Minute, "gtfield") 1040 So(errs, ShouldBeNil) 1041 1042 errs = v.VarWithValue(context.Background(), time.Hour, time.Hour, "gtfield") 1043 So(errs, ShouldNotBeNil) 1044 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'gtfield' tag") 1045 1046 errs = v.VarWithValue(context.Background(), time.Hour, time.Hour+time.Minute, "gtfield") 1047 So(errs, ShouldNotBeNil) 1048 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'gtfield' tag") 1049 1050 errs = v.VarWithValue(context.Background(), time.Duration(0), time.Hour, "omitempty,gtfield") 1051 So(errs, ShouldBeNil) 1052 1053 // -- Validations for a struct with time.Duration type fields. 1054 1055 type TimeDurationTest struct { 1056 First time.Duration `validate:"gtfield=Second"` 1057 Second time.Duration 1058 } 1059 var timeDurationTest *TimeDurationTest 1060 1061 timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute} 1062 errs = v.Struct(context.Background(), timeDurationTest) 1063 So(errs, ShouldBeNil) 1064 1065 timeDurationTest = &TimeDurationTest{time.Hour, time.Hour} 1066 errs = v.Struct(context.Background(), timeDurationTest) 1067 So(errs, ShouldNotBeNil) 1068 So(errs.Error(), ShouldEqual, "Key: 'TimeDurationTest.First' Error:Field validation for 'First' failed on the 'gtfield' tag") 1069 1070 timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute} 1071 errs = v.Struct(context.Background(), timeDurationTest) 1072 So(errs, ShouldNotBeNil) 1073 So(errs.Error(), ShouldEqual, "Key: 'TimeDurationTest.First' Error:Field validation for 'First' failed on the 'gtfield' tag") 1074 1075 type TimeDurationOmitemptyTest struct { 1076 First time.Duration `validate:"omitempty,gtfield=Second"` 1077 Second time.Duration 1078 } 1079 1080 timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), time.Hour} 1081 errs = v.Struct(context.Background(), timeDurationOmitemptyTest) 1082 So(errs, ShouldBeNil) 1083 1084 // Tests for Ints types. 1085 1086 type IntTest struct { 1087 Val1 int `validate:"required"` 1088 Val2 int `validate:"required,gtfield=Val1"` 1089 } 1090 1091 intTest := &IntTest{ 1092 Val1: 1, 1093 Val2: 5, 1094 } 1095 1096 errs = v.Struct(context.Background(), intTest) 1097 So(errs, ShouldBeNil) 1098 1099 intTest = &IntTest{ 1100 Val1: 5, 1101 Val2: 1, 1102 } 1103 1104 errs = v.Struct(context.Background(), intTest) 1105 So(errs, ShouldNotBeNil) 1106 So(errs.Error(), ShouldEqual, "Key: 'IntTest.Val2' Error:Field validation for 'Val2' failed on the 'gtfield' tag") 1107 1108 errs = v.VarWithValue(context.Background(), int(5), int(1), "gtfield") 1109 So(errs, ShouldBeNil) 1110 1111 errs = v.VarWithValue(context.Background(), int(1), int(5), "gtfield") 1112 So(errs, ShouldNotBeNil) 1113 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'gtfield' tag") 1114 1115 type UIntTest struct { 1116 Val1 uint `validate:"required"` 1117 Val2 uint `validate:"required,gtfield=Val1"` 1118 } 1119 1120 uIntTest := &UIntTest{ 1121 Val1: 1, 1122 Val2: 5, 1123 } 1124 1125 errs = v.Struct(context.Background(), uIntTest) 1126 So(errs, ShouldBeNil) 1127 1128 uIntTest = &UIntTest{ 1129 Val1: 5, 1130 Val2: 1, 1131 } 1132 1133 errs = v.Struct(context.Background(), uIntTest) 1134 So(errs, ShouldNotBeNil) 1135 So(errs.Error(), ShouldEqual, "Key: 'UIntTest.Val2' Error:Field validation for 'Val2' failed on the 'gtfield' tag") 1136 1137 errs = v.VarWithValue(context.Background(), uint(5), uint(1), "gtfield") 1138 So(errs, ShouldBeNil) 1139 1140 errs = v.VarWithValue(context.Background(), uint(1), uint(5), "gtfield") 1141 So(errs, ShouldNotBeNil) 1142 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'gtfield' tag") 1143 1144 type FloatTest struct { 1145 Val1 float64 `validate:"required"` 1146 Val2 float64 `validate:"required,gtfield=Val1"` 1147 } 1148 1149 floatTest := &FloatTest{ 1150 Val1: 1, 1151 Val2: 5, 1152 } 1153 1154 errs = v.Struct(context.Background(), floatTest) 1155 So(errs, ShouldBeNil) 1156 1157 floatTest = &FloatTest{ 1158 Val1: 5, 1159 Val2: 1, 1160 } 1161 1162 errs = v.Struct(context.Background(), floatTest) 1163 So(errs, ShouldNotBeNil) 1164 So(errs.Error(), ShouldEqual, "Key: 'FloatTest.Val2' Error:Field validation for 'Val2' failed on the 'gtfield' tag") 1165 1166 errs = v.VarWithValue(context.Background(), float32(5), float32(1), "gtfield") 1167 So(errs, ShouldBeNil) 1168 1169 errs = v.VarWithValue(context.Background(), float32(1), float32(5), "gtfield") 1170 So(errs, ShouldNotBeNil) 1171 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'gtfield' tag") 1172 1173 errs = v.VarWithValue(context.Background(), nil, 1, "gtfield") 1174 So(errs, ShouldNotBeNil) 1175 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'gtfield' tag") 1176 1177 errs = v.VarWithValue(context.Background(), 5, "T", "gtfield") 1178 So(errs, ShouldNotBeNil) 1179 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'gtfield' tag") 1180 1181 errs = v.VarWithValue(context.Background(), 5, start, "gtfield") 1182 So(errs, ShouldNotBeNil) 1183 So(errs.Error(), ShouldEqual, "Key: '' Error:Field validation for '' failed on the 'gtfield' tag") 1184 1185 type TimeTest2 struct { 1186 Start *time.Time `validate:"required"` 1187 End *time.Time `validate:"required,gtfield=NonExistantField"` 1188 } 1189 1190 timeTest2 := &TimeTest2{ 1191 Start: &start, 1192 End: &end, 1193 } 1194 1195 errs = v.Struct(context.Background(), timeTest2) 1196 So(errs, ShouldNotBeNil) 1197 So(errs.Error(), ShouldEqual, "Key: 'TimeTest2.End' Error:Field validation for 'End' failed on the 'gtfield' tag") 1198 1199 type Other struct { 1200 Value string 1201 } 1202 1203 type Test struct { 1204 Value Other 1205 Time time.Time `validate:"gtfield=Value"` 1206 } 1207 1208 tst := Test{ 1209 Value: Other{Value: "StringVal"}, 1210 Time: end, 1211 } 1212 1213 errs = v.Struct(context.Background(), tst) 1214 So(errs, ShouldNotBeNil) 1215 So(errs.Error(), ShouldEqual, "Key: 'Test.Time' Error:Field validation for 'Time' failed on the 'gtfield' tag") 1216 }) 1217 }