From 104c4ac40fe7c9743f67312711be5ca859b22f73 Mon Sep 17 00:00:00 2001 From: "S.Kulish" Date: Wed, 28 Sep 2022 14:58:26 +0200 Subject: [PATCH 1/3] hw09_struct_validator --- hw09_struct_validator/.sync | 0 hw09_struct_validator/go.mod | 7 +- hw09_struct_validator/go.sum | 17 ++ hw09_struct_validator/validator.go | 287 +++++++++++++++++++++++- hw09_struct_validator/validator_test.go | 101 ++++++++- 5 files changed, 402 insertions(+), 10 deletions(-) delete mode 100644 hw09_struct_validator/.sync create mode 100644 hw09_struct_validator/go.sum diff --git a/hw09_struct_validator/.sync b/hw09_struct_validator/.sync deleted file mode 100644 index e69de29..0000000 diff --git a/hw09_struct_validator/go.mod b/hw09_struct_validator/go.mod index a22eccc..3683f8b 100644 --- a/hw09_struct_validator/go.mod +++ b/hw09_struct_validator/go.mod @@ -1,3 +1,8 @@ -module github.com/fixme_my_friend/hw09_struct_validator +module github.com/sofiiakulish/hw09_struct_validator go 1.16 + +require ( + github.com/fatih/structs v1.1.0 + github.com/stretchr/testify v1.8.0 +) diff --git a/hw09_struct_validator/go.sum b/hw09_struct_validator/go.sum new file mode 100644 index 0000000..aa2b388 --- /dev/null +++ b/hw09_struct_validator/go.sum @@ -0,0 +1,17 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hw09_struct_validator/validator.go b/hw09_struct_validator/validator.go index b85fbec..b8f5a74 100644 --- a/hw09_struct_validator/validator.go +++ b/hw09_struct_validator/validator.go @@ -1,5 +1,19 @@ package hw09structvalidator +import ( + "errors" + "fmt" + "github.com/fatih/structs" + "reflect" + "regexp" + "strconv" + "strings" +) + +const VALIDATE_TAG_NAME = "validate" + +var NilValidationError = ValidationError{"", nil} + type ValidationError struct { Field string Err error @@ -7,11 +21,280 @@ type ValidationError struct { type ValidationErrors []ValidationError +type ValidatorType int + +const ( + MIN ValidatorType = iota + MAX + LEN + REGEXP + IN +) + +type ValidatorValue string + +type Validator struct { + Type ValidatorType + Value ValidatorValue +} + func (v ValidationErrors) Error() string { - panic("implement me") + var result string + + for _, validationError := range v { + result += fmt.Sprintf("Validation error for field %q: %s\n", validationError.Field, validationError.Err.Error()) + } + + return result } func Validate(v interface{}) error { - // Place your code here. + var result ValidationErrors + + fields := structs.Fields(v) + + for _, field := range fields { + tag := field.Tag(VALIDATE_TAG_NAME) + if tag == "" { + continue + } + + validators := getValidators(tag) + + for _, validator := range validators { + validationErrors := valid(field, validator) + if validationErrors != nil { + result = append(result, validationErrors...) + } + } + } + + if len(result) == 0 { + return nil + } + + return errors.New(result.Error()) +} + +func getValidators(tagValue string) []Validator { + var validator []Validator + + validatorStrings := strings.Split(tagValue, "|") + + for _, str := range validatorStrings { + + delimiterPos := strings.Index(str, ":") + if delimiterPos == -1 { + panic("Incorrect validator definition. Semicolon should be present") + } + + typeString := str[0:delimiterPos] + typeValue := getValidatorTypeByString(typeString) + + ruleString := str[delimiterPos+1:] + if ruleString == "" { + panic("Incorrect validator definition. Value should be present after semicolon") + } + ruleValue := getValidatorValueByString(ruleString) + + validator = append(validator, Validator{typeValue, ruleValue}) + } + + if len(validator) == 0 { + return nil + } + + return validator +} + +func getValidatorTypeByString(typeString string) ValidatorType { + switch typeString { + case "min": + return MIN + case "max": + return MAX + case "len": + return LEN + case "regexp": + return REGEXP + case "in": + return IN + default: + panic("Unknown validation type '" + typeString + "'") + } +} + +func getValidatorValueByString(ruleString string) ValidatorValue { + return ValidatorValue(ruleString) +} + +func valid(f *structs.Field, validator Validator) []ValidationError { + rt := reflect.TypeOf(f.Value()) + switch rt.Kind() { + case reflect.Slice, reflect.Array: + var result []ValidationError + + array := reflect.ValueOf(f.Value()) + + for i := 0; i < array.Len(); i++ { + result = append(result, validOneEntry(f.Name(), array.Index(i).Interface(), validator)...) + } + + return result + } + + return validOneEntry(f.Name(), f.Value(), validator) +} + +func validOneEntry(fieldName string, fieldValue interface{}, validator Validator) []ValidationError { + switch validator.Type { + case MIN: + value := getIntOrPanic(fieldValue) + err := validateMin(fieldName, value, validator) + if err != NilValidationError { + return []ValidationError{err} + } + case MAX: + value := getIntOrPanic(fieldValue) + err := validateMax(fieldName, value, validator) + if err != NilValidationError { + return []ValidationError{err} + } + case LEN: + value := getStringOrPanic(fieldValue) + + err := validateLen(fieldName, value, validator) + if err != NilValidationError { + return []ValidationError{err} + } + case REGEXP: + value := getStringOrPanic(fieldValue) + + err := validateRegexp(fieldName, value, validator) + if err != NilValidationError { + return []ValidationError{err} + } + case IN: + switch reflect.TypeOf(fieldValue).Kind() { + case reflect.Int: + value := getIntOrPanic(fieldValue) + + err := validateInInt(fieldName, value, validator) + if err != NilValidationError { + return []ValidationError{err} + } + case reflect.String: + value := getStringOrPanic(fieldValue) + + err := validateInString(fieldName, value, validator) + if err != NilValidationError { + return []ValidationError{err} + } + } + default: + panic("Unsupported validation type") + } + return nil } + +func getIntOrPanic(v interface{}) int { + if reflect.TypeOf(v).Kind() != reflect.Int { + panic(fmt.Errorf("Value should have type int. %s provided", reflect.TypeOf(v).Name())) + } + + return int(reflect.ValueOf(v).Int()) +} + +func getStringOrPanic(v interface{}) string { + if reflect.TypeOf(v).Kind() != reflect.String { + panic(fmt.Errorf("Value should have type string. %s provided", reflect.TypeOf(v).Kind())) + } + + return reflect.ValueOf(v).String() +} + +func validateMin(fieldName string, fieldValue int, validator Validator) ValidationError { + validatorValue, err := strconv.Atoi(string(validator.Value)) + if err != nil { + panic(fmt.Sprintf("Error geting int definition for the validator: %s", err)) + } + + if fieldValue < validatorValue { + return ValidationError{fieldName, fmt.Errorf("Value should be more than %d. %d was provided", validatorValue, fieldValue)} + } + + return NilValidationError +} + +func validateMax(fieldName string, fieldValue int, validator Validator) ValidationError { + validatorValue, err := strconv.Atoi(string(validator.Value)) + if err != nil { + panic(err) + } + + if fieldValue > validatorValue { + return ValidationError{fieldName, fmt.Errorf("Value should be less than %d. %d was provided", validatorValue, fieldValue)} + } + + return NilValidationError +} + +func validateInString(fieldName string, fieldValue string, validator Validator) ValidationError { + validatorValues := strings.Split(string(validator.Value), ",") + + for _, val := range validatorValues { + if val == fieldValue { + return NilValidationError + } + } + + return ValidationError{fieldName, fmt.Errorf("Value should be in the list %q. %q was provided", string(validator.Value), fieldValue)} +} + +func validateInInt(fieldName string, fieldValue int, validator Validator) ValidationError { + validatorValuesString := strings.Split(string(validator.Value), ",") + validatorValues := make([]int, len(validatorValuesString)) + + for k, v := range validatorValuesString { + value, err := strconv.Atoi(v) + if err != nil { + panic(fmt.Errorf("Error parsing validation value: %s", err)) + } + + validatorValues[k] = value + } + + for _, val := range validatorValues { + if val == fieldValue { + return NilValidationError + } + } + + return ValidationError{fieldName, fmt.Errorf("Value should be in the list %q. %d was provided", string(validator.Value), fieldValue)} +} + +func validateLen(fieldName string, fieldValue string, validator Validator) ValidationError { + validatorValue, _ := strconv.Atoi(string(validator.Value)) + + if len(fieldValue) != validatorValue { + return ValidationError{fieldName, fmt.Errorf("Value should have length %d. %d (%q) provided", validatorValue, len(fieldValue), fieldValue)} + } + + return NilValidationError +} + +func validateRegexp(fieldName string, fieldValue string, validator Validator) ValidationError { + validatorValue := string(validator.Value) + + matched, err := regexp.MatchString(validatorValue, fieldValue) + if err != nil { + panic(fmt.Errorf("Error regexp: %s", err)) + } + + if matched == false { + return ValidationError{fieldName, fmt.Errorf("Value should match regexp %q. Value %q is not match", validatorValue, fieldValue)} + } + + return NilValidationError +} diff --git a/hw09_struct_validator/validator_test.go b/hw09_struct_validator/validator_test.go index 459facc..51b8ae5 100644 --- a/hw09_struct_validator/validator_test.go +++ b/hw09_struct_validator/validator_test.go @@ -2,7 +2,9 @@ package hw09structvalidator import ( "encoding/json" + "errors" "fmt" + "github.com/stretchr/testify/require" "testing" ) @@ -34,26 +36,111 @@ type ( Code int `validate:"in:200,404,500"` Body string `json:"omitempty"` } + + InvalidLenIntDefinition struct { + ID int `validate:"len:36"` + } + + InvalidMinStringDefinition struct { + Name string `validate:"min:36"` + } + + InvalidMaxStringDefinition struct { + Name string `validate:"max:36"` + } + + InvalidRegexpIntDefinition struct { + Name int `validate:"regexp:^\\w+@\\w+\\.\\w+$"` + } + InvalidLenDefinition struct { + ID int `validate:"len:a"` + } + + InvalidMinDefinition struct { + ID int `validate:"min:a"` + } + + InvalidMaxDefinition struct { + ID int `validate:"max:a"` + } ) -func TestValidate(t *testing.T) { +func TestValidateWithValidationErrors(t *testing.T) { tests := []struct { in interface{} expectedErr error }{ - { - // Place your code here. - }, - // ... - // Place your code here. + {in: User{"123", "Name", 20, "1@gmail.com", "admin", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErr: errors.New("Validation error for field \"ID\": Value should have length 36. 3 (\"123\") provided\n")}, + {in: User{"123", "Name", 12, "1@gmail.com", "admin", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErr: errors.New("Validation error for field \"ID\": Value should have length 36. 3 (\"123\") provided\nValidation error for field \"Age\": Value should be more than 18. 12 was provided\n")}, + {in: User{"123456789012345678901234567890123456", "Name", 20, "1gmail.com", "admin", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErr: errors.New(fmt.Sprintf("Validation error for field \"Email\": Value should match regexp %q. Value \"1gmail.com\" is not match\n", "^\\w+@\\w+\\.\\w+$"))}, + {in: User{"123456789012345678901234567890123456", "Name", 20, "1@gmail.com", "admin2", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErr: errors.New("Validation error for field \"Role\": Value should be in the list \"admin,stuff\". \"admin2\" was provided\n")}, + {in: User{"123456789012345678901234567890123456", "Name", 20, "1@gmail.com", "admin", []string{"123", "1234", "12345678901"}, []byte(`{"data": "test"}`)}, expectedErr: errors.New("Validation error for field \"Phones\": Value should have length 11. 3 (\"123\") provided\nValidation error for field \"Phones\": Value should have length 11. 4 (\"1234\") provided\n")}, + + {in: App{"1234"}, expectedErr: errors.New("Validation error for field \"Version\": Value should have length 5. 4 (\"1234\") provided\n")}, + + {in: Response{401, "Not Auhorized"}, expectedErr: errors.New("Validation error for field \"Code\": Value should be in the list \"200,404,500\". 401 was provided\n")}, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + tt := tt + t.Parallel() + + err := Validate(tt.in) + require.Error(t, err) + require.Equal(t, tt.expectedErr, err) + _ = tt + }) + } +} + +func TestValidateSuccess(t *testing.T) { + tests := []struct { + in interface{} + }{ + {in: User{"123456789012345678901234567890123456", "Name", 20, "1@gmail.com", "admin", []string{"12345678901"}, []byte(`{"data": "test"}`)}}, + {in: App{"12345"}}, + {in: Token{[]byte(`"test"`), []byte(`"test"`), []byte(`"test"`)}}, + {in: Response{200, "{}"}}, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + tt := tt + t.Parallel() + + err := Validate(tt.in) + require.NoError(t, err) + _ = tt + }) + } +} + +func TestInvalidDefinition(t *testing.T) { + tests := []struct { + in interface{} + }{ + {in: InvalidLenIntDefinition{12345}}, + {in: InvalidMinStringDefinition{"Name"}}, + {in: InvalidMaxStringDefinition{"Name"}}, + {in: InvalidRegexpIntDefinition{12}}, + {in: InvalidLenDefinition{12345}}, + {in: InvalidMinDefinition{123}}, + {in: InvalidMaxDefinition{123}}, } for i, tt := range tests { t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } + }() + tt := tt t.Parallel() - // Place your code here. + Validate(tt.in) _ = tt }) } From db74c11e6f7381f353aaf9002f434eccb052df83 Mon Sep 17 00:00:00 2001 From: "S.Kulish" Date: Sun, 30 Oct 2022 13:13:39 +0100 Subject: [PATCH 2/3] code review changes --- hw09_struct_validator/validator.go | 272 +++++++++++++----------- hw09_struct_validator/validator_test.go | 28 +-- 2 files changed, 157 insertions(+), 143 deletions(-) diff --git a/hw09_struct_validator/validator.go b/hw09_struct_validator/validator.go index b8f5a74..8c9e138 100644 --- a/hw09_struct_validator/validator.go +++ b/hw09_struct_validator/validator.go @@ -3,7 +3,6 @@ package hw09structvalidator import ( "errors" "fmt" - "github.com/fatih/structs" "reflect" "regexp" "strconv" @@ -12,23 +11,16 @@ import ( const VALIDATE_TAG_NAME = "validate" -var NilValidationError = ValidationError{"", nil} +var NoValidationError = ValidationError{"", nil} -type ValidationError struct { - Field string - Err error -} - -type ValidationErrors []ValidationError - -type ValidatorType int +type ValidatorType string const ( - MIN ValidatorType = iota - MAX - LEN - REGEXP - IN + MIN ValidatorType = "min" + MAX ValidatorType = "max" + LEN ValidatorType = "len" + REGEXP ValidatorType = "regexp" + IN ValidatorType = "in" ) type ValidatorValue string @@ -38,31 +30,47 @@ type Validator struct { Value ValidatorValue } +type ValidationError struct { + Field string + Err error +} + +type ValidationErrors []ValidationError + func (v ValidationErrors) Error() string { - var result string + var b strings.Builder for _, validationError := range v { - result += fmt.Sprintf("Validation error for field %q: %s\n", validationError.Field, validationError.Err.Error()) + b.WriteString(fmt.Sprintf("Validation error for field %q: %s\n", validationError.Field, validationError.Err.Error())) } - return result + return b.String() } func Validate(v interface{}) error { var result ValidationErrors - fields := structs.Fields(v) + stT := reflect.TypeOf(v) + stV := reflect.ValueOf(v) - for _, field := range fields { - tag := field.Tag(VALIDATE_TAG_NAME) - if tag == "" { - continue - } + for i:=0; i validatorValue { - return ValidationError{fieldName, fmt.Errorf("Value should be less than %d. %d was provided", validatorValue, fieldValue)} + return ValidationError{fieldName, fmt.Errorf("Value should be less than %d. %d was provided", validatorValue, fieldValue)}, nil } - return NilValidationError + return NoValidationError, nil } -func validateInString(fieldName string, fieldValue string, validator Validator) ValidationError { +func validateInString(fieldName string, fieldValue string, validator Validator) (ValidationError, error) { validatorValues := strings.Split(string(validator.Value), ",") for _, val := range validatorValues { if val == fieldValue { - return NilValidationError + return NoValidationError, nil } } - return ValidationError{fieldName, fmt.Errorf("Value should be in the list %q. %q was provided", string(validator.Value), fieldValue)} + return ValidationError{fieldName, fmt.Errorf("Value should be in the list %q. %q was provided", string(validator.Value), fieldValue)}, nil } -func validateInInt(fieldName string, fieldValue int, validator Validator) ValidationError { +func validateInInt(fieldName string, fieldValue int, validator Validator) (ValidationError, error) { validatorValuesString := strings.Split(string(validator.Value), ",") validatorValues := make([]int, len(validatorValuesString)) for k, v := range validatorValuesString { value, err := strconv.Atoi(v) if err != nil { - panic(fmt.Errorf("Error parsing validation value: %s", err)) + return NoValidationError, fmt.Errorf("Error parsing validation value: %s", err) } validatorValues[k] = value @@ -267,34 +287,34 @@ func validateInInt(fieldName string, fieldValue int, validator Validator) Valida for _, val := range validatorValues { if val == fieldValue { - return NilValidationError + return NoValidationError, nil } } - return ValidationError{fieldName, fmt.Errorf("Value should be in the list %q. %d was provided", string(validator.Value), fieldValue)} + return ValidationError{fieldName, fmt.Errorf("Value should be in the list %q. %d was provided", string(validator.Value), fieldValue)}, nil } -func validateLen(fieldName string, fieldValue string, validator Validator) ValidationError { +func validateLen(fieldName string, fieldValue string, validator Validator) (ValidationError, error) { validatorValue, _ := strconv.Atoi(string(validator.Value)) if len(fieldValue) != validatorValue { - return ValidationError{fieldName, fmt.Errorf("Value should have length %d. %d (%q) provided", validatorValue, len(fieldValue), fieldValue)} + return ValidationError{fieldName, fmt.Errorf("Value should have length %d. %d (%q) provided", validatorValue, len(fieldValue), fieldValue)}, nil } - return NilValidationError + return NoValidationError, nil } -func validateRegexp(fieldName string, fieldValue string, validator Validator) ValidationError { +func validateRegexp(fieldName string, fieldValue string, validator Validator) (ValidationError, error) { validatorValue := string(validator.Value) matched, err := regexp.MatchString(validatorValue, fieldValue) if err != nil { - panic(fmt.Errorf("Error regexp: %s", err)) + return NoValidationError, fmt.Errorf("Error regexp: %s", err) } if matched == false { - return ValidationError{fieldName, fmt.Errorf("Value should match regexp %q. Value %q is not match", validatorValue, fieldValue)} + return ValidationError{fieldName, fmt.Errorf("Value should match regexp %q. Value %q is not match", validatorValue, fieldValue)}, nil } - return NilValidationError + return NoValidationError, nil } diff --git a/hw09_struct_validator/validator_test.go b/hw09_struct_validator/validator_test.go index 51b8ae5..ee55b01 100644 --- a/hw09_struct_validator/validator_test.go +++ b/hw09_struct_validator/validator_test.go @@ -89,7 +89,6 @@ func TestValidateWithValidationErrors(t *testing.T) { err := Validate(tt.in) require.Error(t, err) require.Equal(t, tt.expectedErr, err) - _ = tt }) } } @@ -111,7 +110,6 @@ func TestValidateSuccess(t *testing.T) { err := Validate(tt.in) require.NoError(t, err) - _ = tt }) } } @@ -119,29 +117,25 @@ func TestValidateSuccess(t *testing.T) { func TestInvalidDefinition(t *testing.T) { tests := []struct { in interface{} + expectedErr error }{ - {in: InvalidLenIntDefinition{12345}}, - {in: InvalidMinStringDefinition{"Name"}}, - {in: InvalidMaxStringDefinition{"Name"}}, - {in: InvalidRegexpIntDefinition{12}}, - {in: InvalidLenDefinition{12345}}, - {in: InvalidMinDefinition{123}}, - {in: InvalidMaxDefinition{123}}, + {in: InvalidLenIntDefinition{12345}, expectedErr: errors.New("Value should have type string. int provided")}, + {in: InvalidMinStringDefinition{"Name"}, expectedErr: errors.New("Value should have type int. string provided")}, + {in: InvalidMaxStringDefinition{"Name"}, expectedErr: errors.New("Value should have type int. string provided")}, + {in: InvalidRegexpIntDefinition{12}, expectedErr: errors.New("Value should have type string. int provided")}, + {in: InvalidLenDefinition{12345}, expectedErr: errors.New("Value should have type string. int provided")}, + {in: InvalidMinDefinition{123}, expectedErr: errors.New("Error getting int definition for the validator: strconv.Atoi: parsing \"a\": invalid syntax")}, + {in: InvalidMaxDefinition{123}, expectedErr: errors.New("Error getting int definition for the validator: strconv.Atoi: parsing \"a\": invalid syntax")}, } for i, tt := range tests { t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Errorf("The code did not panic") - } - }() - tt := tt t.Parallel() - Validate(tt.in) - _ = tt + err := Validate(tt.in) + require.Error(t, err) + require.Equal(t, tt.expectedErr, err) }) } } From f425b4c85107cf10a19a4ab8aedf07652ae25bf5 Mon Sep 17 00:00:00 2001 From: "S.Kulish" Date: Tue, 8 Nov 2022 19:39:41 +0100 Subject: [PATCH 3/3] return value for validate function --- hw09_struct_validator/validator.go | 6 +++--- hw09_struct_validator/validator_test.go | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/hw09_struct_validator/validator.go b/hw09_struct_validator/validator.go index 8c9e138..c3c4c0e 100644 --- a/hw09_struct_validator/validator.go +++ b/hw09_struct_validator/validator.go @@ -78,10 +78,10 @@ func Validate(v interface{}) error { } if len(result) == 0 { - return nil - } + return nil + } - return errors.New(result.Error()) + return result } func getValidators(tagValue string) ([]Validator, error) { diff --git a/hw09_struct_validator/validator_test.go b/hw09_struct_validator/validator_test.go index ee55b01..570bc6c 100644 --- a/hw09_struct_validator/validator_test.go +++ b/hw09_struct_validator/validator_test.go @@ -67,18 +67,18 @@ type ( func TestValidateWithValidationErrors(t *testing.T) { tests := []struct { - in interface{} - expectedErr error + in interface{} + expectedErrString string }{ - {in: User{"123", "Name", 20, "1@gmail.com", "admin", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErr: errors.New("Validation error for field \"ID\": Value should have length 36. 3 (\"123\") provided\n")}, - {in: User{"123", "Name", 12, "1@gmail.com", "admin", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErr: errors.New("Validation error for field \"ID\": Value should have length 36. 3 (\"123\") provided\nValidation error for field \"Age\": Value should be more than 18. 12 was provided\n")}, - {in: User{"123456789012345678901234567890123456", "Name", 20, "1gmail.com", "admin", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErr: errors.New(fmt.Sprintf("Validation error for field \"Email\": Value should match regexp %q. Value \"1gmail.com\" is not match\n", "^\\w+@\\w+\\.\\w+$"))}, - {in: User{"123456789012345678901234567890123456", "Name", 20, "1@gmail.com", "admin2", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErr: errors.New("Validation error for field \"Role\": Value should be in the list \"admin,stuff\". \"admin2\" was provided\n")}, - {in: User{"123456789012345678901234567890123456", "Name", 20, "1@gmail.com", "admin", []string{"123", "1234", "12345678901"}, []byte(`{"data": "test"}`)}, expectedErr: errors.New("Validation error for field \"Phones\": Value should have length 11. 3 (\"123\") provided\nValidation error for field \"Phones\": Value should have length 11. 4 (\"1234\") provided\n")}, + {in: User{"123", "Name", 20, "1@gmail.com", "admin", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErrString: "Validation error for field \"ID\": Value should have length 36. 3 (\"123\") provided\n"}, + {in: User{"123", "Name", 12, "1@gmail.com", "admin", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErrString: "Validation error for field \"ID\": Value should have length 36. 3 (\"123\") provided\nValidation error for field \"Age\": Value should be more than 18. 12 was provided\n"}, + {in: User{"123456789012345678901234567890123456", "Name", 20, "1gmail.com", "admin", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErrString: fmt.Sprintf("Validation error for field \"Email\": Value should match regexp %q. Value \"1gmail.com\" is not match\n", "^\\w+@\\w+\\.\\w+$")}, + {in: User{"123456789012345678901234567890123456", "Name", 20, "1@gmail.com", "admin2", []string{"12345678901"}, []byte(`{"data": "test"}`)}, expectedErrString: "Validation error for field \"Role\": Value should be in the list \"admin,stuff\". \"admin2\" was provided\n"}, + {in: User{"123456789012345678901234567890123456", "Name", 20, "1@gmail.com", "admin", []string{"123", "1234", "12345678901"}, []byte(`{"data": "test"}`)}, expectedErrString: "Validation error for field \"Phones\": Value should have length 11. 3 (\"123\") provided\nValidation error for field \"Phones\": Value should have length 11. 4 (\"1234\") provided\n"}, - {in: App{"1234"}, expectedErr: errors.New("Validation error for field \"Version\": Value should have length 5. 4 (\"1234\") provided\n")}, + {in: App{"1234"}, expectedErrString: "Validation error for field \"Version\": Value should have length 5. 4 (\"1234\") provided\n"}, - {in: Response{401, "Not Auhorized"}, expectedErr: errors.New("Validation error for field \"Code\": Value should be in the list \"200,404,500\". 401 was provided\n")}, + {in: Response{401, "Not Authorized"}, expectedErrString: "Validation error for field \"Code\": Value should be in the list \"200,404,500\". 401 was provided\n"}, } for i, tt := range tests { @@ -87,8 +87,7 @@ func TestValidateWithValidationErrors(t *testing.T) { t.Parallel() err := Validate(tt.in) - require.Error(t, err) - require.Equal(t, tt.expectedErr, err) + require.Equal(t, tt.expectedErrString, err.Error()) }) } }