@@ -694,6 +694,60 @@ func TestStructLevelValidations(t *testing.T) {
694694 Equal (t , errs , nil )
695695}
696696
697+ func TestRegisterStructValidationMapRules (t * testing.T ) {
698+ type MapRulesTestStruct struct {
699+ Name string `validate:"required"`
700+ Age int
701+ }
702+
703+ rules := map [string ]string {
704+ "Name" : "omitempty" ,
705+ "Age" : "required,gt=0" ,
706+ }
707+
708+ validate := New ()
709+ validate .RegisterStructValidationMapRules (rules , MapRulesTestStruct {})
710+
711+ // Name's own "required" tag is superseded by the "omitempty" rule, so an
712+ // empty Name is now allowed, while Age's rule is enforced.
713+ errs := validate .Struct (MapRulesTestStruct {Name : "" , Age : 5 })
714+ Equal (t , errs , nil )
715+
716+ errs = validate .Struct (MapRulesTestStruct {Name : "" , Age : 0 })
717+ NotEqual (t , errs , nil )
718+ AssertError (t , errs , "MapRulesTestStruct.Age" , "MapRulesTestStruct.Age" , "Age" , "Age" , "required" )
719+
720+ errs = validate .Struct (MapRulesTestStruct {Name : "gopher" , Age : 5 })
721+ Equal (t , errs , nil )
722+
723+ // mutating the map passed to RegisterStructValidationMapRules after
724+ // registration must not affect already-registered rules (deep copy).
725+ rules ["Name" ] = "required"
726+ errs = validate .Struct (MapRulesTestStruct {Name : "" , Age : 5 })
727+ Equal (t , errs , nil )
728+
729+ // a struct with no registered rules falls back to its own tags.
730+ type NoRulesTestStruct struct {
731+ Name string `validate:"required"`
732+ }
733+ errs = validate .Struct (NoRulesTestStruct {Name : "" })
734+ NotEqual (t , errs , nil )
735+ AssertError (t , errs , "NoRulesTestStruct.Name" , "NoRulesTestStruct.Name" , "Name" , "Name" , "required" )
736+
737+ // registering against a pointer type applies the same as the value type.
738+ v2 := New ()
739+ v2 .RegisterStructValidationMapRules (rules , & MapRulesTestStruct {})
740+ errs = v2 .Struct (& MapRulesTestStruct {Name : "" , Age : 5 })
741+ NotEqual (t , errs , nil )
742+ AssertError (t , errs , "MapRulesTestStruct.Name" , "MapRulesTestStruct.Name" , "Name" , "Name" , "required" )
743+
744+ // non-struct types are silently ignored rather than panicking.
745+ v3 := New ()
746+ v3 .RegisterStructValidationMapRules (map [string ]string {"Foo" : "required" }, 5 )
747+ errs = v3 .Struct (MapRulesTestStruct {Name : "gopher" , Age : 5 })
748+ Equal (t , errs , nil )
749+ }
750+
697751func TestAliasTags (t * testing.T ) {
698752 validate := New ()
699753 validate .RegisterAlias ("iscoloralias" , "hexcolor|rgb|rgba|hsl|hsla|cmyk" )
@@ -8031,6 +8085,10 @@ func TestLength(t *testing.T) {
80318085 validate := New ()
80328086 i := true
80338087 PanicMatches (t , func () { _ = validate .Var (i , "len" ) }, "Bad field type bool" )
8088+
8089+ var f32 float32 = 5
8090+ Equal (t , validate .Var (f32 , "len=5" ), nil )
8091+ NotEqual (t , validate .Var (f32 , "len=6" ), nil )
80348092}
80358093
80368094func TestIsGt (t * testing.T ) {
@@ -8051,6 +8109,11 @@ func TestIsGt(t *testing.T) {
80518109 NotEqual (t , errs , nil )
80528110 AssertError (t , errs , "" , "" , "" , "" , "gt" )
80538111
8112+ var f32 float32 = 1.23
8113+ errs = validate .Var (f32 , "gt=5" )
8114+ NotEqual (t , errs , nil )
8115+ AssertError (t , errs , "" , "" , "" , "" , "gt" )
8116+
80548117 i := true
80558118 PanicMatches (t , func () { _ = validate .Var (i , "gt" ) }, "Bad field type bool" )
80568119
@@ -8136,6 +8199,26 @@ func TestIsGte(t *testing.T) {
81368199 var errs error
81378200 validate := New ()
81388201
8202+ myMap := map [string ]string {}
8203+ errs = validate .Var (myMap , "gte=1" )
8204+ NotEqual (t , errs , nil )
8205+ AssertError (t , errs , "" , "" , "" , "" , "gte" )
8206+
8207+ f := 1.23
8208+ errs = validate .Var (f , "gte=5" )
8209+ NotEqual (t , errs , nil )
8210+ AssertError (t , errs , "" , "" , "" , "" , "gte" )
8211+
8212+ var ui uint = 5
8213+ errs = validate .Var (ui , "gte=10" )
8214+ NotEqual (t , errs , nil )
8215+ AssertError (t , errs , "" , "" , "" , "" , "gte" )
8216+
8217+ var f32 float32 = 1.23
8218+ errs = validate .Var (f32 , "gte=5" )
8219+ NotEqual (t , errs , nil )
8220+ AssertError (t , errs , "" , "" , "" , "" , "gte" )
8221+
81398222 i := true
81408223 PanicMatches (t , func () { _ = validate .Var (i , "gte" ) }, "Bad field type bool" )
81418224
@@ -8452,6 +8535,11 @@ func TestIsLt(t *testing.T) {
84528535 NotEqual (t , errs , nil )
84538536 AssertError (t , errs , "" , "" , "" , "" , "lt" )
84548537
8538+ var f32 float32 = 1.23
8539+ errs = validate .Var (f32 , "lt=0" )
8540+ NotEqual (t , errs , nil )
8541+ AssertError (t , errs , "" , "" , "" , "" , "lt" )
8542+
84558543 i := true
84568544 PanicMatches (t , func () { _ = validate .Var (i , "lt" ) }, "Bad field type bool" )
84578545
@@ -8538,6 +8626,26 @@ func TestIsLte(t *testing.T) {
85388626 var errs error
85398627 validate := New ()
85408628
8629+ myMap := map [string ]string {"key" : "value" }
8630+ errs = validate .Var (myMap , "lte=0" )
8631+ NotEqual (t , errs , nil )
8632+ AssertError (t , errs , "" , "" , "" , "" , "lte" )
8633+
8634+ f := 1.23
8635+ errs = validate .Var (f , "lte=0" )
8636+ NotEqual (t , errs , nil )
8637+ AssertError (t , errs , "" , "" , "" , "" , "lte" )
8638+
8639+ var ui uint = 5
8640+ errs = validate .Var (ui , "lte=0" )
8641+ NotEqual (t , errs , nil )
8642+ AssertError (t , errs , "" , "" , "" , "" , "lte" )
8643+
8644+ var f32 float32 = 1.23
8645+ errs = validate .Var (f32 , "lte=0" )
8646+ NotEqual (t , errs , nil )
8647+ AssertError (t , errs , "" , "" , "" , "" , "lte" )
8648+
85418649 i := true
85428650 PanicMatches (t , func () { _ = validate .Var (i , "lte" ) }, "Bad field type bool" )
85438651
@@ -12079,6 +12187,60 @@ func TestEndsWithValidation(t *testing.T) {
1207912187 }
1208012188}
1208112189
12190+ func TestStartsNotWithValidation (t * testing.T ) {
12191+ tests := []struct {
12192+ Value string `validate:"startsnotwith=(/^ヮ^)/*:・゚✧"`
12193+ Tag string
12194+ ExpectedNil bool
12195+ }{
12196+ {Value : "abcd" , Tag : "startsnotwith=(/^ヮ^)/*:・゚✧" , ExpectedNil : true },
12197+ {Value : "(/^ヮ^)/*:・゚✧ glitter" , Tag : "startsnotwith=(/^ヮ^)/*:・゚✧" , ExpectedNil : false },
12198+ }
12199+
12200+ validate := New ()
12201+
12202+ for i , s := range tests {
12203+ errs := validate .Var (s .Value , s .Tag )
12204+
12205+ if (s .ExpectedNil && errs != nil ) || (! s .ExpectedNil && errs == nil ) {
12206+ t .Fatalf ("Index: %d failed Error: %s" , i , errs )
12207+ }
12208+
12209+ errs = validate .Struct (s )
12210+
12211+ if (s .ExpectedNil && errs != nil ) || (! s .ExpectedNil && errs == nil ) {
12212+ t .Fatalf ("Index: %d failed Error: %s" , i , errs )
12213+ }
12214+ }
12215+ }
12216+
12217+ func TestEndsNotWithValidation (t * testing.T ) {
12218+ tests := []struct {
12219+ Value string `validate:"endsnotwith=(/^ヮ^)/*:・゚✧"`
12220+ Tag string
12221+ ExpectedNil bool
12222+ }{
12223+ {Value : "(/^ヮ^)/*:・゚✧ glitter" , Tag : "endsnotwith=(/^ヮ^)/*:・゚✧" , ExpectedNil : true },
12224+ {Value : "glitter (/^ヮ^)/*:・゚✧" , Tag : "endsnotwith=(/^ヮ^)/*:・゚✧" , ExpectedNil : false },
12225+ }
12226+
12227+ validate := New ()
12228+
12229+ for i , s := range tests {
12230+ errs := validate .Var (s .Value , s .Tag )
12231+
12232+ if (s .ExpectedNil && errs != nil ) || (! s .ExpectedNil && errs == nil ) {
12233+ t .Fatalf ("Index: %d failed Error: %s" , i , errs )
12234+ }
12235+
12236+ errs = validate .Struct (s )
12237+
12238+ if (s .ExpectedNil && errs != nil ) || (! s .ExpectedNil && errs == nil ) {
12239+ t .Fatalf ("Index: %d failed Error: %s" , i , errs )
12240+ }
12241+ }
12242+ }
12243+
1208212244func TestRequiredIf (t * testing.T ) {
1208312245 type Inner struct {
1208412246 Field * string
0 commit comments