From edb25ac80eebb2471b3890b1784370a0ca784be1 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Tue, 10 Mar 2015 23:34:19 -0400 Subject: [PATCH] issue-#13 add generic cross field validation for Number and time.Time data types --- baked_in.go | 312 +++++++++++++++++++++++++- doc.go | 28 +++ validator_test.go | 556 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 892 insertions(+), 4 deletions(-) diff --git a/baked_in.go b/baked_in.go index 07c1563..dc4de32 100644 --- a/baked_in.go +++ b/baked_in.go @@ -19,6 +19,10 @@ var BakedInValidators = map[string]ValidationFunc{ "lte": isLte, "gt": isGt, "gte": isGte, + "gtefield": isGteField, + "gtfield": isGtField, + "ltefield": isLteField, + "ltfield": isLtField, "alpha": isAlpha, "alphanum": isAlphanum, "numeric": isNumeric, @@ -229,6 +233,156 @@ func hasValue(val interface{}, field interface{}, param string) bool { } } +func isGteField(val interface{}, field interface{}, param string) bool { + + if val == nil { + panic("struct not passed for cross validation") + } + + topVal := reflect.ValueOf(val) + + if topVal.Kind() == reflect.Ptr && !topVal.IsNil() { + topVal = reflect.ValueOf(topVal.Elem().Interface()) + } + + var topFieldVal reflect.Value + + switch topVal.Kind() { + + case reflect.Struct: + + if topVal.Type() == reflect.TypeOf(time.Time{}) { + topFieldVal = topVal + break + } + + f := topVal.FieldByName(param) + + if f.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + + topFieldVal = f + + default: + + topFieldVal = topVal + } + + if topFieldVal.Kind() == reflect.Ptr && !topFieldVal.IsNil() { + + topFieldVal = reflect.ValueOf(topFieldVal.Elem().Interface()) + } + + fv := reflect.ValueOf(field) + + switch fv.Kind() { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return fv.Int() >= topFieldVal.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return fv.Uint() >= topFieldVal.Uint() + + case reflect.Float32, reflect.Float64: + + return fv.Float() >= topFieldVal.Float() + + case reflect.Struct: + + if fv.Type() == reflect.TypeOf(time.Time{}) { + + if topFieldVal.Type() != reflect.TypeOf(time.Time{}) { + panic("Bad Top Level field type") + } + + t := topFieldVal.Interface().(time.Time) + fieldTime := field.(time.Time) + + return fieldTime.After(t) || fieldTime.Equal(t) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isGtField(val interface{}, field interface{}, param string) bool { + + if val == nil { + panic("struct not passed for cross validation") + } + + topVal := reflect.ValueOf(val) + + if topVal.Kind() == reflect.Ptr && !topVal.IsNil() { + topVal = reflect.ValueOf(topVal.Elem().Interface()) + } + + var topFieldVal reflect.Value + + switch topVal.Kind() { + + case reflect.Struct: + + if topVal.Type() == reflect.TypeOf(time.Time{}) { + topFieldVal = topVal + break + } + + f := topVal.FieldByName(param) + + if f.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + + topFieldVal = f + + default: + + topFieldVal = topVal + } + + if topFieldVal.Kind() == reflect.Ptr && !topFieldVal.IsNil() { + + topFieldVal = reflect.ValueOf(topFieldVal.Elem().Interface()) + } + + fv := reflect.ValueOf(field) + + switch fv.Kind() { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return fv.Int() > topFieldVal.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return fv.Uint() > topFieldVal.Uint() + + case reflect.Float32, reflect.Float64: + + return fv.Float() > topFieldVal.Float() + + case reflect.Struct: + + if fv.Type() == reflect.TypeOf(time.Time{}) { + + if topFieldVal.Type() != reflect.TypeOf(time.Time{}) { + panic("Bad Top Level field type") + } + + t := topFieldVal.Interface().(time.Time) + fieldTime := field.(time.Time) + + return fieldTime.After(t) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + func isGte(val interface{}, field interface{}, param string) bool { st := reflect.ValueOf(field) @@ -262,7 +416,7 @@ func isGte(val interface{}, field interface{}, param string) bool { case reflect.Struct: - if st.Type() == reflect.TypeOf(field) { + if st.Type() == reflect.TypeOf(time.Time{}) { now := time.Now().UTC() t := field.(time.Time) @@ -306,7 +460,7 @@ func isGt(val interface{}, field interface{}, param string) bool { return st.Float() > p case reflect.Struct: - if st.Type() == reflect.TypeOf(field) { + if st.Type() == reflect.TypeOf(time.Time{}) { return field.(time.Time).After(time.Now().UTC()) } @@ -362,6 +516,156 @@ func hasMinOf(val interface{}, field interface{}, param string) bool { return isGte(val, field, param) } +func isLteField(val interface{}, field interface{}, param string) bool { + + if val == nil { + panic("struct not passed for cross validation") + } + + topVal := reflect.ValueOf(val) + + if topVal.Kind() == reflect.Ptr && !topVal.IsNil() { + topVal = reflect.ValueOf(topVal.Elem().Interface()) + } + + var topFieldVal reflect.Value + + switch topVal.Kind() { + + case reflect.Struct: + + if topVal.Type() == reflect.TypeOf(time.Time{}) { + topFieldVal = topVal + break + } + + f := topVal.FieldByName(param) + + if f.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + + topFieldVal = f + + default: + + topFieldVal = topVal + } + + if topFieldVal.Kind() == reflect.Ptr && !topFieldVal.IsNil() { + + topFieldVal = reflect.ValueOf(topFieldVal.Elem().Interface()) + } + + fv := reflect.ValueOf(field) + + switch fv.Kind() { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return fv.Int() <= topFieldVal.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return fv.Uint() <= topFieldVal.Uint() + + case reflect.Float32, reflect.Float64: + + return fv.Float() <= topFieldVal.Float() + + case reflect.Struct: + + if fv.Type() == reflect.TypeOf(time.Time{}) { + + if topFieldVal.Type() != reflect.TypeOf(time.Time{}) { + panic("Bad Top Level field type") + } + + t := topFieldVal.Interface().(time.Time) + fieldTime := field.(time.Time) + + return fieldTime.Before(t) || fieldTime.Equal(t) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isLtField(val interface{}, field interface{}, param string) bool { + + if val == nil { + panic("struct not passed for cross validation") + } + + topVal := reflect.ValueOf(val) + + if topVal.Kind() == reflect.Ptr && !topVal.IsNil() { + topVal = reflect.ValueOf(topVal.Elem().Interface()) + } + + var topFieldVal reflect.Value + + switch topVal.Kind() { + + case reflect.Struct: + + if topVal.Type() == reflect.TypeOf(time.Time{}) { + topFieldVal = topVal + break + } + + f := topVal.FieldByName(param) + + if f.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + + topFieldVal = f + + default: + + topFieldVal = topVal + } + + if topFieldVal.Kind() == reflect.Ptr && !topFieldVal.IsNil() { + + topFieldVal = reflect.ValueOf(topFieldVal.Elem().Interface()) + } + + fv := reflect.ValueOf(field) + + switch fv.Kind() { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return fv.Int() < topFieldVal.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return fv.Uint() < topFieldVal.Uint() + + case reflect.Float32, reflect.Float64: + + return fv.Float() < topFieldVal.Float() + + case reflect.Struct: + + if fv.Type() == reflect.TypeOf(time.Time{}) { + + if topFieldVal.Type() != reflect.TypeOf(time.Time{}) { + panic("Bad Top Level field type") + } + + t := topFieldVal.Interface().(time.Time) + fieldTime := field.(time.Time) + + return fieldTime.Before(t) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + func isLte(val interface{}, field interface{}, param string) bool { st := reflect.ValueOf(field) @@ -395,7 +699,7 @@ func isLte(val interface{}, field interface{}, param string) bool { case reflect.Struct: - if st.Type() == reflect.TypeOf(field) { + if st.Type() == reflect.TypeOf(time.Time{}) { now := time.Now().UTC() t := field.(time.Time) @@ -440,7 +744,7 @@ func isLt(val interface{}, field interface{}, param string) bool { case reflect.Struct: - if st.Type() == reflect.TypeOf(field) { + if st.Type() == reflect.TypeOf(time.Time{}) { return field.(time.Time).Before(time.Now().UTC()) } diff --git a/doc.go b/doc.go index c784d4e..b4cec3f 100644 --- a/doc.go +++ b/doc.go @@ -218,6 +218,34 @@ Here is a list of the current built in validators: For time.Time ensures the time value is less than or equal to time.Now.UTC() (Usage: lte) + gtfield + Only valid for Numbers and time.Time types, this will validate the field value + against another fields value either within a struct or passed in field. + usage examples are for validation of a Start and End date: + Validation on End field using ValidateByStruct Usage(gtfield=Start) + Validating by field ValidateFieldByTagAndValue(start, end, "gtfield") + + gtefield + Only valid for Numbers and time.Time types, this will validate the field value + against another fields value either within a struct or passed in field. + usage examples are for validation of a Start and End date: + Validation on End field using ValidateByStruct Usage(gtefield=Start) + Validating by field ValidateFieldByTagAndValue(start, end, "gtefield") + + ltfield + Only valid for Numbers and time.Time types, this will validate the field value + against another fields value either within a struct or passed in field. + usage examples are for validation of a Start and End date: + Validation on End field using ValidateByStruct Usage(ltfield=Start) + Validating by field ValidateFieldByTagAndValue(start, end, "ltfield") + + ltefield + Only valid for Numbers and time.Time types, this will validate the field value + against another fields value either within a struct or passed in field. + usage examples are for validation of a Start and End date: + Validation on End field using ValidateByStruct Usage(ltefield=Start) + Validating by field ValidateFieldByTagAndValue(start, end, "ltefield") + alpha This validates that a strings value contains alpha characters only (Usage: alpha) diff --git a/validator_test.go b/validator_test.go index 98f0509..8ea8f84 100644 --- a/validator_test.go +++ b/validator_test.go @@ -135,6 +135,562 @@ func isEqualFunc(val interface{}, field interface{}, param string) bool { return val.(string) == field.(string) } +func (ms *MySuite) TestGtField(c *C) { + + type TimeTest struct { + Start *time.Time `validate:"required,gt"` + End *time.Time `validate:"required,gt,gtfield=Start"` + } + + now := time.Now() + start := now.Add(time.Hour * 24) + end := start.Add(time.Hour * 24) + + timeTest := &TimeTest{ + Start: &start, + End: &end, + } + + errs := validator.ValidateStruct(timeTest) + c.Assert(errs, IsNil) + + timeTest = &TimeTest{ + Start: &end, + End: &start, + } + + errs2 := validator.ValidateStruct(timeTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "End", "gtfield", c) + + err3 := validator.ValidateFieldByTagAndValue(&start, &end, "gtfield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(&end, &start, "gtfield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "gtfield") + + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,gtfield=Val1"` + } + + intTest := &IntTest{ + Val1: 1, + Val2: 5, + } + + errs = validator.ValidateStruct(intTest) + c.Assert(errs, IsNil) + + intTest = &IntTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validator.ValidateStruct(intTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "gtfield", c) + + err3 = validator.ValidateFieldByTagAndValue(int(1), int(5), "gtfield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(int(5), int(1), "gtfield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "gtfield") + + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,gtfield=Val1"` + } + + uIntTest := &UIntTest{ + Val1: 1, + Val2: 5, + } + + errs = validator.ValidateStruct(uIntTest) + c.Assert(errs, IsNil) + + uIntTest = &UIntTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validator.ValidateStruct(uIntTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "gtfield", c) + + err3 = validator.ValidateFieldByTagAndValue(uint(1), uint(5), "gtfield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(uint(5), uint(1), "gtfield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "gtfield") + + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,gtfield=Val1"` + } + + floatTest := &FloatTest{ + Val1: 1, + Val2: 5, + } + + errs = validator.ValidateStruct(floatTest) + c.Assert(errs, IsNil) + + floatTest = &FloatTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validator.ValidateStruct(floatTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "gtfield", c) + + err3 = validator.ValidateFieldByTagAndValue(float32(1), float32(5), "gtfield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(float32(5), float32(1), "gtfield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "gtfield") + + c.Assert(func() { validator.ValidateFieldByTagAndValue(nil, 1, "gtfield") }, PanicMatches, "struct not passed for cross validation") + c.Assert(func() { validator.ValidateFieldByTagAndValue(5, "T", "gtfield") }, PanicMatches, "Bad field type string") + c.Assert(func() { validator.ValidateFieldByTagAndValue(5, start, "gtfield") }, PanicMatches, "Bad Top Level field type") + + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,gtfield=NonExistantField"` + } + + timeTest2 := &TimeTest2{ + Start: &start, + End: &end, + } + + c.Assert(func() { validator.ValidateStruct(timeTest2) }, PanicMatches, "Field \"NonExistantField\" not found in struct") +} + +func (ms *MySuite) TestLtField(c *C) { + + type TimeTest struct { + Start *time.Time `validate:"required,lt,ltfield=End"` + End *time.Time `validate:"required,lt"` + } + + now := time.Now() + start := now.Add(time.Hour * 24 * -1 * 2) + end := start.Add(time.Hour * 24) + + timeTest := &TimeTest{ + Start: &start, + End: &end, + } + + errs := validator.ValidateStruct(timeTest) + c.Assert(errs, IsNil) + + timeTest = &TimeTest{ + Start: &end, + End: &start, + } + + errs2 := validator.ValidateStruct(timeTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Start", "ltfield", c) + + err3 := validator.ValidateFieldByTagAndValue(&end, &start, "ltfield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(&start, &end, "ltfield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "ltfield") + + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,ltfield=Val1"` + } + + intTest := &IntTest{ + Val1: 5, + Val2: 1, + } + + errs = validator.ValidateStruct(intTest) + c.Assert(errs, IsNil) + + intTest = &IntTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validator.ValidateStruct(intTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "ltfield", c) + + err3 = validator.ValidateFieldByTagAndValue(int(5), int(1), "ltfield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(int(1), int(5), "ltfield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "ltfield") + + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,ltfield=Val1"` + } + + uIntTest := &UIntTest{ + Val1: 5, + Val2: 1, + } + + errs = validator.ValidateStruct(uIntTest) + c.Assert(errs, IsNil) + + uIntTest = &UIntTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validator.ValidateStruct(uIntTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "ltfield", c) + + err3 = validator.ValidateFieldByTagAndValue(uint(5), uint(1), "ltfield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(uint(1), uint(5), "ltfield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "ltfield") + + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,ltfield=Val1"` + } + + floatTest := &FloatTest{ + Val1: 5, + Val2: 1, + } + + errs = validator.ValidateStruct(floatTest) + c.Assert(errs, IsNil) + + floatTest = &FloatTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validator.ValidateStruct(floatTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "ltfield", c) + + err3 = validator.ValidateFieldByTagAndValue(float32(5), float32(1), "ltfield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(float32(1), float32(5), "ltfield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "ltfield") + + c.Assert(func() { validator.ValidateFieldByTagAndValue(nil, 5, "ltfield") }, PanicMatches, "struct not passed for cross validation") + c.Assert(func() { validator.ValidateFieldByTagAndValue(1, "T", "ltfield") }, PanicMatches, "Bad field type string") + c.Assert(func() { validator.ValidateFieldByTagAndValue(1, end, "ltfield") }, PanicMatches, "Bad Top Level field type") + + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,ltfield=NonExistantField"` + } + + timeTest2 := &TimeTest2{ + Start: &end, + End: &start, + } + + c.Assert(func() { validator.ValidateStruct(timeTest2) }, PanicMatches, "Field \"NonExistantField\" not found in struct") +} + +func (ms *MySuite) TestLteField(c *C) { + + type TimeTest struct { + Start *time.Time `validate:"required,lte,ltefield=End"` + End *time.Time `validate:"required,lte"` + } + + now := time.Now() + start := now.Add(time.Hour * 24 * -1 * 2) + end := start.Add(time.Hour * 24) + + timeTest := &TimeTest{ + Start: &start, + End: &end, + } + + errs := validator.ValidateStruct(timeTest) + c.Assert(errs, IsNil) + + timeTest = &TimeTest{ + Start: &end, + End: &start, + } + + errs2 := validator.ValidateStruct(timeTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Start", "ltefield", c) + + err3 := validator.ValidateFieldByTagAndValue(&end, &start, "ltefield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(&start, &end, "ltefield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "ltefield") + + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,ltefield=Val1"` + } + + intTest := &IntTest{ + Val1: 5, + Val2: 1, + } + + errs = validator.ValidateStruct(intTest) + c.Assert(errs, IsNil) + + intTest = &IntTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validator.ValidateStruct(intTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "ltefield", c) + + err3 = validator.ValidateFieldByTagAndValue(int(5), int(1), "ltefield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(int(1), int(5), "ltefield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "ltefield") + + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,ltefield=Val1"` + } + + uIntTest := &UIntTest{ + Val1: 5, + Val2: 1, + } + + errs = validator.ValidateStruct(uIntTest) + c.Assert(errs, IsNil) + + uIntTest = &UIntTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validator.ValidateStruct(uIntTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "ltefield", c) + + err3 = validator.ValidateFieldByTagAndValue(uint(5), uint(1), "ltefield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(uint(1), uint(5), "ltefield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "ltefield") + + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,ltefield=Val1"` + } + + floatTest := &FloatTest{ + Val1: 5, + Val2: 1, + } + + errs = validator.ValidateStruct(floatTest) + c.Assert(errs, IsNil) + + floatTest = &FloatTest{ + Val1: 1, + Val2: 5, + } + + errs2 = validator.ValidateStruct(floatTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "ltefield", c) + + err3 = validator.ValidateFieldByTagAndValue(float32(5), float32(1), "ltefield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(float32(1), float32(5), "ltefield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "ltefield") + + c.Assert(func() { validator.ValidateFieldByTagAndValue(nil, 5, "ltefield") }, PanicMatches, "struct not passed for cross validation") + c.Assert(func() { validator.ValidateFieldByTagAndValue(1, "T", "ltefield") }, PanicMatches, "Bad field type string") + c.Assert(func() { validator.ValidateFieldByTagAndValue(1, end, "ltefield") }, PanicMatches, "Bad Top Level field type") + + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,ltefield=NonExistantField"` + } + + timeTest2 := &TimeTest2{ + Start: &end, + End: &start, + } + + c.Assert(func() { validator.ValidateStruct(timeTest2) }, PanicMatches, "Field \"NonExistantField\" not found in struct") +} + +func (ms *MySuite) TestGteField(c *C) { + + type TimeTest struct { + Start *time.Time `validate:"required,gte"` + End *time.Time `validate:"required,gte,gtefield=Start"` + } + + now := time.Now() + start := now.Add(time.Hour * 24) + end := start.Add(time.Hour * 24) + + timeTest := &TimeTest{ + Start: &start, + End: &end, + } + + errs := validator.ValidateStruct(timeTest) + c.Assert(errs, IsNil) + + timeTest = &TimeTest{ + Start: &end, + End: &start, + } + + errs2 := validator.ValidateStruct(timeTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "End", "gtefield", c) + + err3 := validator.ValidateFieldByTagAndValue(&start, &end, "gtefield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(&end, &start, "gtefield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "gtefield") + + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,gtefield=Val1"` + } + + intTest := &IntTest{ + Val1: 1, + Val2: 5, + } + + errs = validator.ValidateStruct(intTest) + c.Assert(errs, IsNil) + + intTest = &IntTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validator.ValidateStruct(intTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "gtefield", c) + + err3 = validator.ValidateFieldByTagAndValue(int(1), int(5), "gtefield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(int(5), int(1), "gtefield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "gtefield") + + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,gtefield=Val1"` + } + + uIntTest := &UIntTest{ + Val1: 1, + Val2: 5, + } + + errs = validator.ValidateStruct(uIntTest) + c.Assert(errs, IsNil) + + uIntTest = &UIntTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validator.ValidateStruct(uIntTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "gtefield", c) + + err3 = validator.ValidateFieldByTagAndValue(uint(1), uint(5), "gtefield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(uint(5), uint(1), "gtefield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "gtefield") + + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,gtefield=Val1"` + } + + floatTest := &FloatTest{ + Val1: 1, + Val2: 5, + } + + errs = validator.ValidateStruct(floatTest) + c.Assert(errs, IsNil) + + floatTest = &FloatTest{ + Val1: 5, + Val2: 1, + } + + errs2 = validator.ValidateStruct(floatTest).Flatten() + c.Assert(errs2, NotNil) + AssertMapFieldError(errs2, "Val2", "gtefield", c) + + err3 = validator.ValidateFieldByTagAndValue(float32(1), float32(5), "gtefield") + c.Assert(err3, IsNil) + + err3 = validator.ValidateFieldByTagAndValue(float32(5), float32(1), "gtefield") + c.Assert(err3, NotNil) + c.Assert(err3.ErrorTag, Equals, "gtefield") + + c.Assert(func() { validator.ValidateFieldByTagAndValue(nil, 1, "gtefield") }, PanicMatches, "struct not passed for cross validation") + c.Assert(func() { validator.ValidateFieldByTagAndValue(5, "T", "gtefield") }, PanicMatches, "Bad field type string") + c.Assert(func() { validator.ValidateFieldByTagAndValue(5, start, "gtefield") }, PanicMatches, "Bad Top Level field type") + + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,gtefield=NonExistantField"` + } + + timeTest2 := &TimeTest2{ + Start: &start, + End: &end, + } + + c.Assert(func() { validator.ValidateStruct(timeTest2) }, PanicMatches, "Field \"NonExistantField\" not found in struct") +} + func (ms *MySuite) TestValidateByTagAndValue(c *C) { val := "test"