Merge pull request #76 from joeybloggs/v5-development
Add More Validators + Add godoc examples
Dean Karn
8 years ago
49 | 49 | "excludes": excludes, |
50 | 50 | "excludesall": excludesAll, |
51 | 51 | "excludesrune": excludesRune, |
52 | "isbn": isISBN, | |
53 | "isbn10": isISBN10, | |
54 | "isbn13": isISBN13, | |
55 | "uuid": isUUID, | |
56 | "uuid3": isUUID3, | |
57 | "uuid4": isUUID4, | |
58 | "uuid5": isUUID5, | |
59 | "ascii": isASCII, | |
60 | "printascii": isPrintableASCII, | |
61 | "multibyte": hasMultiByteCharacter, | |
62 | "datauri": isDataURI, | |
63 | "latitude": isLatitude, | |
64 | "longitude": isLongitude, | |
65 | "ssn": isSSN, | |
66 | } | |
67 | ||
68 | func isSSN(top interface{}, current interface{}, field interface{}, param string) bool { | |
69 | ||
70 | if len(field.(string)) != 11 { | |
71 | return false | |
72 | } | |
73 | ||
74 | return matchesRegex(sSNRegex, field) | |
75 | } | |
76 | ||
77 | func isLongitude(top interface{}, current interface{}, field interface{}, param string) bool { | |
78 | return matchesRegex(longitudeRegex, field) | |
79 | } | |
80 | ||
81 | func isLatitude(top interface{}, current interface{}, field interface{}, param string) bool { | |
82 | return matchesRegex(latitudeRegex, field) | |
83 | } | |
84 | ||
85 | func isDataURI(top interface{}, current interface{}, field interface{}, param string) bool { | |
86 | ||
87 | uri := strings.SplitN(field.(string), ",", 2) | |
88 | ||
89 | if len(uri) != 2 { | |
90 | return false | |
91 | } | |
92 | ||
93 | if !matchesRegex(dataURIRegex, uri[0]) { | |
94 | return false | |
95 | } | |
96 | ||
97 | return isBase64(top, current, uri[1], param) | |
98 | } | |
99 | ||
100 | func hasMultiByteCharacter(top interface{}, current interface{}, field interface{}, param string) bool { | |
101 | ||
102 | if len(field.(string)) == 0 { | |
103 | return true | |
104 | } | |
105 | ||
106 | return matchesRegex(multibyteRegex, field) | |
107 | } | |
108 | ||
109 | func isPrintableASCII(top interface{}, current interface{}, field interface{}, param string) bool { | |
110 | return matchesRegex(printableASCIIRegex, field) | |
111 | } | |
112 | ||
113 | func isASCII(top interface{}, current interface{}, field interface{}, param string) bool { | |
114 | return matchesRegex(aSCIIRegex, field) | |
115 | } | |
116 | ||
117 | func isUUID5(top interface{}, current interface{}, field interface{}, param string) bool { | |
118 | return matchesRegex(uUID5Regex, field) | |
119 | } | |
120 | ||
121 | func isUUID4(top interface{}, current interface{}, field interface{}, param string) bool { | |
122 | return matchesRegex(uUID4Regex, field) | |
123 | } | |
124 | ||
125 | func isUUID3(top interface{}, current interface{}, field interface{}, param string) bool { | |
126 | return matchesRegex(uUID3Regex, field) | |
127 | } | |
128 | ||
129 | func isUUID(top interface{}, current interface{}, field interface{}, param string) bool { | |
130 | return matchesRegex(uUIDRegex, field) | |
131 | } | |
132 | ||
133 | func isISBN(top interface{}, current interface{}, field interface{}, param string) bool { | |
134 | return isISBN10(top, current, field, param) || isISBN13(top, current, field, param) | |
135 | } | |
136 | ||
137 | func isISBN13(top interface{}, current interface{}, field interface{}, param string) bool { | |
138 | ||
139 | s := strings.Replace(strings.Replace(field.(string), "-", "", 4), " ", "", 4) | |
140 | ||
141 | if !matchesRegex(iSBN13Regex, s) { | |
142 | return false | |
143 | } | |
144 | ||
145 | var checksum int32 | |
146 | var i int32 | |
147 | ||
148 | factor := []int32{1, 3} | |
149 | ||
150 | for i = 0; i < 12; i++ { | |
151 | checksum += factor[i%2] * int32(s[i]-'0') | |
152 | } | |
153 | ||
154 | if (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 { | |
155 | return true | |
156 | } | |
157 | ||
158 | return false | |
159 | } | |
160 | ||
161 | func isISBN10(top interface{}, current interface{}, field interface{}, param string) bool { | |
162 | ||
163 | s := strings.Replace(strings.Replace(field.(string), "-", "", 3), " ", "", 3) | |
164 | ||
165 | if !matchesRegex(iSBN10Regex, s) { | |
166 | return false | |
167 | } | |
168 | ||
169 | var checksum int32 | |
170 | var i int32 | |
171 | ||
172 | for i = 0; i < 9; i++ { | |
173 | checksum += (i + 1) * int32(s[i]-'0') | |
174 | } | |
175 | ||
176 | if s[9] == 'X' { | |
177 | checksum += 10 * 10 | |
178 | } else { | |
179 | checksum += 10 * int32(s[9]-'0') | |
180 | } | |
181 | ||
182 | if checksum%11 == 0 { | |
183 | return true | |
184 | } | |
185 | ||
186 | return false | |
52 | 187 | } |
53 | 188 | |
54 | 189 | func excludesRune(top interface{}, current interface{}, field interface{}, param string) bool { |
0 | package validator | |
1 | ||
2 | import "testing" | |
3 | ||
4 | func BenchmarkValidateField(b *testing.B) { | |
5 | for n := 0; n < b.N; n++ { | |
6 | validate.Field("1", "len=1") | |
7 | } | |
8 | } | |
9 | ||
10 | func BenchmarkValidateStructSimple(b *testing.B) { | |
11 | ||
12 | type Foo struct { | |
13 | StringValue string `validate:"min=5,max=10"` | |
14 | IntValue int `validate:"min=5,max=10"` | |
15 | } | |
16 | ||
17 | validFoo := &Foo{StringValue: "Foobar", IntValue: 7} | |
18 | invalidFoo := &Foo{StringValue: "Fo", IntValue: 3} | |
19 | ||
20 | for n := 0; n < b.N; n++ { | |
21 | validate.Struct(validFoo) | |
22 | validate.Struct(invalidFoo) | |
23 | } | |
24 | } | |
25 | ||
26 | // func BenchmarkTemplateParallelSimple(b *testing.B) { | |
27 | ||
28 | // type Foo struct { | |
29 | // StringValue string `validate:"min=5,max=10"` | |
30 | // IntValue int `validate:"min=5,max=10"` | |
31 | // } | |
32 | ||
33 | // validFoo := &Foo{StringValue: "Foobar", IntValue: 7} | |
34 | // invalidFoo := &Foo{StringValue: "Fo", IntValue: 3} | |
35 | ||
36 | // b.RunParallel(func(pb *testing.PB) { | |
37 | // for pb.Next() { | |
38 | // validate.Struct(validFoo) | |
39 | // validate.Struct(invalidFoo) | |
40 | // } | |
41 | // }) | |
42 | // } | |
43 | ||
44 | func BenchmarkValidateStructLarge(b *testing.B) { | |
45 | ||
46 | tFail := &TestString{ | |
47 | Required: "", | |
48 | Len: "", | |
49 | Min: "", | |
50 | Max: "12345678901", | |
51 | MinMax: "", | |
52 | Lt: "0123456789", | |
53 | Lte: "01234567890", | |
54 | Gt: "1", | |
55 | Gte: "1", | |
56 | OmitEmpty: "12345678901", | |
57 | Sub: &SubTest{ | |
58 | Test: "", | |
59 | }, | |
60 | Anonymous: struct { | |
61 | A string `validate:"required"` | |
62 | }{ | |
63 | A: "", | |
64 | }, | |
65 | Iface: &Impl{ | |
66 | F: "12", | |
67 | }, | |
68 | } | |
69 | ||
70 | tSuccess := &TestString{ | |
71 | Required: "Required", | |
72 | Len: "length==10", | |
73 | Min: "min=1", | |
74 | Max: "1234567890", | |
75 | MinMax: "12345", | |
76 | Lt: "012345678", | |
77 | Lte: "0123456789", | |
78 | Gt: "01234567890", | |
79 | Gte: "0123456789", | |
80 | OmitEmpty: "", | |
81 | Sub: &SubTest{ | |
82 | Test: "1", | |
83 | }, | |
84 | SubIgnore: &SubTest{ | |
85 | Test: "", | |
86 | }, | |
87 | Anonymous: struct { | |
88 | A string `validate:"required"` | |
89 | }{ | |
90 | A: "1", | |
91 | }, | |
92 | Iface: &Impl{ | |
93 | F: "123", | |
94 | }, | |
95 | } | |
96 | ||
97 | for n := 0; n < b.N; n++ { | |
98 | validate.Struct(tSuccess) | |
99 | validate.Struct(tFail) | |
100 | } | |
101 | } | |
102 | ||
103 | // func BenchmarkTemplateParallelLarge(b *testing.B) { | |
104 | ||
105 | // tFail := &TestString{ | |
106 | // Required: "", | |
107 | // Len: "", | |
108 | // Min: "", | |
109 | // Max: "12345678901", | |
110 | // MinMax: "", | |
111 | // Lt: "0123456789", | |
112 | // Lte: "01234567890", | |
113 | // Gt: "1", | |
114 | // Gte: "1", | |
115 | // OmitEmpty: "12345678901", | |
116 | // Sub: &SubTest{ | |
117 | // Test: "", | |
118 | // }, | |
119 | // Anonymous: struct { | |
120 | // A string `validate:"required"` | |
121 | // }{ | |
122 | // A: "", | |
123 | // }, | |
124 | // Iface: &Impl{ | |
125 | // F: "12", | |
126 | // }, | |
127 | // } | |
128 | ||
129 | // tSuccess := &TestString{ | |
130 | // Required: "Required", | |
131 | // Len: "length==10", | |
132 | // Min: "min=1", | |
133 | // Max: "1234567890", | |
134 | // MinMax: "12345", | |
135 | // Lt: "012345678", | |
136 | // Lte: "0123456789", | |
137 | // Gt: "01234567890", | |
138 | // Gte: "0123456789", | |
139 | // OmitEmpty: "", | |
140 | // Sub: &SubTest{ | |
141 | // Test: "1", | |
142 | // }, | |
143 | // SubIgnore: &SubTest{ | |
144 | // Test: "", | |
145 | // }, | |
146 | // Anonymous: struct { | |
147 | // A string `validate:"required"` | |
148 | // }{ | |
149 | // A: "1", | |
150 | // }, | |
151 | // Iface: &Impl{ | |
152 | // F: "123", | |
153 | // }, | |
154 | // } | |
155 | ||
156 | // b.RunParallel(func(pb *testing.PB) { | |
157 | // for pb.Next() { | |
158 | // validate.Struct(tSuccess) | |
159 | // validate.Struct(tFail) | |
160 | // } | |
161 | // }) | |
162 | // } |
167 | 167 | verify it has been assigned. |
168 | 168 | |
169 | 169 | omitempty |
170 | Allows conitional validation, for example if a field is not set with | |
170 | Allows conditional validation, for example if a field is not set with | |
171 | 171 | a value (Determined by the required validator) then other validation |
172 | 172 | such as min or max won't run, but if a value is set validation will run. |
173 | 173 | (Usage: omitempty) |
361 | 361 | This validates that a string value does not contain the supplied rune value. |
362 | 362 | (Usage: excludesrune=@) |
363 | 363 | |
364 | isbn | |
365 | This validates that a string value contains a valid isbn10 or isbn13 value. | |
366 | (Usage: isbn) | |
367 | ||
368 | isbn10 | |
369 | This validates that a string value contains a valid isbn10 value. | |
370 | (Usage: isbn10) | |
371 | ||
372 | isbn13 | |
373 | This validates that a string value contains a valid isbn13 value. | |
374 | (Usage: isbn13) | |
375 | ||
376 | uuid | |
377 | This validates that a string value contains a valid UUID. | |
378 | (Usage: uuid) | |
379 | ||
380 | uuid3 | |
381 | This validates that a string value contains a valid version 3 UUID. | |
382 | (Usage: uuid3) | |
383 | ||
384 | uuid4 | |
385 | This validates that a string value contains a valid version 4 UUID. | |
386 | (Usage: uuid4) | |
387 | ||
388 | uuid5 | |
389 | This validates that a string value contains a valid version 5 UUID. | |
390 | (Usage: uuid5) | |
391 | ||
392 | ascii | |
393 | This validates that a string value contains only ASCII characters. | |
394 | NOTE: if the string is blank, this validates as true. | |
395 | (Usage: ascii) | |
396 | ||
397 | asciiprint | |
398 | This validates that a string value contains only printable ASCII characters. | |
399 | NOTE: if the string is blank, this validates as true. | |
400 | (Usage: asciiprint) | |
401 | ||
402 | multibyte | |
403 | This validates that a string value contains one or more multibyte characters. | |
404 | NOTE: if the string is blank, this validates as true. | |
405 | (Usage: multibyte) | |
406 | ||
407 | datauri | |
408 | This validates that a string value contains a valid DataURI. | |
409 | NOTE: this will also validate that the data portion is valid base64 | |
410 | (Usage: datauri) | |
411 | ||
412 | latitude | |
413 | This validates that a string value contains a valid latitude. | |
414 | (Usage: latitude) | |
415 | ||
416 | longitude | |
417 | This validates that a string value contains a valid longitude. | |
418 | (Usage: longitude) | |
419 | ||
420 | ssn | |
421 | This validates that a string value contains a valid U.S. Social Security Number. | |
422 | (Usage: ssn) | |
423 | ||
364 | 424 | Validator notes: |
365 | 425 | |
366 | 426 | regex |
0 | package validator_test | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | ||
5 | "../validator" | |
6 | ) | |
7 | ||
8 | func ExampleValidate_new() { | |
9 | validator.New("validate", validator.BakedInValidators) | |
10 | } | |
11 | ||
12 | func ExampleValidate_addFunction() { | |
13 | // This should be stored somewhere globally | |
14 | var validate *validator.Validate | |
15 | ||
16 | validate = validator.New("validate", validator.BakedInValidators) | |
17 | ||
18 | fn := func(top interface{}, current interface{}, field interface{}, param string) bool { | |
19 | return field.(string) == "hello" | |
20 | } | |
21 | ||
22 | validate.AddFunction("valueishello", fn) | |
23 | ||
24 | message := "hello" | |
25 | err := validate.Field(message, "valueishello") | |
26 | fmt.Println(err) | |
27 | //Output: | |
28 | //<nil> | |
29 | } | |
30 | ||
31 | func ExampleValidate_field() { | |
32 | // This should be stored somewhere globally | |
33 | var validate *validator.Validate | |
34 | ||
35 | validate = validator.New("validate", validator.BakedInValidators) | |
36 | ||
37 | i := 0 | |
38 | err := validate.Field(i, "gt=1,lte=10") | |
39 | fmt.Println(err.Field) | |
40 | fmt.Println(err.Tag) | |
41 | fmt.Println(err.Kind) // NOTE: Kind and Type can be different i.e. time Kind=struct and Type=time.Time | |
42 | fmt.Println(err.Type) | |
43 | fmt.Println(err.Param) | |
44 | fmt.Println(err.Value) | |
45 | //Output: | |
46 | // | |
47 | //gt | |
48 | //int | |
49 | //int | |
50 | //1 | |
51 | //0 | |
52 | } | |
53 | ||
54 | func ExampleValidate_struct() { | |
55 | // This should be stored somewhere globally | |
56 | var validate *validator.Validate | |
57 | ||
58 | validate = validator.New("validate", validator.BakedInValidators) | |
59 | ||
60 | type ContactInformation struct { | |
61 | Phone string `validate:"required"` | |
62 | Street string `validate:"required"` | |
63 | City string `validate:"required"` | |
64 | } | |
65 | ||
66 | type User struct { | |
67 | Name string `validate:"required,excludesall=!@#$%^&*()_+-=:;?/0x2C"` // 0x2C = comma (,) | |
68 | Age int8 `validate:"required,gt=0,lt=150"` | |
69 | Email string `validate:"email"` | |
70 | ContactInformation []*ContactInformation | |
71 | } | |
72 | ||
73 | contactInfo := &ContactInformation{ | |
74 | Street: "26 Here Blvd.", | |
75 | City: "Paradeso", | |
76 | } | |
77 | ||
78 | user := &User{ | |
79 | Name: "Joey Bloggs", | |
80 | Age: 31, | |
81 | Email: "joeybloggs@gmail.com", | |
82 | ContactInformation: []*ContactInformation{contactInfo}, | |
83 | } | |
84 | ||
85 | structError := validate.Struct(user) | |
86 | for _, fieldError := range structError.Errors { | |
87 | fmt.Println(fieldError.Field) // Phone | |
88 | fmt.Println(fieldError.Tag) // required | |
89 | //... and so forth | |
90 | //Output: | |
91 | //Phone | |
92 | //required | |
93 | } | |
94 | } |
2 | 2 | import "regexp" |
3 | 3 | |
4 | 4 | const ( |
5 | alphaRegexString = "^[a-zA-Z]+$" | |
6 | alphaNumericRegexString = "^[a-zA-Z0-9]+$" | |
7 | numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" | |
8 | numberRegexString = "^[0-9]+$" | |
9 | hexadecimalRegexString = "^[0-9a-fA-F]+$" | |
10 | hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" | |
11 | 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*\\)$" | |
12 | 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*\\)$" | |
13 | 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*\\)$" | |
14 | 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*\\)$" | |
15 | 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}])))\\.?$" | |
16 | base64RegexString = "(?:^(?:[A-Za-z0-9+\\/]{4}\\n?)*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=)$)" | |
5 | alphaRegexString = "^[a-zA-Z]+$" | |
6 | alphaNumericRegexString = "^[a-zA-Z0-9]+$" | |
7 | numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" | |
8 | numberRegexString = "^[0-9]+$" | |
9 | hexadecimalRegexString = "^[0-9a-fA-F]+$" | |
10 | hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" | |
11 | 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*\\)$" | |
12 | 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*\\)$" | |
13 | 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*\\)$" | |
14 | 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*\\)$" | |
15 | 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}])))\\.?$" | |
16 | base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" | |
17 | iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$" | |
18 | iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$" | |
19 | uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" | |
20 | uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" | |
21 | uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" | |
22 | uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" | |
23 | aSCIIRegexString = "^[\x00-\x7F]*$" | |
24 | printableASCIIRegexString = "^[\x20-\x7E]*$" | |
25 | multibyteRegexString = "[^\x00-\x7F]" | |
26 | dataURIRegexString = "^data:.+\\/(.+);base64$" | |
27 | latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" | |
28 | longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" | |
29 | sSNRegexString = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` | |
17 | 30 | ) |
18 | 31 | |
19 | 32 | var ( |
20 | alphaRegex = regexp.MustCompile(alphaRegexString) | |
21 | alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString) | |
22 | numericRegex = regexp.MustCompile(numericRegexString) | |
23 | numberRegex = regexp.MustCompile(numberRegexString) | |
24 | hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString) | |
25 | hexcolorRegex = regexp.MustCompile(hexcolorRegexString) | |
26 | rgbRegex = regexp.MustCompile(rgbRegexString) | |
27 | rgbaRegex = regexp.MustCompile(rgbaRegexString) | |
28 | hslRegex = regexp.MustCompile(hslRegexString) | |
29 | hslaRegex = regexp.MustCompile(hslaRegexString) | |
30 | emailRegex = regexp.MustCompile(emailRegexString) | |
31 | base64Regex = regexp.MustCompile(base64RegexString) | |
33 | alphaRegex = regexp.MustCompile(alphaRegexString) | |
34 | alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString) | |
35 | numericRegex = regexp.MustCompile(numericRegexString) | |
36 | numberRegex = regexp.MustCompile(numberRegexString) | |
37 | hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString) | |
38 | hexcolorRegex = regexp.MustCompile(hexcolorRegexString) | |
39 | rgbRegex = regexp.MustCompile(rgbRegexString) | |
40 | rgbaRegex = regexp.MustCompile(rgbaRegexString) | |
41 | hslRegex = regexp.MustCompile(hslRegexString) | |
42 | hslaRegex = regexp.MustCompile(hslaRegexString) | |
43 | emailRegex = regexp.MustCompile(emailRegexString) | |
44 | base64Regex = regexp.MustCompile(base64RegexString) | |
45 | iSBN10Regex = regexp.MustCompile(iSBN10RegexString) | |
46 | iSBN13Regex = regexp.MustCompile(iSBN13RegexString) | |
47 | uUID3Regex = regexp.MustCompile(uUID3RegexString) | |
48 | uUID4Regex = regexp.MustCompile(uUID4RegexString) | |
49 | uUID5Regex = regexp.MustCompile(uUID5RegexString) | |
50 | uUIDRegex = regexp.MustCompile(uUIDRegexString) | |
51 | aSCIIRegex = regexp.MustCompile(aSCIIRegexString) | |
52 | printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString) | |
53 | multibyteRegex = regexp.MustCompile(multibyteRegexString) | |
54 | dataURIRegex = regexp.MustCompile(dataURIRegexString) | |
55 | latitudeRegex = regexp.MustCompile(latitudeRegexString) | |
56 | longitudeRegex = regexp.MustCompile(longitudeRegexString) | |
57 | sSNRegex = regexp.MustCompile(sSNRegexString) | |
32 | 58 | ) |
33 | 59 | |
34 | 60 | func matchesRegex(regex *regexp.Regexp, field interface{}) bool { |
245 | 245 | v.tagName = tagName |
246 | 246 | } |
247 | 247 | |
248 | // SetStructPoolMax sets the struct pools max size. this may be usefull for fine grained | |
248 | // SetMaxStructPoolSize sets the struct pools max size. this may be usefull for fine grained | |
249 | 249 | // performance tuning towards your application, however, the default should be fine for |
250 | 250 | // nearly all cases. only increase if you have a deeply nested struct structure. |
251 | 251 | // NOTE: this method is not thread-safe |
223 | 223 | NotEqualSkip(t, 2, val, nil) |
224 | 224 | EqualSkip(t, 2, val.Field, field) |
225 | 225 | EqualSkip(t, 2, val.Tag, expectedTag) |
226 | } | |
227 | ||
228 | func TestSSNValidation(t *testing.T) { | |
229 | tests := []struct { | |
230 | param string | |
231 | expected bool | |
232 | }{ | |
233 | {"", false}, | |
234 | {"00-90-8787", false}, | |
235 | {"66690-76", false}, | |
236 | {"191 60 2869", true}, | |
237 | {"191-60-2869", true}, | |
238 | } | |
239 | ||
240 | for i, test := range tests { | |
241 | ||
242 | err := validate.Field(test.param, "ssn") | |
243 | ||
244 | if test.expected == true { | |
245 | if !IsEqual(t, err, nil) { | |
246 | t.Fatalf("Index: %d SSN failed Error: %s", i, err) | |
247 | } | |
248 | } else { | |
249 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "ssn") { | |
250 | t.Fatalf("Index: %d SSN failed Error: %s", i, err) | |
251 | } | |
252 | } | |
253 | } | |
254 | } | |
255 | ||
256 | func TestLongitudeValidation(t *testing.T) { | |
257 | tests := []struct { | |
258 | param string | |
259 | expected bool | |
260 | }{ | |
261 | {"", false}, | |
262 | {"-180.000", true}, | |
263 | {"180.1", false}, | |
264 | {"+73.234", true}, | |
265 | {"+382.3811", false}, | |
266 | {"23.11111111", true}, | |
267 | } | |
268 | ||
269 | for i, test := range tests { | |
270 | ||
271 | err := validate.Field(test.param, "longitude") | |
272 | ||
273 | if test.expected == true { | |
274 | if !IsEqual(t, err, nil) { | |
275 | t.Fatalf("Index: %d Longitude failed Error: %s", i, err) | |
276 | } | |
277 | } else { | |
278 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "longitude") { | |
279 | t.Fatalf("Index: %d Longitude failed Error: %s", i, err) | |
280 | } | |
281 | } | |
282 | } | |
283 | } | |
284 | ||
285 | func TestLatitudeValidation(t *testing.T) { | |
286 | tests := []struct { | |
287 | param string | |
288 | expected bool | |
289 | }{ | |
290 | {"", false}, | |
291 | {"-90.000", true}, | |
292 | {"+90", true}, | |
293 | {"47.1231231", true}, | |
294 | {"+99.9", false}, | |
295 | {"108", false}, | |
296 | } | |
297 | ||
298 | for i, test := range tests { | |
299 | ||
300 | err := validate.Field(test.param, "latitude") | |
301 | ||
302 | if test.expected == true { | |
303 | if !IsEqual(t, err, nil) { | |
304 | t.Fatalf("Index: %d Latitude failed Error: %s", i, err) | |
305 | } | |
306 | } else { | |
307 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "latitude") { | |
308 | t.Fatalf("Index: %d Latitude failed Error: %s", i, err) | |
309 | } | |
310 | } | |
311 | } | |
312 | } | |
313 | ||
314 | func TestDataURIValidation(t *testing.T) { | |
315 | tests := []struct { | |
316 | param string | |
317 | expected bool | |
318 | }{ | |
319 | {"data:image/png;base64,TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=", true}, | |
320 | {"data:text/plain;base64,Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==", true}, | |
321 | {"image/gif;base64,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, | |
322 | {"data:image/gif;base64,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw" + | |
323 | "UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye" + | |
324 | "rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619" + | |
325 | "FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx" + | |
326 | "QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ" + | |
327 | "Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ" + "HQIDAQAB", true}, | |
328 | {"data:image/png;base64,12345", false}, | |
329 | {"", false}, | |
330 | {"data:text,:;base85,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, | |
331 | } | |
332 | ||
333 | for i, test := range tests { | |
334 | ||
335 | err := validate.Field(test.param, "datauri") | |
336 | ||
337 | if test.expected == true { | |
338 | if !IsEqual(t, err, nil) { | |
339 | t.Fatalf("Index: %d DataURI failed Error: %s", i, err) | |
340 | } | |
341 | } else { | |
342 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "datauri") { | |
343 | t.Fatalf("Index: %d DataURI failed Error: %s", i, err) | |
344 | } | |
345 | } | |
346 | } | |
347 | } | |
348 | ||
349 | func TestMultibyteValidation(t *testing.T) { | |
350 | tests := []struct { | |
351 | param string | |
352 | expected bool | |
353 | }{ | |
354 | {"", true}, | |
355 | {"abc", false}, | |
356 | {"123", false}, | |
357 | {"<>@;.-=", false}, | |
358 | {"ひらがな・カタカナ、.漢字", true}, | |
359 | {"あいうえお foobar", true}, | |
360 | {"test@example.com", true}, | |
361 | {"test@example.com", true}, | |
362 | {"1234abcDExyz", true}, | |
363 | {"カタカナ", true}, | |
364 | } | |
365 | ||
366 | for i, test := range tests { | |
367 | ||
368 | err := validate.Field(test.param, "multibyte") | |
369 | ||
370 | if test.expected == true { | |
371 | if !IsEqual(t, err, nil) { | |
372 | t.Fatalf("Index: %d Multibyte failed Error: %s", i, err) | |
373 | } | |
374 | } else { | |
375 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "multibyte") { | |
376 | t.Fatalf("Index: %d Multibyte failed Error: %s", i, err) | |
377 | } | |
378 | } | |
379 | } | |
380 | } | |
381 | ||
382 | func TestPrintableASCIIValidation(t *testing.T) { | |
383 | tests := []struct { | |
384 | param string | |
385 | expected bool | |
386 | }{ | |
387 | {"", true}, | |
388 | {"foobar", false}, | |
389 | {"xyz098", false}, | |
390 | {"123456", false}, | |
391 | {"カタカナ", false}, | |
392 | {"foobar", true}, | |
393 | {"0987654321", true}, | |
394 | {"test@example.com", true}, | |
395 | {"1234abcDEF", true}, | |
396 | {"newline\n", false}, | |
397 | {"\x19test\x7F", false}, | |
398 | } | |
399 | ||
400 | for i, test := range tests { | |
401 | ||
402 | err := validate.Field(test.param, "printascii") | |
403 | ||
404 | if test.expected == true { | |
405 | if !IsEqual(t, err, nil) { | |
406 | t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, err) | |
407 | } | |
408 | } else { | |
409 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "printascii") { | |
410 | t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, err) | |
411 | } | |
412 | } | |
413 | } | |
414 | } | |
415 | ||
416 | func TestASCIIValidation(t *testing.T) { | |
417 | tests := []struct { | |
418 | param string | |
419 | expected bool | |
420 | }{ | |
421 | {"", true}, | |
422 | {"foobar", false}, | |
423 | {"xyz098", false}, | |
424 | {"123456", false}, | |
425 | {"カタカナ", false}, | |
426 | {"foobar", true}, | |
427 | {"0987654321", true}, | |
428 | {"test@example.com", true}, | |
429 | {"1234abcDEF", true}, | |
430 | {"", true}, | |
431 | } | |
432 | ||
433 | for i, test := range tests { | |
434 | ||
435 | err := validate.Field(test.param, "ascii") | |
436 | ||
437 | if test.expected == true { | |
438 | if !IsEqual(t, err, nil) { | |
439 | t.Fatalf("Index: %d ASCII failed Error: %s", i, err) | |
440 | } | |
441 | } else { | |
442 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "ascii") { | |
443 | t.Fatalf("Index: %d ASCII failed Error: %s", i, err) | |
444 | } | |
445 | } | |
446 | } | |
447 | } | |
448 | ||
449 | func TestUUID5Validation(t *testing.T) { | |
450 | tests := []struct { | |
451 | param string | |
452 | expected bool | |
453 | }{ | |
454 | ||
455 | {"", false}, | |
456 | {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, | |
457 | {"9c858901-8a57-4791-81fe-4c455b099bc9", false}, | |
458 | {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, | |
459 | {"987fbc97-4bed-5078-af07-9141ba07c9f3", true}, | |
460 | {"987fbc97-4bed-5078-9f07-9141ba07c9f3", true}, | |
461 | } | |
462 | ||
463 | for i, test := range tests { | |
464 | ||
465 | err := validate.Field(test.param, "uuid5") | |
466 | ||
467 | if test.expected == true { | |
468 | if !IsEqual(t, err, nil) { | |
469 | t.Fatalf("Index: %d UUID5 failed Error: %s", i, err) | |
470 | } | |
471 | } else { | |
472 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid5") { | |
473 | t.Fatalf("Index: %d UUID5 failed Error: %s", i, err) | |
474 | } | |
475 | } | |
476 | } | |
477 | } | |
478 | ||
479 | func TestUUID4Validation(t *testing.T) { | |
480 | tests := []struct { | |
481 | param string | |
482 | expected bool | |
483 | }{ | |
484 | {"", false}, | |
485 | {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, | |
486 | {"a987fbc9-4bed-5078-af07-9141ba07c9f3", false}, | |
487 | {"934859", false}, | |
488 | {"57b73598-8764-4ad0-a76a-679bb6640eb1", true}, | |
489 | {"625e63f3-58f5-40b7-83a1-a72ad31acffb", true}, | |
490 | } | |
491 | ||
492 | for i, test := range tests { | |
493 | ||
494 | err := validate.Field(test.param, "uuid4") | |
495 | ||
496 | if test.expected == true { | |
497 | if !IsEqual(t, err, nil) { | |
498 | t.Fatalf("Index: %d UUID4 failed Error: %s", i, err) | |
499 | } | |
500 | } else { | |
501 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid4") { | |
502 | t.Fatalf("Index: %d UUID4 failed Error: %s", i, err) | |
503 | } | |
504 | } | |
505 | } | |
506 | } | |
507 | ||
508 | func TestUUID3Validation(t *testing.T) { | |
509 | tests := []struct { | |
510 | param string | |
511 | expected bool | |
512 | }{ | |
513 | {"", false}, | |
514 | {"412452646", false}, | |
515 | {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, | |
516 | {"a987fbc9-4bed-4078-8f07-9141ba07c9f3", false}, | |
517 | {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, | |
518 | } | |
519 | ||
520 | for i, test := range tests { | |
521 | ||
522 | err := validate.Field(test.param, "uuid3") | |
523 | ||
524 | if test.expected == true { | |
525 | if !IsEqual(t, err, nil) { | |
526 | t.Fatalf("Index: %d UUID3 failed Error: %s", i, err) | |
527 | } | |
528 | } else { | |
529 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid3") { | |
530 | t.Fatalf("Index: %d UUID3 failed Error: %s", i, err) | |
531 | } | |
532 | } | |
533 | } | |
534 | } | |
535 | ||
536 | func TestUUIDValidation(t *testing.T) { | |
537 | tests := []struct { | |
538 | param string | |
539 | expected bool | |
540 | }{ | |
541 | {"", false}, | |
542 | {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, | |
543 | {"a987fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false}, | |
544 | {"a987fbc94bed3078cf079141ba07c9f3", false}, | |
545 | {"934859", false}, | |
546 | {"987fbc9-4bed-3078-cf07a-9141ba07c9f3", false}, | |
547 | {"aaaaaaaa-1111-1111-aaag-111111111111", false}, | |
548 | {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, | |
549 | } | |
550 | ||
551 | for i, test := range tests { | |
552 | ||
553 | err := validate.Field(test.param, "uuid") | |
554 | ||
555 | if test.expected == true { | |
556 | if !IsEqual(t, err, nil) { | |
557 | t.Fatalf("Index: %d UUID failed Error: %s", i, err) | |
558 | } | |
559 | } else { | |
560 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid") { | |
561 | t.Fatalf("Index: %d UUID failed Error: %s", i, err) | |
562 | } | |
563 | } | |
564 | } | |
565 | } | |
566 | ||
567 | func TestISBNValidation(t *testing.T) { | |
568 | tests := []struct { | |
569 | param string | |
570 | expected bool | |
571 | }{ | |
572 | {"", false}, | |
573 | {"foo", false}, | |
574 | {"3836221195", true}, | |
575 | {"1-61729-085-8", true}, | |
576 | {"3 423 21412 0", true}, | |
577 | {"3 401 01319 X", true}, | |
578 | {"9784873113685", true}, | |
579 | {"978-4-87311-368-5", true}, | |
580 | {"978 3401013190", true}, | |
581 | {"978-3-8362-2119-1", true}, | |
582 | } | |
583 | ||
584 | for i, test := range tests { | |
585 | ||
586 | err := validate.Field(test.param, "isbn") | |
587 | ||
588 | if test.expected == true { | |
589 | if !IsEqual(t, err, nil) { | |
590 | t.Fatalf("Index: %d ISBN failed Error: %s", i, err) | |
591 | } | |
592 | } else { | |
593 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn") { | |
594 | t.Fatalf("Index: %d ISBN failed Error: %s", i, err) | |
595 | } | |
596 | } | |
597 | } | |
598 | } | |
599 | ||
600 | func TestISBN13Validation(t *testing.T) { | |
601 | tests := []struct { | |
602 | param string | |
603 | expected bool | |
604 | }{ | |
605 | {"", false}, | |
606 | {"foo", false}, | |
607 | {"3-8362-2119-5", false}, | |
608 | {"01234567890ab", false}, | |
609 | {"978 3 8362 2119 0", false}, | |
610 | {"9784873113685", true}, | |
611 | {"978-4-87311-368-5", true}, | |
612 | {"978 3401013190", true}, | |
613 | {"978-3-8362-2119-1", true}, | |
614 | } | |
615 | ||
616 | for i, test := range tests { | |
617 | ||
618 | err := validate.Field(test.param, "isbn13") | |
619 | ||
620 | if test.expected == true { | |
621 | if !IsEqual(t, err, nil) { | |
622 | t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err) | |
623 | } | |
624 | } else { | |
625 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn13") { | |
626 | t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err) | |
627 | } | |
628 | } | |
629 | } | |
630 | } | |
631 | ||
632 | func TestISBN10Validation(t *testing.T) { | |
633 | tests := []struct { | |
634 | param string | |
635 | expected bool | |
636 | }{ | |
637 | {"", false}, | |
638 | {"foo", false}, | |
639 | {"3423214121", false}, | |
640 | {"978-3836221191", false}, | |
641 | {"3-423-21412-1", false}, | |
642 | {"3 423 21412 1", false}, | |
643 | {"3836221195", true}, | |
644 | {"1-61729-085-8", true}, | |
645 | {"3 423 21412 0", true}, | |
646 | {"3 401 01319 X", true}, | |
647 | } | |
648 | ||
649 | for i, test := range tests { | |
650 | ||
651 | err := validate.Field(test.param, "isbn10") | |
652 | ||
653 | if test.expected == true { | |
654 | if !IsEqual(t, err, nil) { | |
655 | t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err) | |
656 | } | |
657 | } else { | |
658 | if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn10") { | |
659 | t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err) | |
660 | } | |
661 | } | |
662 | } | |
226 | 663 | } |
227 | 664 | |
228 | 665 | func TestExcludesRuneValidation(t *testing.T) { |
1735 | 2172 | err = validate.Field(s, "rgba") |
1736 | 2173 | Equal(t, err, nil) |
1737 | 2174 | |
2175 | s = "rgba(12%,55%,100%,0.12)" | |
2176 | err = validate.Field(s, "rgba") | |
2177 | Equal(t, err, nil) | |
2178 | ||
1738 | 2179 | s = "rgba( 0, 31, 255, 0.5)" |
1739 | 2180 | err = validate.Field(s, "rgba") |
1740 | 2181 | Equal(t, err, nil) |
1741 | 2182 | |
2183 | s = "rgba(12%,55,100%,0.12)" | |
2184 | err = validate.Field(s, "rgba") | |
2185 | NotEqual(t, err, nil) | |
2186 | Equal(t, err.Tag, "rgba") | |
2187 | ||
1742 | 2188 | s = "rgb(0, 31, 255)" |
1743 | 2189 | err = validate.Field(s, "rgba") |
1744 | 2190 | NotEqual(t, err, nil) |
1767 | 2213 | s = "rgb(0, 31, 255)" |
1768 | 2214 | err = validate.Field(s, "rgb") |
1769 | 2215 | Equal(t, err, nil) |
2216 | ||
2217 | s = "rgb(10%, 50%, 100%)" | |
2218 | err = validate.Field(s, "rgb") | |
2219 | Equal(t, err, nil) | |
2220 | ||
2221 | s = "rgb(10%, 50%, 55)" | |
2222 | err = validate.Field(s, "rgb") | |
2223 | NotEqual(t, err, nil) | |
2224 | Equal(t, err.Tag, "rgb") | |
1770 | 2225 | |
1771 | 2226 | s = "rgb(1,349,275)" |
1772 | 2227 | err = validate.Field(s, "rgb") |
2334 | 2789 | |
2335 | 2790 | PanicMatches(t, func() { validate.Field(s.Test, "zzxxBadFunction") }, fmt.Sprintf("Undefined validation function on field %s", "")) |
2336 | 2791 | } |
2337 | ||
2338 | func BenchmarkValidateField(b *testing.B) { | |
2339 | for n := 0; n < b.N; n++ { | |
2340 | validate.Field("1", "len=1") | |
2341 | } | |
2342 | } | |
2343 | ||
2344 | func BenchmarkValidateStructSimple(b *testing.B) { | |
2345 | ||
2346 | type Foo struct { | |
2347 | StringValue string `validate:"min=5,max=10"` | |
2348 | IntValue int `validate:"min=5,max=10"` | |
2349 | } | |
2350 | ||
2351 | validFoo := &Foo{StringValue: "Foobar", IntValue: 7} | |
2352 | invalidFoo := &Foo{StringValue: "Fo", IntValue: 3} | |
2353 | ||
2354 | for n := 0; n < b.N; n++ { | |
2355 | validate.Struct(validFoo) | |
2356 | validate.Struct(invalidFoo) | |
2357 | } | |
2358 | } | |
2359 | ||
2360 | // func BenchmarkTemplateParallelSimple(b *testing.B) { | |
2361 | ||
2362 | // type Foo struct { | |
2363 | // StringValue string `validate:"min=5,max=10"` | |
2364 | // IntValue int `validate:"min=5,max=10"` | |
2365 | // } | |
2366 | ||
2367 | // validFoo := &Foo{StringValue: "Foobar", IntValue: 7} | |
2368 | // invalidFoo := &Foo{StringValue: "Fo", IntValue: 3} | |
2369 | ||
2370 | // b.RunParallel(func(pb *testing.PB) { | |
2371 | // for pb.Next() { | |
2372 | // validate.Struct(validFoo) | |
2373 | // validate.Struct(invalidFoo) | |
2374 | // } | |
2375 | // }) | |
2376 | // } | |
2377 | ||
2378 | func BenchmarkValidateStructLarge(b *testing.B) { | |
2379 | ||
2380 | tFail := &TestString{ | |
2381 | Required: "", | |
2382 | Len: "", | |
2383 | Min: "", | |
2384 | Max: "12345678901", | |
2385 | MinMax: "", | |
2386 | Lt: "0123456789", | |
2387 | Lte: "01234567890", | |
2388 | Gt: "1", | |
2389 | Gte: "1", | |
2390 | OmitEmpty: "12345678901", | |
2391 | Sub: &SubTest{ | |
2392 | Test: "", | |
2393 | }, | |
2394 | Anonymous: struct { | |
2395 | A string `validate:"required"` | |
2396 | }{ | |
2397 | A: "", | |
2398 | }, | |
2399 | Iface: &Impl{ | |
2400 | F: "12", | |
2401 | }, | |
2402 | } | |
2403 | ||
2404 | tSuccess := &TestString{ | |
2405 | Required: "Required", | |
2406 | Len: "length==10", | |
2407 | Min: "min=1", | |
2408 | Max: "1234567890", | |
2409 | MinMax: "12345", | |
2410 | Lt: "012345678", | |
2411 | Lte: "0123456789", | |
2412 | Gt: "01234567890", | |
2413 | Gte: "0123456789", | |
2414 | OmitEmpty: "", | |
2415 | Sub: &SubTest{ | |
2416 | Test: "1", | |
2417 | }, | |
2418 | SubIgnore: &SubTest{ | |
2419 | Test: "", | |
2420 | }, | |
2421 | Anonymous: struct { | |
2422 | A string `validate:"required"` | |
2423 | }{ | |
2424 | A: "1", | |
2425 | }, | |
2426 | Iface: &Impl{ | |
2427 | F: "123", | |
2428 | }, | |
2429 | } | |
2430 | ||
2431 | for n := 0; n < b.N; n++ { | |
2432 | validate.Struct(tSuccess) | |
2433 | validate.Struct(tFail) | |
2434 | } | |
2435 | } | |
2436 | ||
2437 | // func BenchmarkTemplateParallelLarge(b *testing.B) { | |
2438 | ||
2439 | // tFail := &TestString{ | |
2440 | // Required: "", | |
2441 | // Len: "", | |
2442 | // Min: "", | |
2443 | // Max: "12345678901", | |
2444 | // MinMax: "", | |
2445 | // Lt: "0123456789", | |
2446 | // Lte: "01234567890", | |
2447 | // Gt: "1", | |
2448 | // Gte: "1", | |
2449 | // OmitEmpty: "12345678901", | |
2450 | // Sub: &SubTest{ | |
2451 | // Test: "", | |
2452 | // }, | |
2453 | // Anonymous: struct { | |
2454 | // A string `validate:"required"` | |
2455 | // }{ | |
2456 | // A: "", | |
2457 | // }, | |
2458 | // Iface: &Impl{ | |
2459 | // F: "12", | |
2460 | // }, | |
2461 | // } | |
2462 | ||
2463 | // tSuccess := &TestString{ | |
2464 | // Required: "Required", | |
2465 | // Len: "length==10", | |
2466 | // Min: "min=1", | |
2467 | // Max: "1234567890", | |
2468 | // MinMax: "12345", | |
2469 | // Lt: "012345678", | |
2470 | // Lte: "0123456789", | |
2471 | // Gt: "01234567890", | |
2472 | // Gte: "0123456789", | |
2473 | // OmitEmpty: "", | |
2474 | // Sub: &SubTest{ | |
2475 | // Test: "1", | |
2476 | // }, | |
2477 | // SubIgnore: &SubTest{ | |
2478 | // Test: "", | |
2479 | // }, | |
2480 | // Anonymous: struct { | |
2481 | // A string `validate:"required"` | |
2482 | // }{ | |
2483 | // A: "1", | |
2484 | // }, | |
2485 | // Iface: &Impl{ | |
2486 | // F: "123", | |
2487 | // }, | |
2488 | // } | |
2489 | ||
2490 | // b.RunParallel(func(pb *testing.PB) { | |
2491 | // for pb.Next() { | |
2492 | // validate.Struct(tSuccess) | |
2493 | // validate.Struct(tFail) | |
2494 | // } | |
2495 | // }) | |
2496 | // } |