From 1e1442d02cca409356a1eb7e1f40b12010e296f5 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Wed, 8 Apr 2015 18:23:20 -0400 Subject: [PATCH] merge changes from Pull Request #24 from @manucorporat --- baked_in.go | 144 +++++++--------------------------------------- doc.go | 18 +++--- regexes.go | 7 ++- validator.go | 61 ++++++++++---------- validator_test.go | 22 +++---- 5 files changed, 78 insertions(+), 174 deletions(-) diff --git a/baked_in.go b/baked_in.go index dfab14e..a3795ab 100644 --- a/baked_in.go +++ b/baked_in.go @@ -8,8 +8,9 @@ import ( "time" ) -// BakedInValidators is the map of ValidationFunc used internally -// but can be used with any new Validator if desired +// BakedInValidators is the default map of ValidationFunc +// you can add, remove or even replace items to suite your needs, +// or even disregard and use your own map if so desired. var BakedInValidators = map[string]ValidationFunc{ "required": hasValue, "len": hasLengthOf, @@ -54,7 +55,6 @@ func isURI(top interface{}, current interface{}, field interface{}, param string } func isURL(top interface{}, current interface{}, field interface{}, param string) bool { - st := reflect.ValueOf(field) switch st.Kind() { @@ -77,146 +77,47 @@ func isURL(top interface{}, current interface{}, field interface{}, param string } func isEmail(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { - - case reflect.String: - return emailRegex.MatchString(field.(string)) - } - - panic(fmt.Sprintf("Bad field type %T", field)) + return matchesRegex(emailRegex, field) } func isHsla(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { - - case reflect.String: - return hslaRegex.MatchString(field.(string)) - } - - panic(fmt.Sprintf("Bad field type %T", field)) + return matchesRegex(hslaRegex, field) } func isHsl(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { - - case reflect.String: - return hslRegex.MatchString(field.(string)) - } - - panic(fmt.Sprintf("Bad field type %T", field)) + return matchesRegex(hslRegex, field) } func isRgba(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { - - case reflect.String: - return rgbaRegex.MatchString(field.(string)) - } - - panic(fmt.Sprintf("Bad field type %T", field)) + return matchesRegex(rgbaRegex, field) } func isRgb(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { - - case reflect.String: - return rgbRegex.MatchString(field.(string)) - } - - panic(fmt.Sprintf("Bad field type %T", field)) + return matchesRegex(rgbRegex, field) } func isHexcolor(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { - - case reflect.String: - return hexcolorRegex.MatchString(field.(string)) - } - - panic(fmt.Sprintf("Bad field type %T", field)) + return matchesRegex(hexcolorRegex, field) } func isHexadecimal(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { - - case reflect.String: - return hexadecimalRegex.MatchString(field.(string)) - } - - panic(fmt.Sprintf("Bad field type %T", field)) + return matchesRegex(hexadecimalRegex, field) } func isNumber(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { - - case reflect.String: - return numberRegex.MatchString(field.(string)) - } - - panic(fmt.Sprintf("Bad field type %T", field)) + return matchesRegex(numberRegex, field) } func isNumeric(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { - - case reflect.String: - return numericRegex.MatchString(field.(string)) - } - - panic(fmt.Sprintf("Bad field type %T", field)) + return matchesRegex(numericRegex, field) } func isAlphanum(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { - - case reflect.String: - return alphaNumericRegex.MatchString(field.(string)) - } - - panic(fmt.Sprintf("Bad field type %T", field)) + return matchesRegex(alphaNumericRegex, field) } func isAlpha(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { - - case reflect.String: - return alphaRegex.MatchString(field.(string)) - } - - panic(fmt.Sprintf("Bad field type %T", field)) + return matchesRegex(alphaRegex, field) } func hasValue(top interface{}, current interface{}, field interface{}, param string) bool { @@ -767,10 +668,7 @@ func hasMaxOf(top interface{}, current interface{}, field interface{}, param str func asInt(param string) int64 { i, err := strconv.ParseInt(param, 0, 64) - - if err != nil { - panic(err.Error()) - } + panicIf(err) return i } @@ -780,10 +678,7 @@ func asInt(param string) int64 { func asUint(param string) uint64 { i, err := strconv.ParseUint(param, 0, 64) - - if err != nil { - panic(err.Error()) - } + panicIf(err) return i } @@ -793,10 +688,13 @@ func asUint(param string) uint64 { func asFloat(param string) float64 { i, err := strconv.ParseFloat(param, 64) + panicIf(err) + return i +} + +func panicIf(err error) { if err != nil { panic(err.Error()) } - - return i } diff --git a/doc.go b/doc.go index b0304bf..6f30a72 100644 --- a/doc.go +++ b/doc.go @@ -1,10 +1,8 @@ /* -Package validator implements value validations for structs and individual fields based on tags. +Package validator implements value validations for structs and individual fields based on tags. It can also handle Cross Field validation and even Cross Field Cross Struct validation for nested structs. Built In Validator - v3 no longer contains a built in Validator instance. - myValidator = validator.NewValidator("validate", validator.BakedInValidators) errs := myValidator.ValidateStruct(//your struct) @@ -36,6 +34,9 @@ A simple example usage: errs.StructErrors will be empty <-- fields that were structs errs.Errors will have 1 error of type FieldValidationError + NOTE: Anonymous Structs - they don't have names so expect the Struct name + within StructValidationErrors to be blank. + Error Handling The error can be used like so @@ -103,9 +104,9 @@ Cross Field Validation can be implemented, for example Start & End Date range va // when calling myValidator.ValidateFieldByTag(field, tag) val will be nil // // Because of the specific requirements and field names within each persons project that - // uses this library it is likely that custom functions will need to be created. - // however there are some build in Generic Cross Field validation, see Baked In Validators and - // Tags below + // uses this library it is likely that custom functions will need to be created for your + // Cross Field Validation needs, however there are some build in Generic Cross Field validations, + // see Baked In Validators and Tags below func isDateRangeValid(val interface{}, field interface{}, param string) bool { @@ -307,10 +308,11 @@ Validator notes: a regex which conflict with the validation definitions, although workarounds can be made, they take away from using pure regex's. Furthermore it's quick and dirty but the regex's become harder to maintain and are not reusable, so - it's as much as a programming philosiphy as anything. + it's as much a programming philosiphy as anything. In place of this new validator functions should be created; a regex can be - used within the validator function and even be precompiled for better efficiency. + used within the validator function and even be precompiled for better efficiency + within regexes.go. And the best reason, you can sumit a pull request and we can keep on adding to the validation library of this package! diff --git a/regexes.go b/regexes.go index 881d57f..8107a21 100644 --- a/regexes.go +++ b/regexes.go @@ -14,8 +14,6 @@ const ( hslRegexString = "^hsl\\(\\s*(0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*\\)$" hslaRegexString = "^hsla\\(\\s*(0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0.[1-9]*)|[01])\\s*\\)$" emailRegexString = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" - // urlRegexString = `^((ftp|http|https):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|((www\.)?)?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?_?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?)|localhost)(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$` - // urlRegexString = "^(?:(?:https?|ftp):\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\\.(?:[a-z\u00a1-\uffff]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?$" ) var ( @@ -31,3 +29,8 @@ var ( hslaRegex = regexp.MustCompile(hslaRegexString) emailRegex = regexp.MustCompile(emailRegexString) ) + +func matchesRegex(regex *regexp.Regexp, field interface{}) bool { + fieldAsString := field.(string) //this will panic inherently + return regex.MatchString(fieldAsString) +} diff --git a/validator.go b/validator.go index b0952c5..f8f2a3f 100644 --- a/validator.go +++ b/validator.go @@ -9,6 +9,7 @@ package validator import ( + "bytes" "errors" "fmt" "reflect" @@ -28,7 +29,8 @@ const ( validationStructErrMsg = "Struct:%s\n" ) -// FieldValidationError contains a single fields validation error +// FieldValidationError contains a single field's validation error along +// with other properties that may be needed for error message creation type FieldValidationError struct { Field string ErrorTag string @@ -44,7 +46,8 @@ func (e *FieldValidationError) Error() string { return fmt.Sprintf(validationFieldErrMsg, e.Field, e.ErrorTag) } -// StructValidationErrors is hierarchical list of field and struct errors +// StructValidationErrors is hierarchical list of field and struct validation errors +// for a non hierarchical representation please see the Flatten method for StructValidationErrors type StructValidationErrors struct { // Name of the Struct Struct string @@ -58,21 +61,20 @@ type StructValidationErrors struct { // This is intended for use in development + debugging and not intended to be a production error message. // it also allows StructValidationErrors to be used as an Error interface func (e *StructValidationErrors) Error() string { - - s := fmt.Sprintf(validationStructErrMsg, e.Struct) + buff := bytes.NewBufferString(fmt.Sprintf(validationStructErrMsg, e.Struct)) for _, err := range e.Errors { - s += err.Error() + buff.WriteString(err.Error()) } - for _, sErr := range e.StructErrors { - s += sErr.Error() + for _, err := range e.StructErrors { + buff.WriteString(err.Error()) } - - return fmt.Sprintf("%s\n\n", s) + buff.WriteString("\n\n") + return buff.String() } -// Flatten flattens the StructValidationErrors hierarchical sctructure into a flat namespace style field name +// Flatten flattens the StructValidationErrors hierarchical structure into a flat namespace style field name // for those that want/need it func (e *StructValidationErrors) Flatten() map[string]*FieldValidationError { @@ -110,17 +112,16 @@ type ValidationFunc func(top interface{}, current interface{}, f interface{}, pa // Validator implements the Validator Struct // NOTE: Fields within are not thread safe and that is on purpose -// Functions Tags etc. should all be predifined before use, so subscribe to the philosiphy +// Functions and Tags should all be predifined before use, so subscribe to the philosiphy // or make it thread safe on your end type Validator struct { - // TagName being used. + // tagName being used. tagName string // validationFuncs is a map of validation functions and the tag keys validationFuncs map[string]ValidationFunc } -// NewValidator creates a new Validator instance -// NOTE: it is not necessary to create a new validator as the internal one will do in 99.9% of cases, but the option is there. +// NewValidator creates a new Validator instance for use. func NewValidator(tagName string, funcs map[string]ValidationFunc) *Validator { return &Validator{ tagName: tagName, @@ -128,12 +129,14 @@ func NewValidator(tagName string, funcs map[string]ValidationFunc) *Validator { } } -// SetTag sets tagName of the Validator to one of your choosing +// SetTag sets tagName of the Validator to one of your choosing after creation +// perhaps to dodge a tag name conflict in a specific section of code func (v *Validator) SetTag(tagName string) { v.tagName = tagName } // AddFunction adds a ValidationFunc to a Validator's map of validators denoted by the key +// NOTE: if the key already exists, it will get replaced. func (v *Validator) AddFunction(key string, f ValidationFunc) error { if len(key) == 0 { @@ -141,38 +144,30 @@ func (v *Validator) AddFunction(key string, f ValidationFunc) error { } if f == nil { - return errors.New("Function Key cannot be empty") + return errors.New("Function cannot be empty") } - // Commented out to allow overwritting of Baked In Function if so desired. - // if v.ValidationFuncs[key] != nil { - // return errors.New(fmt.Sprintf("Validation Function with key: %s already exists.", key)) - // } - v.validationFuncs[key] = f return nil } -// ValidateStruct validates a struct and returns a struct containing the errors +// ValidateStruct validates a struct, even it's nested structs, and returns a struct containing the errors +// NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good +// way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to +// the Array or Map. func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors { return v.validateStructRecursive(s, s, s) } -// validateStructRecursive validates a struct recursivly and passes the top level struct around for use in validator functions and returns a struct containing the errors +// validateStructRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors func (v *Validator) validateStructRecursive(top interface{}, current interface{}, s interface{}) *StructValidationErrors { structValue := reflect.ValueOf(s) structType := reflect.TypeOf(s) structName := structType.Name() - validationErrors := &StructValidationErrors{ - Struct: structName, - Errors: map[string]*FieldValidationError{}, - StructErrors: map[string]*StructValidationErrors{}, - } - if structValue.Kind() == reflect.Ptr && !structValue.IsNil() { return v.validateStructRecursive(top, current, structValue.Elem().Interface()) } @@ -181,6 +176,12 @@ func (v *Validator) validateStructRecursive(top interface{}, current interface{} panic("interface passed for validation is not a struct") } + validationErrors := &StructValidationErrors{ + Struct: structName, + Errors: map[string]*FieldValidationError{}, + StructErrors: map[string]*StructValidationErrors{}, + } + var numFields = structValue.NumField() for i := 0; i < numFields; i++ { @@ -255,7 +256,7 @@ func (v *Validator) ValidateFieldByTag(f interface{}, tag string) *FieldValidati return v.ValidateFieldByTagAndValue(nil, f, tag) } -// ValidateFieldByTagAndValue allows validation of a single field, still using tag style validation to check multiple errors +// ValidateFieldByTagAndValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors func (v *Validator) ValidateFieldByTagAndValue(val interface{}, f interface{}, tag string) *FieldValidationError { return v.validateFieldByNameAndTagAndValue(nil, val, f, "", tag) diff --git a/validator_test.go b/validator_test.go index 4f66dc0..89f0003 100644 --- a/validator_test.go +++ b/validator_test.go @@ -1163,7 +1163,7 @@ func (ms *MySuite) TestHsla(c *C) { c.Assert(err.ErrorTag, Equals, "hsla") i := 1 - c.Assert(func() { myValidator.ValidateFieldByTag(i, "hsla") }, PanicMatches, "Bad field type int") + c.Assert(func() { myValidator.ValidateFieldByTag(i, "hsla") }, PanicMatches, "interface conversion: interface is int, not string") } func (ms *MySuite) TestHsl(c *C) { @@ -1197,7 +1197,7 @@ func (ms *MySuite) TestHsl(c *C) { c.Assert(err.ErrorTag, Equals, "hsl") i := 1 - c.Assert(func() { myValidator.ValidateFieldByTag(i, "hsl") }, PanicMatches, "Bad field type int") + c.Assert(func() { myValidator.ValidateFieldByTag(i, "hsl") }, PanicMatches, "interface conversion: interface is int, not string") } func (ms *MySuite) TestRgba(c *C) { @@ -1230,7 +1230,7 @@ func (ms *MySuite) TestRgba(c *C) { c.Assert(err.ErrorTag, Equals, "rgba") i := 1 - c.Assert(func() { myValidator.ValidateFieldByTag(i, "rgba") }, PanicMatches, "Bad field type int") + c.Assert(func() { myValidator.ValidateFieldByTag(i, "rgba") }, PanicMatches, "interface conversion: interface is int, not string") } func (ms *MySuite) TestRgb(c *C) { @@ -1259,7 +1259,7 @@ func (ms *MySuite) TestRgb(c *C) { c.Assert(err.ErrorTag, Equals, "rgb") i := 1 - c.Assert(func() { myValidator.ValidateFieldByTag(i, "rgb") }, PanicMatches, "Bad field type int") + c.Assert(func() { myValidator.ValidateFieldByTag(i, "rgb") }, PanicMatches, "interface conversion: interface is int, not string") } func (ms *MySuite) TestEmail(c *C) { @@ -1289,7 +1289,7 @@ func (ms *MySuite) TestEmail(c *C) { c.Assert(err.ErrorTag, Equals, "email") i := true - c.Assert(func() { myValidator.ValidateFieldByTag(i, "email") }, PanicMatches, "Bad field type bool") + c.Assert(func() { myValidator.ValidateFieldByTag(i, "email") }, PanicMatches, "interface conversion: interface is bool, not string") } func (ms *MySuite) TestHexColor(c *C) { @@ -1313,7 +1313,7 @@ func (ms *MySuite) TestHexColor(c *C) { c.Assert(err.ErrorTag, Equals, "hexcolor") i := true - c.Assert(func() { myValidator.ValidateFieldByTag(i, "hexcolor") }, PanicMatches, "Bad field type bool") + c.Assert(func() { myValidator.ValidateFieldByTag(i, "hexcolor") }, PanicMatches, "interface conversion: interface is bool, not string") } func (ms *MySuite) TestHexadecimal(c *C) { @@ -1328,7 +1328,7 @@ func (ms *MySuite) TestHexadecimal(c *C) { c.Assert(err.ErrorTag, Equals, "hexadecimal") i := true - c.Assert(func() { myValidator.ValidateFieldByTag(i, "hexadecimal") }, PanicMatches, "Bad field type bool") + c.Assert(func() { myValidator.ValidateFieldByTag(i, "hexadecimal") }, PanicMatches, "interface conversion: interface is bool, not string") } func (ms *MySuite) TestNumber(c *C) { @@ -1373,7 +1373,7 @@ func (ms *MySuite) TestNumber(c *C) { c.Assert(err.ErrorTag, Equals, "number") i := 1 - c.Assert(func() { myValidator.ValidateFieldByTag(i, "number") }, PanicMatches, "Bad field type int") + c.Assert(func() { myValidator.ValidateFieldByTag(i, "number") }, PanicMatches, "interface conversion: interface is int, not string") } func (ms *MySuite) TestNumeric(c *C) { @@ -1413,7 +1413,7 @@ func (ms *MySuite) TestNumeric(c *C) { c.Assert(err.ErrorTag, Equals, "numeric") i := 1 - c.Assert(func() { myValidator.ValidateFieldByTag(i, "numeric") }, PanicMatches, "Bad field type int") + c.Assert(func() { myValidator.ValidateFieldByTag(i, "numeric") }, PanicMatches, "interface conversion: interface is int, not string") } func (ms *MySuite) TestAlphaNumeric(c *C) { @@ -1427,7 +1427,7 @@ func (ms *MySuite) TestAlphaNumeric(c *C) { c.Assert(err, NotNil) c.Assert(err.ErrorTag, Equals, "alphanum") - c.Assert(func() { myValidator.ValidateFieldByTag(1, "alphanum") }, PanicMatches, "Bad field type int") + c.Assert(func() { myValidator.ValidateFieldByTag(1, "alphanum") }, PanicMatches, "interface conversion: interface is int, not string") } func (ms *MySuite) TestAlpha(c *C) { @@ -1441,7 +1441,7 @@ func (ms *MySuite) TestAlpha(c *C) { c.Assert(err, NotNil) c.Assert(err.ErrorTag, Equals, "alpha") - c.Assert(func() { myValidator.ValidateFieldByTag(1, "alpha") }, PanicMatches, "Bad field type int") + c.Assert(func() { myValidator.ValidateFieldByTag(1, "alpha") }, PanicMatches, "interface conversion: interface is int, not string") } func (ms *MySuite) TestFlattening(c *C) {