Merge pull request #76 from joeybloggs/v5-development

Add More Validators + Add godoc examples
pull/77/head
Dean Karn 10 years ago
commit 64eb07fa30
  1. 135
      baked_in.go
  2. 163
      benchmarks_test.go
  3. 62
      doc.go
  4. 95
      examples_test.go
  5. 38
      regexes.go
  6. 2
      validator.go
  7. 615
      validator_test.go

@ -50,6 +50,141 @@ var BakedInValidators = map[string]Func{
"excludes": excludes,
"excludesall": excludesAll,
"excludesrune": excludesRune,
"isbn": isISBN,
"isbn10": isISBN10,
"isbn13": isISBN13,
"uuid": isUUID,
"uuid3": isUUID3,
"uuid4": isUUID4,
"uuid5": isUUID5,
"ascii": isASCII,
"printascii": isPrintableASCII,
"multibyte": hasMultiByteCharacter,
"datauri": isDataURI,
"latitude": isLatitude,
"longitude": isLongitude,
"ssn": isSSN,
}
func isSSN(top interface{}, current interface{}, field interface{}, param string) bool {
if len(field.(string)) != 11 {
return false
}
return matchesRegex(sSNRegex, field)
}
func isLongitude(top interface{}, current interface{}, field interface{}, param string) bool {
return matchesRegex(longitudeRegex, field)
}
func isLatitude(top interface{}, current interface{}, field interface{}, param string) bool {
return matchesRegex(latitudeRegex, field)
}
func isDataURI(top interface{}, current interface{}, field interface{}, param string) bool {
uri := strings.SplitN(field.(string), ",", 2)
if len(uri) != 2 {
return false
}
if !matchesRegex(dataURIRegex, uri[0]) {
return false
}
return isBase64(top, current, uri[1], param)
}
func hasMultiByteCharacter(top interface{}, current interface{}, field interface{}, param string) bool {
if len(field.(string)) == 0 {
return true
}
return matchesRegex(multibyteRegex, field)
}
func isPrintableASCII(top interface{}, current interface{}, field interface{}, param string) bool {
return matchesRegex(printableASCIIRegex, field)
}
func isASCII(top interface{}, current interface{}, field interface{}, param string) bool {
return matchesRegex(aSCIIRegex, field)
}
func isUUID5(top interface{}, current interface{}, field interface{}, param string) bool {
return matchesRegex(uUID5Regex, field)
}
func isUUID4(top interface{}, current interface{}, field interface{}, param string) bool {
return matchesRegex(uUID4Regex, field)
}
func isUUID3(top interface{}, current interface{}, field interface{}, param string) bool {
return matchesRegex(uUID3Regex, field)
}
func isUUID(top interface{}, current interface{}, field interface{}, param string) bool {
return matchesRegex(uUIDRegex, field)
}
func isISBN(top interface{}, current interface{}, field interface{}, param string) bool {
return isISBN10(top, current, field, param) || isISBN13(top, current, field, param)
}
func isISBN13(top interface{}, current interface{}, field interface{}, param string) bool {
s := strings.Replace(strings.Replace(field.(string), "-", "", 4), " ", "", 4)
if !matchesRegex(iSBN13Regex, s) {
return false
}
var checksum int32
var i int32
factor := []int32{1, 3}
for i = 0; i < 12; i++ {
checksum += factor[i%2] * int32(s[i]-'0')
}
if (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 {
return true
}
return false
}
func isISBN10(top interface{}, current interface{}, field interface{}, param string) bool {
s := strings.Replace(strings.Replace(field.(string), "-", "", 3), " ", "", 3)
if !matchesRegex(iSBN10Regex, s) {
return false
}
var checksum int32
var i int32
for i = 0; i < 9; i++ {
checksum += (i + 1) * int32(s[i]-'0')
}
if s[9] == 'X' {
checksum += 10 * 10
} else {
checksum += 10 * int32(s[9]-'0')
}
if checksum%11 == 0 {
return true
}
return false
}
func excludesRune(top interface{}, current interface{}, field interface{}, param string) bool {

@ -0,0 +1,163 @@
package validator
import "testing"
func BenchmarkValidateField(b *testing.B) {
for n := 0; n < b.N; n++ {
validate.Field("1", "len=1")
}
}
func BenchmarkValidateStructSimple(b *testing.B) {
type Foo struct {
StringValue string `validate:"min=5,max=10"`
IntValue int `validate:"min=5,max=10"`
}
validFoo := &Foo{StringValue: "Foobar", IntValue: 7}
invalidFoo := &Foo{StringValue: "Fo", IntValue: 3}
for n := 0; n < b.N; n++ {
validate.Struct(validFoo)
validate.Struct(invalidFoo)
}
}
// func BenchmarkTemplateParallelSimple(b *testing.B) {
// type Foo struct {
// StringValue string `validate:"min=5,max=10"`
// IntValue int `validate:"min=5,max=10"`
// }
// validFoo := &Foo{StringValue: "Foobar", IntValue: 7}
// invalidFoo := &Foo{StringValue: "Fo", IntValue: 3}
// b.RunParallel(func(pb *testing.PB) {
// for pb.Next() {
// validate.Struct(validFoo)
// validate.Struct(invalidFoo)
// }
// })
// }
func BenchmarkValidateStructLarge(b *testing.B) {
tFail := &TestString{
Required: "",
Len: "",
Min: "",
Max: "12345678901",
MinMax: "",
Lt: "0123456789",
Lte: "01234567890",
Gt: "1",
Gte: "1",
OmitEmpty: "12345678901",
Sub: &SubTest{
Test: "",
},
Anonymous: struct {
A string `validate:"required"`
}{
A: "",
},
Iface: &Impl{
F: "12",
},
}
tSuccess := &TestString{
Required: "Required",
Len: "length==10",
Min: "min=1",
Max: "1234567890",
MinMax: "12345",
Lt: "012345678",
Lte: "0123456789",
Gt: "01234567890",
Gte: "0123456789",
OmitEmpty: "",
Sub: &SubTest{
Test: "1",
},
SubIgnore: &SubTest{
Test: "",
},
Anonymous: struct {
A string `validate:"required"`
}{
A: "1",
},
Iface: &Impl{
F: "123",
},
}
for n := 0; n < b.N; n++ {
validate.Struct(tSuccess)
validate.Struct(tFail)
}
}
// func BenchmarkTemplateParallelLarge(b *testing.B) {
// tFail := &TestString{
// Required: "",
// Len: "",
// Min: "",
// Max: "12345678901",
// MinMax: "",
// Lt: "0123456789",
// Lte: "01234567890",
// Gt: "1",
// Gte: "1",
// OmitEmpty: "12345678901",
// Sub: &SubTest{
// Test: "",
// },
// Anonymous: struct {
// A string `validate:"required"`
// }{
// A: "",
// },
// Iface: &Impl{
// F: "12",
// },
// }
// tSuccess := &TestString{
// Required: "Required",
// Len: "length==10",
// Min: "min=1",
// Max: "1234567890",
// MinMax: "12345",
// Lt: "012345678",
// Lte: "0123456789",
// Gt: "01234567890",
// Gte: "0123456789",
// OmitEmpty: "",
// Sub: &SubTest{
// Test: "1",
// },
// SubIgnore: &SubTest{
// Test: "",
// },
// Anonymous: struct {
// A string `validate:"required"`
// }{
// A: "1",
// },
// Iface: &Impl{
// F: "123",
// },
// }
// b.RunParallel(func(pb *testing.PB) {
// for pb.Next() {
// validate.Struct(tSuccess)
// validate.Struct(tFail)
// }
// })
// }

@ -168,7 +168,7 @@ Here is a list of the current built in validators:
verify it has been assigned.
omitempty
Allows conitional validation, for example if a field is not set with
Allows conditional validation, for example if a field is not set with
a value (Determined by the required validator) then other validation
such as min or max won't run, but if a value is set validation will run.
(Usage: omitempty)
@ -362,6 +362,66 @@ Here is a list of the current built in validators:
This validates that a string value does not contain the supplied rune value.
(Usage: excludesrune=@)
isbn
This validates that a string value contains a valid isbn10 or isbn13 value.
(Usage: isbn)
isbn10
This validates that a string value contains a valid isbn10 value.
(Usage: isbn10)
isbn13
This validates that a string value contains a valid isbn13 value.
(Usage: isbn13)
uuid
This validates that a string value contains a valid UUID.
(Usage: uuid)
uuid3
This validates that a string value contains a valid version 3 UUID.
(Usage: uuid3)
uuid4
This validates that a string value contains a valid version 4 UUID.
(Usage: uuid4)
uuid5
This validates that a string value contains a valid version 5 UUID.
(Usage: uuid5)
ascii
This validates that a string value contains only ASCII characters.
NOTE: if the string is blank, this validates as true.
(Usage: ascii)
asciiprint
This validates that a string value contains only printable ASCII characters.
NOTE: if the string is blank, this validates as true.
(Usage: asciiprint)
multibyte
This validates that a string value contains one or more multibyte characters.
NOTE: if the string is blank, this validates as true.
(Usage: multibyte)
datauri
This validates that a string value contains a valid DataURI.
NOTE: this will also validate that the data portion is valid base64
(Usage: datauri)
latitude
This validates that a string value contains a valid latitude.
(Usage: latitude)
longitude
This validates that a string value contains a valid longitude.
(Usage: longitude)
ssn
This validates that a string value contains a valid U.S. Social Security Number.
(Usage: ssn)
Validator notes:
regex

@ -0,0 +1,95 @@
package validator_test
import (
"fmt"
"../validator"
)
func ExampleValidate_new() {
validator.New("validate", validator.BakedInValidators)
}
func ExampleValidate_addFunction() {
// This should be stored somewhere globally
var validate *validator.Validate
validate = validator.New("validate", validator.BakedInValidators)
fn := func(top interface{}, current interface{}, field interface{}, param string) bool {
return field.(string) == "hello"
}
validate.AddFunction("valueishello", fn)
message := "hello"
err := validate.Field(message, "valueishello")
fmt.Println(err)
//Output:
//<nil>
}
func ExampleValidate_field() {
// This should be stored somewhere globally
var validate *validator.Validate
validate = validator.New("validate", validator.BakedInValidators)
i := 0
err := validate.Field(i, "gt=1,lte=10")
fmt.Println(err.Field)
fmt.Println(err.Tag)
fmt.Println(err.Kind) // NOTE: Kind and Type can be different i.e. time Kind=struct and Type=time.Time
fmt.Println(err.Type)
fmt.Println(err.Param)
fmt.Println(err.Value)
//Output:
//
//gt
//int
//int
//1
//0
}
func ExampleValidate_struct() {
// This should be stored somewhere globally
var validate *validator.Validate
validate = validator.New("validate", validator.BakedInValidators)
type ContactInformation struct {
Phone string `validate:"required"`
Street string `validate:"required"`
City string `validate:"required"`
}
type User struct {
Name string `validate:"required,excludesall=!@#$%^&*()_+-=:;?/0x2C"` // 0x2C = comma (,)
Age int8 `validate:"required,gt=0,lt=150"`
Email string `validate:"email"`
ContactInformation []*ContactInformation
}
contactInfo := &ContactInformation{
Street: "26 Here Blvd.",
City: "Paradeso",
}
user := &User{
Name: "Joey Bloggs",
Age: 31,
Email: "joeybloggs@gmail.com",
ContactInformation: []*ContactInformation{contactInfo},
}
structError := validate.Struct(user)
for _, fieldError := range structError.Errors {
fmt.Println(fieldError.Field) // Phone
fmt.Println(fieldError.Tag) // required
//... and so forth
//Output:
//Phone
//required
}
}

@ -9,12 +9,25 @@ const (
numberRegexString = "^[0-9]+$"
hexadecimalRegexString = "^[0-9a-fA-F]+$"
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])\\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])\\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*\\)$"
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}])))\\.?$"
base64RegexString = "(?:^(?:[A-Za-z0-9+\\/]{4}\\n?)*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=)$)"
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*\\)$"
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}])))\\.?$"
base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$"
iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$"
uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
aSCIIRegexString = "^[\x00-\x7F]*$"
printableASCIIRegexString = "^[\x20-\x7E]*$"
multibyteRegexString = "[^\x00-\x7F]"
dataURIRegexString = "^data:.+\\/(.+);base64$"
latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
sSNRegexString = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
)
var (
@ -30,6 +43,19 @@ var (
hslaRegex = regexp.MustCompile(hslaRegexString)
emailRegex = regexp.MustCompile(emailRegexString)
base64Regex = regexp.MustCompile(base64RegexString)
iSBN10Regex = regexp.MustCompile(iSBN10RegexString)
iSBN13Regex = regexp.MustCompile(iSBN13RegexString)
uUID3Regex = regexp.MustCompile(uUID3RegexString)
uUID4Regex = regexp.MustCompile(uUID4RegexString)
uUID5Regex = regexp.MustCompile(uUID5RegexString)
uUIDRegex = regexp.MustCompile(uUIDRegexString)
aSCIIRegex = regexp.MustCompile(aSCIIRegexString)
printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString)
multibyteRegex = regexp.MustCompile(multibyteRegexString)
dataURIRegex = regexp.MustCompile(dataURIRegexString)
latitudeRegex = regexp.MustCompile(latitudeRegexString)
longitudeRegex = regexp.MustCompile(longitudeRegexString)
sSNRegex = regexp.MustCompile(sSNRegexString)
)
func matchesRegex(regex *regexp.Regexp, field interface{}) bool {

@ -246,7 +246,7 @@ func (v *Validate) SetTag(tagName string) {
v.tagName = tagName
}
// SetStructPoolMax sets the struct pools max size. this may be usefull for fine grained
// SetMaxStructPoolSize sets the struct pools max size. this may be usefull for fine grained
// performance tuning towards your application, however, the default should be fine for
// nearly all cases. only increase if you have a deeply nested struct structure.
// NOTE: this method is not thread-safe

@ -226,6 +226,443 @@ func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, e
EqualSkip(t, 2, val.Tag, expectedTag)
}
func TestSSNValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"00-90-8787", false},
{"66690-76", false},
{"191 60 2869", true},
{"191-60-2869", true},
}
for i, test := range tests {
err := validate.Field(test.param, "ssn")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d SSN failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "ssn") {
t.Fatalf("Index: %d SSN failed Error: %s", i, err)
}
}
}
}
func TestLongitudeValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"-180.000", true},
{"180.1", false},
{"+73.234", true},
{"+382.3811", false},
{"23.11111111", true},
}
for i, test := range tests {
err := validate.Field(test.param, "longitude")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d Longitude failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "longitude") {
t.Fatalf("Index: %d Longitude failed Error: %s", i, err)
}
}
}
}
func TestLatitudeValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"-90.000", true},
{"+90", true},
{"47.1231231", true},
{"+99.9", false},
{"108", false},
}
for i, test := range tests {
err := validate.Field(test.param, "latitude")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d Latitude failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "latitude") {
t.Fatalf("Index: %d Latitude failed Error: %s", i, err)
}
}
}
}
func TestDataURIValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"data:image/png;base64,TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=", true},
{"data:text/plain;base64,Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==", true},
{"image/gif;base64,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false},
{"data:image/gif;base64,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw" +
"UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye" +
"rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619" +
"FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx" +
"QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ" +
"Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ" + "HQIDAQAB", true},
{"data:image/png;base64,12345", false},
{"", false},
{"data:text,:;base85,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false},
}
for i, test := range tests {
err := validate.Field(test.param, "datauri")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d DataURI failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "datauri") {
t.Fatalf("Index: %d DataURI failed Error: %s", i, err)
}
}
}
}
func TestMultibyteValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", true},
{"abc", false},
{"123", false},
{"<>@;.-=", false},
{"ひらがな・カタカナ、.漢字", true},
{"あいうえお foobar", true},
{"test@example.com", true},
{"test@example.com", true},
{"1234abcDExyz", true},
{"カタカナ", true},
}
for i, test := range tests {
err := validate.Field(test.param, "multibyte")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d Multibyte failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "multibyte") {
t.Fatalf("Index: %d Multibyte failed Error: %s", i, err)
}
}
}
}
func TestPrintableASCIIValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", true},
{"foobar", false},
{"xyz098", false},
{"123456", false},
{"カタカナ", false},
{"foobar", true},
{"0987654321", true},
{"test@example.com", true},
{"1234abcDEF", true},
{"newline\n", false},
{"\x19test\x7F", false},
}
for i, test := range tests {
err := validate.Field(test.param, "printascii")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "printascii") {
t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, err)
}
}
}
}
func TestASCIIValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", true},
{"foobar", false},
{"xyz098", false},
{"123456", false},
{"カタカナ", false},
{"foobar", true},
{"0987654321", true},
{"test@example.com", true},
{"1234abcDEF", true},
{"", true},
}
for i, test := range tests {
err := validate.Field(test.param, "ascii")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d ASCII failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "ascii") {
t.Fatalf("Index: %d ASCII failed Error: %s", i, err)
}
}
}
}
func TestUUID5Validation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false},
{"9c858901-8a57-4791-81fe-4c455b099bc9", false},
{"a987fbc9-4bed-3078-cf07-9141ba07c9f3", false},
{"987fbc97-4bed-5078-af07-9141ba07c9f3", true},
{"987fbc97-4bed-5078-9f07-9141ba07c9f3", true},
}
for i, test := range tests {
err := validate.Field(test.param, "uuid5")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d UUID5 failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid5") {
t.Fatalf("Index: %d UUID5 failed Error: %s", i, err)
}
}
}
}
func TestUUID4Validation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false},
{"a987fbc9-4bed-5078-af07-9141ba07c9f3", false},
{"934859", false},
{"57b73598-8764-4ad0-a76a-679bb6640eb1", true},
{"625e63f3-58f5-40b7-83a1-a72ad31acffb", true},
}
for i, test := range tests {
err := validate.Field(test.param, "uuid4")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d UUID4 failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid4") {
t.Fatalf("Index: %d UUID4 failed Error: %s", i, err)
}
}
}
}
func TestUUID3Validation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"412452646", false},
{"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false},
{"a987fbc9-4bed-4078-8f07-9141ba07c9f3", false},
{"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true},
}
for i, test := range tests {
err := validate.Field(test.param, "uuid3")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d UUID3 failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid3") {
t.Fatalf("Index: %d UUID3 failed Error: %s", i, err)
}
}
}
}
func TestUUIDValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false},
{"a987fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false},
{"a987fbc94bed3078cf079141ba07c9f3", false},
{"934859", false},
{"987fbc9-4bed-3078-cf07a-9141ba07c9f3", false},
{"aaaaaaaa-1111-1111-aaag-111111111111", false},
{"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true},
}
for i, test := range tests {
err := validate.Field(test.param, "uuid")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d UUID failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid") {
t.Fatalf("Index: %d UUID failed Error: %s", i, err)
}
}
}
}
func TestISBNValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"foo", false},
{"3836221195", true},
{"1-61729-085-8", true},
{"3 423 21412 0", true},
{"3 401 01319 X", true},
{"9784873113685", true},
{"978-4-87311-368-5", true},
{"978 3401013190", true},
{"978-3-8362-2119-1", true},
}
for i, test := range tests {
err := validate.Field(test.param, "isbn")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d ISBN failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn") {
t.Fatalf("Index: %d ISBN failed Error: %s", i, err)
}
}
}
}
func TestISBN13Validation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"foo", false},
{"3-8362-2119-5", false},
{"01234567890ab", false},
{"978 3 8362 2119 0", false},
{"9784873113685", true},
{"978-4-87311-368-5", true},
{"978 3401013190", true},
{"978-3-8362-2119-1", true},
}
for i, test := range tests {
err := validate.Field(test.param, "isbn13")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn13") {
t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err)
}
}
}
}
func TestISBN10Validation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"foo", false},
{"3423214121", false},
{"978-3836221191", false},
{"3-423-21412-1", false},
{"3 423 21412 1", false},
{"3836221195", true},
{"1-61729-085-8", true},
{"3 423 21412 0", true},
{"3 401 01319 X", true},
}
for i, test := range tests {
err := validate.Field(test.param, "isbn10")
if test.expected == true {
if !IsEqual(t, err, nil) {
t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err)
}
} else {
if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn10") {
t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err)
}
}
}
}
func TestExcludesRuneValidation(t *testing.T) {
tests := []struct {
@ -1736,10 +2173,19 @@ func TestRgba(t *testing.T) {
err = validate.Field(s, "rgba")
Equal(t, err, nil)
s = "rgba(12%,55%,100%,0.12)"
err = validate.Field(s, "rgba")
Equal(t, err, nil)
s = "rgba( 0, 31, 255, 0.5)"
err = validate.Field(s, "rgba")
Equal(t, err, nil)
s = "rgba(12%,55,100%,0.12)"
err = validate.Field(s, "rgba")
NotEqual(t, err, nil)
Equal(t, err.Tag, "rgba")
s = "rgb(0, 31, 255)"
err = validate.Field(s, "rgba")
NotEqual(t, err, nil)
@ -1769,6 +2215,15 @@ func TestRgb(t *testing.T) {
err = validate.Field(s, "rgb")
Equal(t, err, nil)
s = "rgb(10%, 50%, 100%)"
err = validate.Field(s, "rgb")
Equal(t, err, nil)
s = "rgb(10%, 50%, 55)"
err = validate.Field(s, "rgb")
NotEqual(t, err, nil)
Equal(t, err.Tag, "rgb")
s = "rgb(1,349,275)"
err = validate.Field(s, "rgb")
NotEqual(t, err, nil)
@ -2335,163 +2790,3 @@ func TestInvalidValidatorFunction(t *testing.T) {
PanicMatches(t, func() { validate.Field(s.Test, "zzxxBadFunction") }, fmt.Sprintf("Undefined validation function on field %s", ""))
}
func BenchmarkValidateField(b *testing.B) {
for n := 0; n < b.N; n++ {
validate.Field("1", "len=1")
}
}
func BenchmarkValidateStructSimple(b *testing.B) {
type Foo struct {
StringValue string `validate:"min=5,max=10"`
IntValue int `validate:"min=5,max=10"`
}
validFoo := &Foo{StringValue: "Foobar", IntValue: 7}
invalidFoo := &Foo{StringValue: "Fo", IntValue: 3}
for n := 0; n < b.N; n++ {
validate.Struct(validFoo)
validate.Struct(invalidFoo)
}
}
// func BenchmarkTemplateParallelSimple(b *testing.B) {
// type Foo struct {
// StringValue string `validate:"min=5,max=10"`
// IntValue int `validate:"min=5,max=10"`
// }
// validFoo := &Foo{StringValue: "Foobar", IntValue: 7}
// invalidFoo := &Foo{StringValue: "Fo", IntValue: 3}
// b.RunParallel(func(pb *testing.PB) {
// for pb.Next() {
// validate.Struct(validFoo)
// validate.Struct(invalidFoo)
// }
// })
// }
func BenchmarkValidateStructLarge(b *testing.B) {
tFail := &TestString{
Required: "",
Len: "",
Min: "",
Max: "12345678901",
MinMax: "",
Lt: "0123456789",
Lte: "01234567890",
Gt: "1",
Gte: "1",
OmitEmpty: "12345678901",
Sub: &SubTest{
Test: "",
},
Anonymous: struct {
A string `validate:"required"`
}{
A: "",
},
Iface: &Impl{
F: "12",
},
}
tSuccess := &TestString{
Required: "Required",
Len: "length==10",
Min: "min=1",
Max: "1234567890",
MinMax: "12345",
Lt: "012345678",
Lte: "0123456789",
Gt: "01234567890",
Gte: "0123456789",
OmitEmpty: "",
Sub: &SubTest{
Test: "1",
},
SubIgnore: &SubTest{
Test: "",
},
Anonymous: struct {
A string `validate:"required"`
}{
A: "1",
},
Iface: &Impl{
F: "123",
},
}
for n := 0; n < b.N; n++ {
validate.Struct(tSuccess)
validate.Struct(tFail)
}
}
// func BenchmarkTemplateParallelLarge(b *testing.B) {
// tFail := &TestString{
// Required: "",
// Len: "",
// Min: "",
// Max: "12345678901",
// MinMax: "",
// Lt: "0123456789",
// Lte: "01234567890",
// Gt: "1",
// Gte: "1",
// OmitEmpty: "12345678901",
// Sub: &SubTest{
// Test: "",
// },
// Anonymous: struct {
// A string `validate:"required"`
// }{
// A: "",
// },
// Iface: &Impl{
// F: "12",
// },
// }
// tSuccess := &TestString{
// Required: "Required",
// Len: "length==10",
// Min: "min=1",
// Max: "1234567890",
// MinMax: "12345",
// Lt: "012345678",
// Lte: "0123456789",
// Gt: "01234567890",
// Gte: "0123456789",
// OmitEmpty: "",
// Sub: &SubTest{
// Test: "1",
// },
// SubIgnore: &SubTest{
// Test: "",
// },
// Anonymous: struct {
// A string `validate:"required"`
// }{
// A: "1",
// },
// Iface: &Impl{
// F: "123",
// },
// }
// b.RunParallel(func(pb *testing.PB) {
// for pb.Next() {
// validate.Struct(tSuccess)
// validate.Struct(tFail)
// }
// })
// }

Loading…
Cancel
Save