diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..24b80ced6 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @go-playground/validator-maintainers \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 01044997f..40b774aed 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,3 +1,6 @@ +- [ ] I have looked at the documentation [here](https://pkg.go.dev/github.com/go-playground/validator/v10#section-documentation) first? +- [ ] I have looked at the examples provided that may showcase my question [here](/_examples)? + ### Package version eg. v9, v10: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6bb0db3e7..d99afdb08 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,4 +4,4 @@ **Make sure that you've checked the boxes below before you submit PR:** - [ ] Tests exist or have been written that cover this particular change. -@go-playground/admins \ No newline at end of file +@go-playground/validator-maintainers \ No newline at end of file diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 0025ea0c1..acf873d2a 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -8,7 +8,7 @@ jobs: test: strategy: matrix: - go-version: [1.14.x, 1.15.x] + go-version: [1.15.x, 1.16.x] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -32,7 +32,7 @@ jobs: run: go test -race -covermode=atomic -coverprofile="profile.cov" ./... - name: Send Coverage - if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.15.x' + if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.16.x' uses: shogo82148/actions-goveralls@v1 with: path-to-profile: profile.cov @@ -45,4 +45,4 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: - version: v1.31 + version: v1.39 diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 000000000..b809c4ce1 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,16 @@ +## Maintainers Guide + +### Semantic Versioning +Semantic versioning as defined [here](https://semver.org) must be strictly adhered to. + +### External Dependencies +Any new external dependencies MUST: +- Have a compatible LICENSE present. +- Be actively maintained. +- Be approved by @go-playground/admins + +### PR Merge Requirements +- Up-to-date branch. +- Passing tests and linting. +- CODEOWNERS approval. +- Tests that cover both the Happy and Unhappy paths. \ No newline at end of file diff --git a/Makefile b/Makefile index 19c91ed73..164e8bb91 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,11 @@ GOCMD=GO111MODULE=on go linters-install: @golangci-lint --version >/dev/null 2>&1 || { \ echo "installing linting tools..."; \ - curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.21.0; \ + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.39.0; \ } lint: linters-install - $(PWD)/bin/golangci-lint run + golangci-lint run test: $(GOCMD) test -cover -race ./... diff --git a/README.md b/README.md index 2ba36d73f..d64e5ae01 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Package validator -================ +================= [![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -![Project status](https://img.shields.io/badge/version-10.5.0-green.svg) +![Project status](https://img.shields.io/badge/version-10.6.0-green.svg) [![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator) [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) @@ -295,5 +295,10 @@ How to Contribute Make a pull request... License ------- +------- Distributed under MIT License, please see license file within the code for more details. + +Maintainers +----------- +This project has grown large enough that more than one person is required to properly support the community. +If you are interested in becoming a maintainer please reach out to me https://github.com/deankarn \ No newline at end of file diff --git a/baked_in.go b/baked_in.go index 70376cab9..c46e6cd62 100644 --- a/baked_in.go +++ b/baked_in.go @@ -192,7 +192,7 @@ var ( "bcp47_language_tag": isBCP47LanguageTag, "postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2, "postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field, - "bic": isIsoBicFormat, + "bic": isIsoBicFormat, } ) @@ -552,7 +552,7 @@ func isEthereumAddress(fl FieldLevel) bool { return false } - if ethaddressRegexUpper.MatchString(address) || ethAddressRegexLower.MatchString(address) { + if ethAddressRegexUpper.MatchString(address) || ethAddressRegexLower.MatchString(address) { return true } @@ -1225,8 +1225,6 @@ func isPostcodeByIso3166Alpha2(fl FieldLevel) bool { // example: `postcode_iso3166_alpha2_field=CountryCode` func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool { field := fl.Field() - kind := field.Kind() - params := parseOneOfParam2(fl.Param()) if len(params) != 1 { @@ -1390,7 +1388,7 @@ func isRGB(fl FieldLevel) bool { // IsHEXColor is the validation function for validating if the current field's value is a valid HEX color. func isHEXColor(fl FieldLevel) bool { - return hexcolorRegex.MatchString(fl.Field().String()) + return hexColorRegex.MatchString(fl.Field().String()) } // IsHexadecimal is the validation function for validating if the current field's value is a valid hexadecimal. @@ -2363,9 +2361,5 @@ func isBCP47LanguageTag(fl FieldLevel) bool { func isIsoBicFormat(fl FieldLevel) bool { bicString := fl.Field().String() - if !bicRegex.MatchString(bicString) { - return false - } - - return true + return bicRegex.MatchString(bicString) } diff --git a/errors.go b/errors.go index f6e72da7d..9a1b1abe9 100644 --- a/errors.go +++ b/errors.go @@ -82,7 +82,7 @@ func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslati // FieldError contains all functions to get error details type FieldError interface { - // returns the validation tag that failed. if the + // Tag returns the validation tag that failed. if the // validation was an alias, this will return the // alias name and not the underlying tag that failed. // @@ -90,7 +90,7 @@ type FieldError interface { // will return "iscolor" Tag() string - // returns the validation tag that failed, even if an + // ActualTag returns the validation tag that failed, even if an // alias the actual tag within the alias will be returned. // If an 'or' validation fails the entire or will be returned. // @@ -98,7 +98,7 @@ type FieldError interface { // will return "hexcolor|rgb|rgba|hsl|hsla" ActualTag() string - // returns the namespace for the field error, with the tag + // Namespace returns the namespace for the field error, with the tag // name taking precedence over the field's actual name. // // eg. JSON name "User.fname" @@ -109,7 +109,7 @@ type FieldError interface { // using validate.Field(...) as there is no way to extract it's name Namespace() string - // returns the namespace for the field error, with the field's + // StructNamespace returns the namespace for the field error, with the field's // actual name. // // eq. "User.FirstName" see Namespace for comparison @@ -118,24 +118,24 @@ type FieldError interface { // using validate.Field(...) as there is no way to extract its name StructNamespace() string - // returns the fields name with the tag name taking precedence over the + // Field returns the fields name with the tag name taking precedence over the // field's actual name. // // eq. JSON name "fname" // see StructField for comparison Field() string - // returns the field's actual name from the struct, when able to determine. + // StructField returns the field's actual name from the struct, when able to determine. // // eq. "FirstName" // see Field for comparison StructField() string - // returns the actual field's value in case needed for creating the error + // Value returns the actual field's value in case needed for creating the error // message Value() interface{} - // returns the param value, in string form for comparison; this will also + // Param returns the param value, in string form for comparison; this will also // help with generating an error message Param() string @@ -149,7 +149,7 @@ type FieldError interface { // eg. time.Time's type is time.Time Type() reflect.Type - // returns the FieldError's translated error + // Translate returns the FieldError's translated error // from the provided 'ut.Translator' and registered 'TranslationFunc' // // NOTE: if no registered translator can be found it returns the same as @@ -221,7 +221,7 @@ func (fe *fieldError) Field() string { // return fld } -// returns the field's actual name from the struct, when able to determine. +// StructField returns the field's actual name from the struct, when able to determine. func (fe *fieldError) StructField() string { // return fe.structField return fe.structNs[len(fe.structNs)-int(fe.structfieldLen):] diff --git a/field_level.go b/field_level.go index f0e2a9a85..ef35826ee 100644 --- a/field_level.go +++ b/field_level.go @@ -5,24 +5,25 @@ import "reflect" // FieldLevel contains all the information and helper functions // to validate a field type FieldLevel interface { - // returns the top level struct, if any + + // Top returns the top level struct, if any Top() reflect.Value - // returns the current fields parent struct, if any or + // Parent returns the current fields parent struct, if any or // the comparison value if called 'VarWithValue' Parent() reflect.Value - // returns current field for validation + // Field returns current field for validation Field() reflect.Value - // returns the field's name with the tag + // FieldName returns the field's name with the tag // name taking precedence over the fields actual name. FieldName() string - // returns the struct field's name + // StructFieldName returns the struct field's name StructFieldName() string - // returns param for validation against current field + // Param returns param for validation against current field Param() string // GetTag returns the current validations tag name @@ -33,7 +34,7 @@ type FieldLevel interface { // underlying value and it's kind. ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool) - // traverses the parent struct to retrieve a specific field denoted by the provided namespace + // GetStructFieldOK traverses the parent struct to retrieve a specific field denoted by the provided namespace // in the param and returns the field, field kind and whether is was successful in retrieving // the field at all. // @@ -49,7 +50,7 @@ type FieldLevel interface { // Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable. GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) - // traverses the parent struct to retrieve a specific field denoted by the provided namespace + // GetStructFieldOK2 traverses the parent struct to retrieve a specific field denoted by the provided namespace // in the param and returns the field, field kind, if it's a nullable type and whether is was successful in retrieving // the field at all. // @@ -57,7 +58,7 @@ type FieldLevel interface { // could not be retrieved because it didn't exist. GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) - // GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for + // GetStructFieldOKAdvanced2 is the same as GetStructFieldOK except that it accepts the parent struct to start looking for // the field and namespace allowing more extensibility for validators. GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) } @@ -107,12 +108,12 @@ func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) return current, kind, found } -// GetStructFieldOK returns Param returns param for validation against current field +// GetStructFieldOK2 returns Param returns param for validation against current field func (v *validate) GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) { return v.getStructFieldOKInternal(v.slflParent, v.ct.param) } -// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for +// GetStructFieldOKAdvanced2 is the same as GetStructFieldOK except that it accepts the parent struct to start looking for // the field and namespace allowing more extensibility for validators. func (v *validate) GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) { return v.getStructFieldOKInternal(val, namespace) diff --git a/regexes.go b/regexes.go index 7b01e1325..9ecf4b1b4 100644 --- a/regexes.go +++ b/regexes.go @@ -10,7 +10,7 @@ const ( numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" numberRegexString = "^[0-9]+$" hexadecimalRegexString = "^(0[xX])?[0-9a-fA-F]+$" - hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + hexColorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$" rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" 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*\\)$" @@ -60,7 +60,7 @@ var ( numericRegex = regexp.MustCompile(numericRegexString) numberRegex = regexp.MustCompile(numberRegexString) hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString) - hexcolorRegex = regexp.MustCompile(hexcolorRegexString) + hexColorRegex = regexp.MustCompile(hexColorRegexString) rgbRegex = regexp.MustCompile(rgbRegexString) rgbaRegex = regexp.MustCompile(rgbaRegexString) hslRegex = regexp.MustCompile(hslRegexString) @@ -93,7 +93,7 @@ var ( btcUpperAddressRegexBech32 = regexp.MustCompile(btcAddressUpperRegexStringBech32) btcLowerAddressRegexBech32 = regexp.MustCompile(btcAddressLowerRegexStringBech32) ethAddressRegex = regexp.MustCompile(ethAddressRegexString) - ethaddressRegexUpper = regexp.MustCompile(ethAddressUpperRegexString) + ethAddressRegexUpper = regexp.MustCompile(ethAddressUpperRegexString) ethAddressRegexLower = regexp.MustCompile(ethAddressLowerRegexString) uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString) hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString) diff --git a/struct_level.go b/struct_level.go index 57691ee38..c0d89cfbe 100644 --- a/struct_level.go +++ b/struct_level.go @@ -23,18 +23,18 @@ func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx { // to validate a struct type StructLevel interface { - // returns the main validation object, in case one wants to call validations internally. + // Validator returns the main validation object, in case one wants to call validations internally. // this is so you don't have to use anonymous functions to get access to the validate // instance. Validator() *Validate - // returns the top level struct, if any + // Top returns the top level struct, if any Top() reflect.Value - // returns the current fields parent struct, if any + // Parent returns the current fields parent struct, if any Parent() reflect.Value - // returns the current struct. + // Current returns the current struct. Current() reflect.Value // ExtractType gets the actual underlying type of field value. @@ -42,7 +42,7 @@ type StructLevel interface { // underlying value and its kind. ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool) - // reports an error just by passing the field and tag information + // ReportError reports an error just by passing the field and tag information // // NOTES: // @@ -54,7 +54,7 @@ type StructLevel interface { // and process on the flip side it's up to you. ReportError(field interface{}, fieldName, structFieldName string, tag, param string) - // reports an error just by passing ValidationErrors + // ReportValidationErrors reports an error just by passing ValidationErrors // // NOTES: // diff --git a/validator_test.go b/validator_test.go index be967d4fa..db5bf739f 100644 --- a/validator_test.go +++ b/validator_test.go @@ -11126,154 +11126,154 @@ func TestBCP47LanguageTagValidation(t *testing.T) { } func TestBicIsoFormatValidation(t *testing.T) { - tests := []struct { - value string `validate:"bic"` - tag string - expected bool - }{ - {"SBICKEN1345", "bic", true}, - {"SBICKEN1", "bic", true}, - {"SBICKENY", "bic", true}, - {"SBICKEN1YYP", "bic", true}, - {"SBIC23NXXX", "bic", false}, - {"S23CKENXXXX", "bic", false}, - {"SBICKENXX", "bic", false}, - {"SBICKENXX9", "bic", false}, - {"SBICKEN13458", "bic", false}, - {"SBICKEN", "bic", false}, - } - - validate := New() - - for i, test := range tests { - - errs := validate.Var(test.value, test.tag) - - if test.expected { - if !IsEqual(errs, nil) { - t.Fatalf("Index: %d bic failed Error: %s", i, errs) - } - } else { - if IsEqual(errs, nil) { - t.Fatalf("Index: %d bic failed Error: %s", i, errs) - } else { - val := getError(errs, "", "") - if val.Tag() != "bic" { - t.Fatalf("Index: %d bic failed Error: %s", i, errs) - } - } - } - } - } + tests := []struct { + value string `validate:"bic"` + tag string + expected bool + }{ + {"SBICKEN1345", "bic", true}, + {"SBICKEN1", "bic", true}, + {"SBICKENY", "bic", true}, + {"SBICKEN1YYP", "bic", true}, + {"SBIC23NXXX", "bic", false}, + {"S23CKENXXXX", "bic", false}, + {"SBICKENXX", "bic", false}, + {"SBICKENXX9", "bic", false}, + {"SBICKEN13458", "bic", false}, + {"SBICKEN", "bic", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.value, test.tag) + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d bic failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d bic failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "bic" { + t.Fatalf("Index: %d bic failed Error: %s", i, errs) + } + } + } + } +} func TestPostCodeByIso3166Alpha2(t *testing.T) { - tests := map[string][]struct { - value string - expected bool - }{ - "VN": { - {"ABC", false}, - {"700000", true}, - {"A1", false}, - }, - "GB": { - {"EC1A 1BB", true}, - {"CF10 1B1H", false}, - }, - "VI": { - {"00803", true}, - {"1234567", false}, - }, - "LC": { // not support regexp for post code - {"123456", false}, - }, - "XX": { // not support country - {"123456", false}, - }, - } - - validate := New() - - for cc, ccTests := range tests { - for i, test := range ccTests { - errs := validate.Var(test.value, fmt.Sprintf("postcode_iso3166_alpha2=%s", cc)) - - if test.expected { - if !IsEqual(errs, nil) { - t.Fatalf("Index: %d postcode_iso3166_alpha2=%s failed Error: %s", i, cc, errs) - } - } else { - if IsEqual(errs, nil) { - t.Fatalf("Index: %d postcode_iso3166_alpha2=%s failed Error: %s", i, cc, errs) - } - } - } - } - } - - func TestPostCodeByIso3166Alpha2Field(t *testing.T) { - tests := []struct { - Value string `validate:"postcode_iso3166_alpha2_field=CountryCode"` - CountryCode interface{} - expected bool - }{ - {"ABC", "VN", false}, - {"700000", "VN", true}, - {"A1", "VN", false}, - {"EC1A 1BB", "GB", true}, - {"CF10 1B1H", "GB", false}, - {"00803", "VI", true}, - {"1234567", "VI", false}, - {"123456", "LC", false}, // not support regexp for post code - {"123456", "XX", false}, // not support country - } - - validate := New() - - for i, test := range tests { - errs := validate.Struct(test) - if test.expected { - if !IsEqual(errs, nil) { - t.Fatalf("Index: %d postcode_iso3166_alpha2_field=CountryCode failed Error: %s", i, errs) - } - } else { - if IsEqual(errs, nil) { - t.Fatalf("Index: %d postcode_iso3166_alpha2_field=CountryCode failed Error: %s", i, errs) - } - } - } - } - - func TestPostCodeByIso3166Alpha2Field_WrongField(t *testing.T) { - type test struct { - Value string `validate:"postcode_iso3166_alpha2_field=CountryCode"` - CountryCode1 interface{} - expected bool - } - - errs := New().Struct(test{"ABC", "VN", false}) - assert.NotEqual(t, nil, errs) - } - - func TestPostCodeByIso3166Alpha2Field_MissingParam(t *testing.T) { - type test struct { - Value string `validate:"postcode_iso3166_alpha2_field="` - CountryCode1 interface{} - expected bool - } - - errs := New().Struct(test{"ABC", "VN", false}) - assert.NotEqual(t, nil, errs) - } - - func TestPostCodeByIso3166Alpha2Field_InvalidKind(t *testing.T) { - type test struct { - Value string `validate:"postcode_iso3166_alpha2_field=CountryCode"` - CountryCode interface{} - expected bool - } - defer func() { recover() }() - - _ = New().Struct(test{"ABC", 123, false}) - t.Errorf("Didn't panic as expected") - } + tests := map[string][]struct { + value string + expected bool + }{ + "VN": { + {"ABC", false}, + {"700000", true}, + {"A1", false}, + }, + "GB": { + {"EC1A 1BB", true}, + {"CF10 1B1H", false}, + }, + "VI": { + {"00803", true}, + {"1234567", false}, + }, + "LC": { // not support regexp for post code + {"123456", false}, + }, + "XX": { // not support country + {"123456", false}, + }, + } + + validate := New() + + for cc, ccTests := range tests { + for i, test := range ccTests { + errs := validate.Var(test.value, fmt.Sprintf("postcode_iso3166_alpha2=%s", cc)) + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d postcode_iso3166_alpha2=%s failed Error: %s", i, cc, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d postcode_iso3166_alpha2=%s failed Error: %s", i, cc, errs) + } + } + } + } +} + +func TestPostCodeByIso3166Alpha2Field(t *testing.T) { + tests := []struct { + Value string `validate:"postcode_iso3166_alpha2_field=CountryCode"` + CountryCode interface{} + expected bool + }{ + {"ABC", "VN", false}, + {"700000", "VN", true}, + {"A1", "VN", false}, + {"EC1A 1BB", "GB", true}, + {"CF10 1B1H", "GB", false}, + {"00803", "VI", true}, + {"1234567", "VI", false}, + {"123456", "LC", false}, // not support regexp for post code + {"123456", "XX", false}, // not support country + } + + validate := New() + + for i, test := range tests { + errs := validate.Struct(test) + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d postcode_iso3166_alpha2_field=CountryCode failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d postcode_iso3166_alpha2_field=CountryCode failed Error: %s", i, errs) + } + } + } +} + +func TestPostCodeByIso3166Alpha2Field_WrongField(t *testing.T) { + type test struct { + Value string `validate:"postcode_iso3166_alpha2_field=CountryCode"` + CountryCode1 interface{} + expected bool + } + + errs := New().Struct(test{"ABC", "VN", false}) + assert.NotEqual(t, nil, errs) +} + +func TestPostCodeByIso3166Alpha2Field_MissingParam(t *testing.T) { + type test struct { + Value string `validate:"postcode_iso3166_alpha2_field="` + CountryCode1 interface{} + expected bool + } + + errs := New().Struct(test{"ABC", "VN", false}) + assert.NotEqual(t, nil, errs) +} + +func TestPostCodeByIso3166Alpha2Field_InvalidKind(t *testing.T) { + type test struct { + Value string `validate:"postcode_iso3166_alpha2_field=CountryCode"` + CountryCode interface{} + expected bool + } + defer func() { _ = recover() }() + + _ = New().Struct(test{"ABC", 123, false}) + t.Errorf("Didn't panic as expected") +}