diff --git a/baked_in.go b/baked_in.go index 1fa65c2..464969c 100644 --- a/baked_in.go +++ b/baked_in.go @@ -9,7 +9,7 @@ import ( var bakedInValidators = map[string]ValidationFunc{ "required": required, - "length": length, + "len": length, "min": min, "max": max, "regex": regex, @@ -17,7 +17,16 @@ var bakedInValidators = map[string]ValidationFunc{ func required(field interface{}, param string) bool { - return field != nil && field != reflect.Zero(reflect.TypeOf(field)).Interface() + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.Slice, reflect.Map, reflect.Array: + return field != nil && int64(st.Len()) > 0 + + default: + return field != nil && field != reflect.Zero(reflect.TypeOf(field)).Interface() + } } // length tests whether a variable's length is equal to a given diff --git a/validator.go b/validator.go index c85584d..2664663 100644 --- a/validator.go +++ b/validator.go @@ -1,9 +1,8 @@ /** * Package validator * - * NOTES: - * - * anonymous structs - they don't have names so expect the Struct name within StructValidationErrors to be blank + * MISC: + * - anonymous structs - they don't have names so expect the Struct name within StructValidationErrors to be blank * */ @@ -12,7 +11,6 @@ package validator import ( "errors" "fmt" - "log" "reflect" "strings" "unicode" @@ -152,7 +150,7 @@ func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors { } if structValue.Kind() != reflect.Struct { - log.Fatal("interface passed for validation is not a struct") + panic("interface passed for validation is not a struct") } var numFields = structValue.NumField() @@ -253,7 +251,7 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st switch valueField.Kind() { case reflect.Struct, reflect.Invalid: - log.Fatal("Invalid field passed to ValidateFieldWithTag") + panic("Invalid field passed to ValidateFieldWithTag") } valTags := strings.Split(tag, ",") @@ -264,7 +262,7 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st key := strings.Trim(vals[0], " ") if len(key) == 0 { - log.Fatalf("Invalid validation tag on field %s", name) + panic(fmt.Sprintf("Invalid validation tag on field %s", name)) } // OK to continue because we checked it's existance before getting into this loop @@ -274,7 +272,7 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st valFunc := v.validationFuncs[key] if valFunc == nil { - log.Fatalf("Undefined validation function on field %s", name) + panic(fmt.Sprintf("Undefined validation function on field %s", name)) } param := "" diff --git a/validator_test.go b/validator_test.go index ed12593..58375b4 100644 --- a/validator_test.go +++ b/validator_test.go @@ -2,93 +2,343 @@ package validator_test import ( "fmt" - "log" "testing" "github.com/joeybloggs/go-validate-yourself" + . "gopkg.in/check.v1" ) -// type UserDetails struct { -// Address string `validate:"omitempty,length=6"` -// Sub struct { -// A string `validate:"required"` -// } -// } +type SubTest struct { + Test string `validate:"required"` +} -type UserDetails struct { - Address string `validate:"omitempty,length=6"` - Sub struct { +type TestString struct { + Required string `validate:"required"` + Len string `validate:"len=10"` + Min string `validate:"min=1"` + Max string `validate:"max=10"` + MinMax string `validate:"min=1,max=10"` + OmitEmpty string `validate:"omitempty,min=1,max=10"` + Sub *SubTest + SubIgnore *SubTest `validate:"-"` + Anonymous struct { A string `validate:"required"` } } -type User struct { - FirstName string `validate:"required"` - Details *UserDetails -} - -// func Test(t *testing.T) { TestingT(t) } -// -// type MySuite struct{} -// -// var _ = Suite(&MySuite{}) -// -// func (s *MySuite) SetUpTest(c *C) { -// s.dir = c.MkDir() -// // Use s.dir to prepare some data. -// } - -// func RecursiveErrorReporter(e *validator.StructValidationErrors) { -// -// // log.Printf("Error within Struct:%s\n", e.Struct) -// -// // for _, f := range e.Errors { -// // log.Println(f.Error()) -// // } -// } - -func TestValidateStruct(t *testing.T) { - - u := &User{ - FirstName: "", - Details: &UserDetails{ - Address: "", - Sub: struct { - A string `validate:"required"` - }{ - A: "", - }, +type TestInt32 struct { + Required int `validate:"required"` + Len int `validate:"len=10"` + Min int `validate:"min=1"` + Max int `validate:"max=10"` + MinMax int `validate:"min=1,max=10"` + OmitEmpty int `validate:"omitempty,min=1,max=10"` +} + +type TestUint64 struct { + Required uint64 `validate:"required"` + Len uint64 `validate:"len=10"` + Min uint64 `validate:"min=1"` + Max uint64 `validate:"max=10"` + MinMax uint64 `validate:"min=1,max=10"` + OmitEmpty uint64 `validate:"omitempty,min=1,max=10"` +} + +type TestFloat64 struct { + Required int64 `validate:"required"` + Len int64 `validate:"len=10"` + Min int64 `validate:"min=1"` + Max int64 `validate:"max=10"` + MinMax int64 `validate:"min=1,max=10"` + OmitEmpty int64 `validate:"omitempty,min=1,max=10"` +} + +type TestSlice struct { + Required []int `validate:"required"` + Len []int `validate:"len=10"` + Min []int `validate:"min=1"` + Max []int `validate:"max=10"` + MinMax []int `validate:"min=1,max=10"` + OmitEmpty []int `validate:"omitempty,min=1,max=10"` +} + +func Test(t *testing.T) { TestingT(t) } + +type MySuite struct{} + +var _ = Suite(&MySuite{}) + +func AssetStruct(s *validator.StructValidationErrors, structFieldName string, expectedStructName string, c *C) *validator.StructValidationErrors { + + val, ok := s.StructErrors[structFieldName] + c.Assert(ok, Equals, true) + c.Assert(val, NotNil) + c.Assert(val.Struct, Equals, expectedStructName) + + return val +} + +func AssertFieldError(s *validator.StructValidationErrors, field string, expectedTag string, c *C) { + + val, ok := s.Errors[field] + c.Assert(ok, Equals, true) + c.Assert(val, NotNil) + c.Assert(val.Field, Equals, field) + c.Assert(val.ErrorTag, Equals, expectedTag) +} + +func (ms *MySuite) TestStructStringValidation(c *C) { + + tSuccess := &TestString{ + Required: "Required", + Len: "length==10", + Min: "min=1", + Max: "1234567890", + MinMax: "12345", + OmitEmpty: "", + Sub: &SubTest{ + Test: "1", + }, + SubIgnore: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "1", }, } - errors := validator.ValidateStruct(u) - - fmt.Println(errors == nil) - log.Println(errors.Error()) - - // for _, i := range errors { - // fmt.Printf("Error Struct:%s\n", i.Struct) - // - // for _, j := range i.Errors { - // - // fmt.Printf("Error Field:%s Error Tag:%s\n", j.Field, j.ErrorTag) - // fmt.Println(j.Error()) - // } - // } - -} - -// func TestValidateField(t *testing.T) { -// -// u := &User{ -// FirstName: "Dean Karn", -// Details: &UserDetails{ -// "26 Here Blvd.", -// }, -// } -// -// err := validator.ValidateFieldByTag(u.FirstName, "required") -// -// fmt.Println(err == nil) -// fmt.Println(err) -// } + err := validator.ValidateStruct(tSuccess) + c.Assert(err, IsNil) + + tFail := &TestString{ + Required: "", + Len: "", + Min: "", + Max: "12345678901", + MinMax: "", + OmitEmpty: "12345678901", + Sub: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "", + }, + } + + err = validator.ValidateStruct(tFail) + + // Assert Top Level + c.Assert(err.Struct, Equals, "TestString") + c.Assert(len(err.Errors), Equals, 6) + c.Assert(len(err.StructErrors), Equals, 2) + + // Assert Fields + AssertFieldError(err, "Required", "required", c) + AssertFieldError(err, "Len", "len", c) + AssertFieldError(err, "Min", "min", c) + AssertFieldError(err, "Max", "max", c) + AssertFieldError(err, "MinMax", "min", c) + AssertFieldError(err, "OmitEmpty", "max", c) + + // Assert Anonymous embedded struct + AssetStruct(err, "Anonymous", "", c) + + // Assert SubTest embedded struct + val := AssetStruct(err, "Sub", "SubTest", c) + c.Assert(len(val.Errors), Equals, 1) + c.Assert(len(val.StructErrors), Equals, 0) + + AssertFieldError(val, "Test", "required", c) +} + +func (ms *MySuite) TestStructInt32Validation(c *C) { + + tSuccess := &TestInt32{ + Required: 1, + Len: 10, + Min: 1, + Max: 10, + MinMax: 5, + OmitEmpty: 0, + } + + err := validator.ValidateStruct(tSuccess) + c.Assert(err, IsNil) + + tFail := &TestInt32{ + Required: 0, + Len: 11, + Min: -1, + Max: 11, + MinMax: -1, + OmitEmpty: 11, + } + + err = validator.ValidateStruct(tFail) + + // Assert Top Level + c.Assert(err.Struct, Equals, "TestInt32") + c.Assert(len(err.Errors), Equals, 6) + c.Assert(len(err.StructErrors), Equals, 0) + + // Assert Fields + AssertFieldError(err, "Required", "required", c) + AssertFieldError(err, "Len", "len", c) + AssertFieldError(err, "Min", "min", c) + AssertFieldError(err, "Max", "max", c) + AssertFieldError(err, "MinMax", "min", c) + AssertFieldError(err, "OmitEmpty", "max", c) +} + +func (ms *MySuite) TestStructUint64Validation(c *C) { + + tSuccess := &TestUint64{ + Required: 1, + Len: 10, + Min: 1, + Max: 10, + MinMax: 5, + OmitEmpty: 0, + } + + err := validator.ValidateStruct(tSuccess) + c.Assert(err, IsNil) + + tFail := &TestUint64{ + Required: 0, + Len: 11, + Min: 0, + Max: 11, + MinMax: 0, + OmitEmpty: 11, + } + + err = validator.ValidateStruct(tFail) + + // Assert Top Level + c.Assert(err.Struct, Equals, "TestUint64") + c.Assert(len(err.Errors), Equals, 6) + c.Assert(len(err.StructErrors), Equals, 0) + + // Assert Fields + AssertFieldError(err, "Required", "required", c) + AssertFieldError(err, "Len", "len", c) + AssertFieldError(err, "Min", "min", c) + AssertFieldError(err, "Max", "max", c) + AssertFieldError(err, "MinMax", "min", c) + AssertFieldError(err, "OmitEmpty", "max", c) +} + +func (ms *MySuite) TestStructFloat64Validation(c *C) { + + tSuccess := &TestFloat64{ + Required: 1, + Len: 10, + Min: 1, + Max: 10, + MinMax: 5, + OmitEmpty: 0, + } + + err := validator.ValidateStruct(tSuccess) + c.Assert(err, IsNil) + + tFail := &TestFloat64{ + Required: 0, + Len: 11, + Min: 0, + Max: 11, + MinMax: 0, + OmitEmpty: 11, + } + + err = validator.ValidateStruct(tFail) + + // Assert Top Level + c.Assert(err.Struct, Equals, "TestFloat64") + c.Assert(len(err.Errors), Equals, 6) + c.Assert(len(err.StructErrors), Equals, 0) + + // Assert Fields + AssertFieldError(err, "Required", "required", c) + AssertFieldError(err, "Len", "len", c) + AssertFieldError(err, "Min", "min", c) + AssertFieldError(err, "Max", "max", c) + AssertFieldError(err, "MinMax", "min", c) + AssertFieldError(err, "OmitEmpty", "max", c) +} + +func (ms *MySuite) TestStructSliceValidation(c *C) { + + tSuccess := &TestSlice{ + Required: []int{1}, + Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + Min: []int{1, 2}, + Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + MinMax: []int{1, 2, 3, 4, 5}, + OmitEmpty: []int{}, + } + + err := validator.ValidateStruct(tSuccess) + c.Assert(err, IsNil) + + tFail := &TestSlice{ + Required: []int{}, + Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, + Min: []int{}, + Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, + MinMax: []int{}, + OmitEmpty: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, + } + + err = validator.ValidateStruct(tFail) + + // Assert Top Level + c.Assert(err.Struct, Equals, "TestSlice") + c.Assert(len(err.Errors), Equals, 6) + c.Assert(len(err.StructErrors), Equals, 0) + + // Assert Fields + AssertFieldError(err, "Required", "required", c) + AssertFieldError(err, "Len", "len", c) + AssertFieldError(err, "Min", "min", c) + AssertFieldError(err, "Max", "max", c) + AssertFieldError(err, "MinMax", "min", c) + AssertFieldError(err, "OmitEmpty", "max", c) +} + +func (ms *MySuite) TestInvalidStruct(c *C) { + s := &SubTest{ + Test: "1", + } + + c.Assert(func() { validator.ValidateStruct(s.Test) }, PanicMatches, "interface passed for validation is not a struct") +} + +func (ms *MySuite) TestInvalidField(c *C) { + s := &SubTest{ + Test: "1", + } + + c.Assert(func() { validator.ValidateFieldByTag(s, "required") }, PanicMatches, "Invalid field passed to ValidateFieldWithTag") +} + +func (ms *MySuite) TestInvalidTagField(c *C) { + s := &SubTest{ + Test: "1", + } + + c.Assert(func() { validator.ValidateFieldByTag(s.Test, "") }, PanicMatches, fmt.Sprintf("Invalid validation tag on field %s", "")) +} + +func (ms *MySuite) TestInvalidValidatorFunction(c *C) { + s := &SubTest{ + Test: "1", + } + + c.Assert(func() { validator.ValidateFieldByTag(s.Test, "zzxxBadFunction") }, PanicMatches, fmt.Sprintf("Undefined validation function on field %s", "")) +}