Merge pull request #91 from bluesuncorp/v5

merge latest changes from v5
pull/115/head
Dean Karn 9 years ago
commit 5ca3cffc8c
  1. 2
      doc.go
  2. 255
      validator.go
  3. 403
      validator_test.go

@ -176,7 +176,7 @@ Here is a list of the current built in validators:
dive
This tells the validator to dive into a slice, array or map and validate that
level of the slice, array or map with the validation tags that follow.
Multidimensional nesting is also supported, each level you with to dive will
Multidimensional nesting is also supported, each level you wish to dive will
require another dive tag. (Usage: dive)
Example: [][]string with validation tag "gt=0,dive,len=1,dive,required"
gt=0 will be applied to []

@ -20,20 +20,19 @@ import (
)
const (
utf8HexComma = "0x2C"
tagSeparator = ","
orSeparator = "|"
noValidationTag = "-"
tagKeySeparator = "="
structOnlyTag = "structonly"
omitempty = "omitempty"
required = "required"
fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag"
sliceErrMsg = "Field validation for \"%s\" failed at index \"%d\" with error(s): %s"
mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s"
structErrMsg = "Struct:%s\n"
diveTag = "dive"
// diveSplit = "," + diveTag
utf8HexComma = "0x2C"
tagSeparator = ","
orSeparator = "|"
noValidationTag = "-"
tagKeySeparator = "="
structOnlyTag = "structonly"
omitempty = "omitempty"
required = "required"
fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag"
sliceErrMsg = "Field validation for \"%s\" failed at index \"%d\" with error(s): %s"
mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s"
structErrMsg = "Struct:%s\n"
diveTag = "dive"
arrayIndexFieldName = "%s[%d]"
mapIndexFieldName = "%s[%v]"
)
@ -193,6 +192,82 @@ func (e *FieldError) Error() string {
return fmt.Sprintf(fieldErrMsg, e.Field, e.Tag)
}
// Flatten flattens the FieldError hierarchical structure into a flat namespace style field name
// for those that want/need it.
// This is now needed because of the new dive functionality
func (e *FieldError) Flatten() map[string]*FieldError {
errs := map[string]*FieldError{}
if e.IsPlaceholderErr {
if e.IsSliceOrArray {
for key, err := range e.SliceOrArrayErrs {
fe, ok := err.(*FieldError)
if ok {
if flat := fe.Flatten(); flat != nil && len(flat) > 0 {
for k, v := range flat {
if fe.IsPlaceholderErr {
errs[fmt.Sprintf("[%#v]%s", key, k)] = v
} else {
errs[fmt.Sprintf("[%#v]", key)] = v
}
}
}
} else {
se := err.(*StructErrors)
if flat := se.Flatten(); flat != nil && len(flat) > 0 {
for k, v := range flat {
errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v
}
}
}
}
}
if e.IsMap {
for key, err := range e.MapErrs {
fe, ok := err.(*FieldError)
if ok {
if flat := fe.Flatten(); flat != nil && len(flat) > 0 {
for k, v := range flat {
if fe.IsPlaceholderErr {
errs[fmt.Sprintf("[%#v]%s", key, k)] = v
} else {
errs[fmt.Sprintf("[%#v]", key)] = v
}
}
}
} else {
se := err.(*StructErrors)
if flat := se.Flatten(); flat != nil && len(flat) > 0 {
for k, v := range flat {
errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v
}
}
}
}
}
return errs
}
errs[e.Field] = e
return errs
}
// StructErrors is hierarchical list of field and struct validation errors
// for a non hierarchical representation please see the Flatten method for StructErrors
type StructErrors struct {
@ -234,7 +309,17 @@ func (e *StructErrors) Flatten() map[string]*FieldError {
for _, f := range e.Errors {
errs[f.Field] = f
if flat := f.Flatten(); flat != nil && len(flat) > 0 {
for k, fe := range flat {
if f.IsPlaceholderErr {
errs[f.Field+k] = fe
} else {
errs[k] = fe
}
}
}
}
for key, val := range e.StructErrors {
@ -353,7 +438,6 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
structName = structType.Name()
numFields = structValue.NumField()
cs = &cachedStruct{name: structName, children: numFields}
structCache.Set(structType, cs)
}
validationErrors := structPool.Borrow()
@ -429,24 +513,62 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
continue
}
if valueField.Kind() == reflect.Ptr && valueField.IsNil() {
if (valueField.Kind() == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() {
if strings.Contains(cField.tag, omitempty) {
continue
goto CACHEFIELD
}
if strings.Contains(cField.tag, required) {
tags := strings.Split(cField.tag, tagSeparator)
if len(tags) > 0 {
var param string
vals := strings.SplitN(tags[0], tagKeySeparator, 2)
if len(vals) > 1 {
param = vals[1]
}
validationErrors.Errors[cField.name] = &FieldError{
Field: cField.name,
Tag: required,
Tag: vals[0],
Param: param,
Value: valueField.Interface(),
Kind: valueField.Kind(),
Type: valueField.Type(),
}
continue
goto CACHEFIELD
}
}
// if we get here, the field is interface and could be a struct or a field
// and we need to check the inner type and validate
if cField.kind == reflect.Interface {
valueField = valueField.Elem()
if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
valueField = valueField.Elem()
}
if valueField.Kind() == reflect.Struct {
goto VALIDATESTRUCT
}
// sending nil for cField as it was type interface and could be anything
// each time and so must be calculated each time and can't be cached reliably
if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, nil); fieldError != nil {
validationErrors.Errors[fieldError.Field] = fieldError
// free up memory reference
fieldError = nil
}
goto CACHEFIELD
}
VALIDATESTRUCT:
if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil {
validationErrors.StructErrors[cField.name] = structErrors
// free up memory map no longer needed
@ -486,11 +608,14 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
}
}
CACHEFIELD:
if !isCached {
cs.fields = append(cs.fields, cField)
}
}
structCache.Set(structType, cs)
if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
structPool.Return(validationErrors)
return nil
@ -709,19 +834,29 @@ func (v *Validate) traverseMap(val interface{}, current interface{}, valueField
continue
}
if idxField.Kind() == reflect.Ptr && idxField.IsNil() {
if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {
if strings.Contains(cField.tag, omitempty) {
if strings.Contains(cField.diveTag, omitempty) {
continue
}
if strings.Contains(cField.tag, required) {
tags := strings.Split(cField.diveTag, tagSeparator)
if len(tags) > 0 {
var param string
vals := strings.SplitN(tags[0], tagKeySeparator, 2)
if len(vals) > 1 {
param = vals[1]
}
errs[key.Interface()] = &FieldError{
Field: fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()),
Tag: required,
Tag: vals[0],
Param: param,
Value: idxField.Interface(),
Kind: reflect.Ptr,
Kind: idxField.Kind(),
Type: cField.mapSubtype,
}
}
@ -729,6 +864,30 @@ func (v *Validate) traverseMap(val interface{}, current interface{}, valueField
continue
}
// if we get here, the field is interface and could be a struct or a field
// and we need to check the inner type and validate
if idxField.Kind() == reflect.Interface {
idxField = idxField.Elem()
if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
idxField = idxField.Elem()
}
if idxField.Kind() == reflect.Struct {
goto VALIDATESTRUCT
}
// sending nil for cField as it was type interface and could be anything
// each time and so must be calculated each time and can't be cached reliably
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil {
errs[key.Interface()] = fieldError
}
continue
}
VALIDATESTRUCT:
if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil {
errs[key.Interface()] = structErrors
}
@ -768,19 +927,29 @@ func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, va
continue
}
if idxField.Kind() == reflect.Ptr && idxField.IsNil() {
if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {
if strings.Contains(cField.tag, omitempty) {
if strings.Contains(cField.diveTag, omitempty) {
continue
}
if strings.Contains(cField.tag, required) {
tags := strings.Split(cField.diveTag, tagSeparator)
if len(tags) > 0 {
var param string
vals := strings.SplitN(tags[0], tagKeySeparator, 2)
if len(vals) > 1 {
param = vals[1]
}
errs[i] = &FieldError{
Field: fmt.Sprintf(arrayIndexFieldName, cField.name, i),
Tag: required,
Tag: vals[0],
Param: param,
Value: idxField.Interface(),
Kind: reflect.Ptr,
Kind: idxField.Kind(),
Type: cField.sliceSubtype,
}
}
@ -788,6 +957,30 @@ func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, va
continue
}
// if we get here, the field is interface and could be a struct or a field
// and we need to check the inner type and validate
if idxField.Kind() == reflect.Interface {
idxField = idxField.Elem()
if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
idxField = idxField.Elem()
}
if idxField.Kind() == reflect.Struct {
goto VALIDATESTRUCT
}
// sending nil for cField as it was type interface and could be anything
// each time and so must be calculated each time and can't be cached reliably
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil {
errs[i] = fieldError
}
continue
}
VALIDATESTRUCT:
if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil {
errs[i] = structErrors
}

@ -226,6 +226,383 @@ func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, e
EqualSkip(t, 2, val.Tag, expectedTag)
}
func TestFlattenValidation(t *testing.T) {
type Inner struct {
Name string `validate:"required"`
}
type TestMultiDimensionalStructsPtr struct {
Errs [][]*Inner `validate:"gt=0,dive,dive,required"`
}
var errStructPtrArray [][]*Inner
errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{"ok"}})
tmsp := &TestMultiDimensionalStructsPtr{
Errs: errStructPtrArray,
}
errs := validate.Struct(tmsp)
NotEqual(t, errs, nil)
Equal(t, len(errs.Errors), 1)
// for full test coverage
fmt.Sprint(errs.Error())
fieldErr := errs.Errors["Errs"]
Equal(t, fieldErr.IsPlaceholderErr, true)
Equal(t, fieldErr.IsSliceOrArray, true)
Equal(t, fieldErr.Field, "Errs")
Equal(t, len(fieldErr.SliceOrArrayErrs), 1)
innerSlice1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError)
Equal(t, ok, true)
Equal(t, innerSlice1.IsPlaceholderErr, true)
Equal(t, innerSlice1.Field, "Errs[0]")
flatFieldErr, ok := fieldErr.Flatten()["[0][1].Inner.Name"]
Equal(t, ok, true)
Equal(t, flatFieldErr.Field, "Name")
Equal(t, flatFieldErr.Tag, "required")
structErrFlatten, ok := errs.Flatten()["Errs[0][1].Inner.Name"]
Equal(t, ok, true)
Equal(t, structErrFlatten.Field, "Name")
Equal(t, structErrFlatten.Tag, "required")
errStructPtrArray = [][]*Inner{}
errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, nil, &Inner{"ok"}})
tmsp = &TestMultiDimensionalStructsPtr{
Errs: errStructPtrArray,
}
errs = validate.Struct(tmsp)
NotEqual(t, errs, nil)
Equal(t, len(errs.Errors), 1)
// for full test coverage
fmt.Sprint(errs.Error())
fieldErr = errs.Errors["Errs"]
Equal(t, fieldErr.IsPlaceholderErr, true)
Equal(t, fieldErr.IsSliceOrArray, true)
Equal(t, fieldErr.Field, "Errs")
Equal(t, len(fieldErr.SliceOrArrayErrs), 1)
innerSlice1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError)
Equal(t, ok, true)
Equal(t, innerSlice1.IsPlaceholderErr, true)
Equal(t, innerSlice1.Field, "Errs[0]")
flatFieldErr, ok = fieldErr.Flatten()["[0][1]"]
Equal(t, ok, true)
Equal(t, flatFieldErr.Field, "Errs[0][1]")
Equal(t, flatFieldErr.Tag, "required")
type TestMapStructPtr struct {
Errs map[int]*Inner `validate:"gt=0,dive,required"`
}
mip := map[int]*Inner{0: &Inner{"ok"}, 3: &Inner{""}, 4: &Inner{"ok"}}
msp := &TestMapStructPtr{
Errs: mip,
}
errs = validate.Struct(msp)
NotEqual(t, errs, nil)
Equal(t, len(errs.Errors), 1)
fieldError := errs.Errors["Errs"]
Equal(t, fieldError.IsPlaceholderErr, true)
Equal(t, fieldError.IsMap, true)
Equal(t, len(fieldError.MapErrs), 1)
innerStructError, ok := fieldError.MapErrs[3].(*StructErrors)
Equal(t, ok, true)
Equal(t, innerStructError.Struct, "Inner")
Equal(t, len(innerStructError.Errors), 1)
innerInnerFieldError, ok := innerStructError.Errors["Name"]
Equal(t, ok, true)
Equal(t, innerInnerFieldError.IsPlaceholderErr, false)
Equal(t, innerInnerFieldError.IsSliceOrArray, false)
Equal(t, innerInnerFieldError.Field, "Name")
Equal(t, innerInnerFieldError.Tag, "required")
flatErrs, ok := errs.Flatten()["Errs[3].Inner.Name"]
Equal(t, ok, true)
Equal(t, flatErrs.Field, "Name")
Equal(t, flatErrs.Tag, "required")
mip2 := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}}
msp2 := &TestMapStructPtr{
Errs: mip2,
}
errs = validate.Struct(msp2)
NotEqual(t, errs, nil)
Equal(t, len(errs.Errors), 1)
fieldError = errs.Errors["Errs"]
Equal(t, fieldError.IsPlaceholderErr, true)
Equal(t, fieldError.IsMap, true)
Equal(t, len(fieldError.MapErrs), 1)
innerFieldError, ok := fieldError.MapErrs[3].(*FieldError)
Equal(t, ok, true)
Equal(t, innerFieldError.IsPlaceholderErr, false)
Equal(t, innerFieldError.IsSliceOrArray, false)
Equal(t, innerFieldError.Field, "Errs[3]")
Equal(t, innerFieldError.Tag, "required")
flatErrs, ok = errs.Flatten()["Errs[3]"]
Equal(t, ok, true)
Equal(t, flatErrs.Field, "Errs[3]")
Equal(t, flatErrs.Tag, "required")
type TestMapInnerArrayStruct struct {
Errs map[int][]string `validate:"gt=0,dive,dive,required"`
}
mias := map[int][]string{0: []string{"ok"}, 3: []string{"ok", ""}, 4: []string{"ok"}}
mia := &TestMapInnerArrayStruct{
Errs: mias,
}
errs = validate.Struct(mia)
NotEqual(t, errs, nil)
Equal(t, len(errs.Errors), 1)
flatErrs, ok = errs.Flatten()["Errs[3][1]"]
Equal(t, ok, true)
Equal(t, flatErrs.Field, "Errs[3][1]")
Equal(t, flatErrs.Tag, "required")
}
func TestInterfaceErrValidation(t *testing.T) {
var v1 interface{}
var v2 interface{}
v2 = 1
v1 = v2
err := validate.Field(v1, "len=1")
Equal(t, err, nil)
err = validate.Field(v2, "len=1")
Equal(t, err, nil)
type ExternalCMD struct {
Userid string `json:"userid"`
Action uint32 `json:"action"`
Data interface{} `json:"data,omitempty" validate:"required"`
}
s := &ExternalCMD{
Userid: "123456",
Action: 10000,
// Data: 1,
}
errs := validate.Struct(s)
NotEqual(t, errs, nil)
Equal(t, errs.Errors["Data"].Field, "Data")
Equal(t, errs.Errors["Data"].Tag, "required")
type ExternalCMD2 struct {
Userid string `json:"userid"`
Action uint32 `json:"action"`
Data interface{} `json:"data,omitempty" validate:"len=1"`
}
s2 := &ExternalCMD2{
Userid: "123456",
Action: 10000,
// Data: 1,
}
errs = validate.Struct(s2)
NotEqual(t, errs, nil)
Equal(t, errs.Errors["Data"].Field, "Data")
Equal(t, errs.Errors["Data"].Tag, "len")
Equal(t, errs.Errors["Data"].Param, "1")
s3 := &ExternalCMD2{
Userid: "123456",
Action: 10000,
Data: 2,
}
errs = validate.Struct(s3)
NotEqual(t, errs, nil)
Equal(t, errs.Errors["Data"].Field, "Data")
Equal(t, errs.Errors["Data"].Tag, "len")
Equal(t, errs.Errors["Data"].Param, "1")
type Inner struct {
Name string `validate:"required"`
}
inner := &Inner{
Name: "",
}
s4 := &ExternalCMD{
Userid: "123456",
Action: 10000,
Data: inner,
}
errs = validate.Struct(s4)
NotEqual(t, errs, nil)
Equal(t, errs.StructErrors["Data"].Struct, "Inner")
Equal(t, errs.StructErrors["Data"].Errors["Name"].Field, "Name")
Equal(t, errs.StructErrors["Data"].Errors["Name"].Tag, "required")
type TestMapStructPtr struct {
Errs map[int]interface{} `validate:"gt=0,dive,len=2"`
}
mip := map[int]interface{}{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}}
msp := &TestMapStructPtr{
Errs: mip,
}
errs = validate.Struct(msp)
NotEqual(t, errs, nil)
Equal(t, len(errs.Errors), 1)
fieldError := errs.Errors["Errs"]
Equal(t, fieldError.IsPlaceholderErr, true)
Equal(t, fieldError.IsMap, true)
Equal(t, len(fieldError.MapErrs), 1)
innerFieldError, ok := fieldError.MapErrs[3].(*FieldError)
Equal(t, ok, true)
Equal(t, innerFieldError.IsPlaceholderErr, false)
Equal(t, innerFieldError.IsMap, false)
Equal(t, len(innerFieldError.MapErrs), 0)
Equal(t, innerFieldError.Field, "Errs[3]")
Equal(t, innerFieldError.Tag, "len")
type TestMultiDimensionalStructs struct {
Errs [][]interface{} `validate:"gt=0,dive,dive,len=2"`
}
var errStructArray [][]interface{}
errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}})
errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}})
tms := &TestMultiDimensionalStructs{
Errs: errStructArray,
}
errs = validate.Struct(tms)
NotEqual(t, errs, nil)
Equal(t, len(errs.Errors), 1)
fieldErr, ok := errs.Errors["Errs"]
Equal(t, ok, true)
Equal(t, fieldErr.IsPlaceholderErr, true)
Equal(t, fieldErr.IsSliceOrArray, true)
Equal(t, len(fieldErr.SliceOrArrayErrs), 2)
sliceError1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError)
Equal(t, ok, true)
Equal(t, sliceError1.IsPlaceholderErr, true)
Equal(t, sliceError1.IsSliceOrArray, true)
Equal(t, len(sliceError1.SliceOrArrayErrs), 2)
innerSliceStructError1, ok := sliceError1.SliceOrArrayErrs[1].(*StructErrors)
Equal(t, ok, true)
Equal(t, len(innerSliceStructError1.Errors), 1)
innerInnersliceError1 := innerSliceStructError1.Errors["Name"]
Equal(t, innerInnersliceError1.IsPlaceholderErr, false)
Equal(t, innerInnersliceError1.IsSliceOrArray, false)
Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0)
type TestMultiDimensionalStructsPtr2 struct {
Errs [][]*Inner `validate:"gt=0,dive,dive,len=2"`
}
var errStructPtr2Array [][]*Inner
errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}})
errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}})
errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil})
tmsp2 := &TestMultiDimensionalStructsPtr2{
Errs: errStructPtr2Array,
}
errs = validate.Struct(tmsp2)
NotEqual(t, errs, nil)
Equal(t, len(errs.Errors), 1)
fieldErr, ok = errs.Errors["Errs"]
Equal(t, ok, true)
Equal(t, fieldErr.IsPlaceholderErr, true)
Equal(t, fieldErr.IsSliceOrArray, true)
Equal(t, len(fieldErr.SliceOrArrayErrs), 3)
sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError)
Equal(t, ok, true)
Equal(t, sliceError1.IsPlaceholderErr, true)
Equal(t, sliceError1.IsSliceOrArray, true)
Equal(t, len(sliceError1.SliceOrArrayErrs), 2)
innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors)
Equal(t, ok, true)
Equal(t, len(innerSliceStructError1.Errors), 1)
innerSliceStructError2, ok := sliceError1.SliceOrArrayErrs[2].(*FieldError)
Equal(t, ok, true)
Equal(t, innerSliceStructError2.IsPlaceholderErr, false)
Equal(t, innerSliceStructError2.IsSliceOrArray, false)
Equal(t, len(innerSliceStructError2.SliceOrArrayErrs), 0)
Equal(t, innerSliceStructError2.Field, "Errs[2][2]")
innerInnersliceError1 = innerSliceStructError1.Errors["Name"]
Equal(t, innerInnersliceError1.IsPlaceholderErr, false)
Equal(t, innerInnersliceError1.IsSliceOrArray, false)
Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0)
m := map[int]interface{}{0: "ok", 3: "", 4: "ok"}
err = validate.Field(m, "len=3,dive,len=2")
NotEqual(t, err, nil)
Equal(t, err.IsPlaceholderErr, true)
Equal(t, err.IsMap, true)
Equal(t, len(err.MapErrs), 1)
err = validate.Field(m, "len=2,dive,required")
NotEqual(t, err, nil)
Equal(t, err.IsPlaceholderErr, false)
Equal(t, err.IsMap, false)
Equal(t, len(err.MapErrs), 0)
arr := []interface{}{"ok", "", "ok"}
err = validate.Field(arr, "len=3,dive,len=2")
NotEqual(t, err, nil)
Equal(t, err.IsPlaceholderErr, true)
Equal(t, err.IsSliceOrArray, true)
Equal(t, len(err.SliceOrArrayErrs), 1)
err = validate.Field(arr, "len=2,dive,required")
NotEqual(t, err, nil)
Equal(t, err.IsPlaceholderErr, false)
Equal(t, err.IsSliceOrArray, false)
Equal(t, len(err.SliceOrArrayErrs), 0)
}
func TestMapDiveValidation(t *testing.T) {
m := map[int]string{0: "ok", 3: "", 4: "ok"}
@ -358,6 +735,11 @@ func TestArrayDiveValidation(t *testing.T) {
Equal(t, err.IsSliceOrArray, true)
Equal(t, len(err.SliceOrArrayErrs), 1)
// flat := err.Flatten()
// fe, ok := flat["[1]"]
// Equal(t, ok, true)
// Equal(t, fe.Tag, "required")
err = validate.Field(arr, "len=2,dive,required")
NotEqual(t, err, nil)
Equal(t, err.IsPlaceholderErr, false)
@ -386,6 +768,12 @@ func TestArrayDiveValidation(t *testing.T) {
NotEqual(t, errs, nil)
Equal(t, len(errs.Errors), 1)
// flat = errs.Flatten()
// me, ok := flat["Errs[1]"]
// Equal(t, ok, true)
// Equal(t, me.Field, "Errs[1]")
// Equal(t, me.Tag, "required")
fieldErr, ok := errs.Errors["Errs"]
Equal(t, ok, true)
Equal(t, fieldErr.IsPlaceholderErr, true)
@ -446,6 +834,7 @@ func TestArrayDiveValidation(t *testing.T) {
Equal(t, sliceError1.IsPlaceholderErr, true)
Equal(t, sliceError1.IsSliceOrArray, true)
Equal(t, len(sliceError1.SliceOrArrayErrs), 2)
Equal(t, sliceError1.Field, "Errs[0]")
innerSliceError1, ok := sliceError1.SliceOrArrayErrs[1].(*FieldError)
Equal(t, ok, true)
@ -453,6 +842,7 @@ func TestArrayDiveValidation(t *testing.T) {
Equal(t, innerSliceError1.Tag, required)
Equal(t, innerSliceError1.IsSliceOrArray, false)
Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0)
Equal(t, innerSliceError1.Field, "Errs[0][1]")
type Inner struct {
Name string `validate:"required"`
@ -516,12 +906,25 @@ func TestArrayDiveValidation(t *testing.T) {
// for full test coverage
fmt.Sprint(errs.Error())
// flat := errs.Flatten()
// // fmt.Println(errs)
// fmt.Println(flat)
// expect Errs[0][1].Inner.Name
// me, ok := flat["Errs[1]"]
// Equal(t, ok, true)
// Equal(t, me.Field, "Errs[1]")
// Equal(t, me.Tag, "required")
fieldErr, ok = errs.Errors["Errs"]
Equal(t, ok, true)
Equal(t, fieldErr.IsPlaceholderErr, true)
Equal(t, fieldErr.IsSliceOrArray, true)
Equal(t, len(fieldErr.SliceOrArrayErrs), 3)
// flat := fieldErr.Flatten()
// fmt.Println(errs)
// fmt.Println(flat)
sliceError1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError)
Equal(t, ok, true)
Equal(t, sliceError1.IsPlaceholderErr, true)

Loading…
Cancel
Save