github.com/ManabuSeki/goa-v1@v1.4.3/goagen/codegen/validation_test.go (about) 1 package codegen_test 2 3 import ( 4 "math" 5 "strings" 6 7 "github.com/goadesign/goa/design" 8 "github.com/goadesign/goa/dslengine" 9 "github.com/goadesign/goa/goagen/codegen" 10 . "github.com/onsi/ginkgo" 11 . "github.com/onsi/gomega" 12 ) 13 14 var _ = Describe("validation code generation", func() { 15 BeforeEach(func() { 16 codegen.TempCount = 0 17 }) 18 19 Describe("ValidationChecker", func() { 20 Context("given an attribute definition and validations", func() { 21 var attType design.DataType 22 var validation *dslengine.ValidationDefinition 23 24 att := new(design.AttributeDefinition) 25 target := "val" 26 context := "context" 27 var code string // generated code 28 29 JustBeforeEach(func() { 30 att.Type = attType 31 att.Validation = validation 32 code = codegen.NewValidator().Code(att, false, false, false, target, context, 1, false) 33 }) 34 35 Context("of enum", func() { 36 BeforeEach(func() { 37 attType = design.Integer 38 validation = &dslengine.ValidationDefinition{ 39 Values: []interface{}{1, 2, 3}, 40 } 41 }) 42 43 It("produces the validation go code", func() { 44 Ω(code).Should(Equal(enumValCode)) 45 }) 46 }) 47 48 Context("of pattern", func() { 49 BeforeEach(func() { 50 attType = design.String 51 validation = &dslengine.ValidationDefinition{ 52 Pattern: ".*", 53 } 54 }) 55 56 It("produces the validation go code", func() { 57 Ω(code).Should(Equal(patternValCode)) 58 }) 59 }) 60 61 Context("of min value 0", func() { 62 BeforeEach(func() { 63 attType = design.Integer 64 min := 0.0 65 validation = &dslengine.ValidationDefinition{ 66 Minimum: &min, 67 } 68 }) 69 70 It("produces the validation go code", func() { 71 Ω(code).Should(Equal(minValCode)) 72 }) 73 }) 74 75 Context("of max value math.MaxInt64", func() { 76 BeforeEach(func() { 77 attType = design.Integer 78 max := float64(math.MaxInt64) 79 validation = &dslengine.ValidationDefinition{ 80 Maximum: &max, 81 } 82 }) 83 84 It("produces the validation go code", func() { 85 Ω(code).Should(Equal(maxValCode)) 86 }) 87 }) 88 89 Context("of min value math.MinInt64", func() { 90 BeforeEach(func() { 91 attType = design.Integer 92 min := float64(math.MinInt64) 93 validation = &dslengine.ValidationDefinition{ 94 Minimum: &min, 95 } 96 }) 97 98 It("produces the validation go code", func() { 99 Ω(code).Should(Equal(minminValCode)) 100 }) 101 }) 102 103 Context("of array min length 1", func() { 104 BeforeEach(func() { 105 attType = &design.Array{ 106 ElemType: &design.AttributeDefinition{ 107 Type: design.String, 108 }, 109 } 110 min := 1 111 validation = &dslengine.ValidationDefinition{ 112 MinLength: &min, 113 } 114 }) 115 116 It("produces the validation go code", func() { 117 Ω(code).Should(Equal(arrayMinLengthValCode)) 118 }) 119 }) 120 121 Context("of array elements", func() { 122 BeforeEach(func() { 123 attType = &design.Array{ 124 ElemType: &design.AttributeDefinition{ 125 Type: design.String, 126 Validation: &dslengine.ValidationDefinition{ 127 Pattern: ".*", 128 }, 129 }, 130 } 131 validation = nil 132 }) 133 134 It("produces the validation go code", func() { 135 Ω(code).Should(Equal(arrayElementsValCode)) 136 }) 137 }) 138 139 Context("of hash elements (key, elem)", func() { 140 BeforeEach(func() { 141 attType = &design.Hash{ 142 KeyType: &design.AttributeDefinition{ 143 Type: design.String, 144 Validation: &dslengine.ValidationDefinition{ 145 Pattern: ".*", 146 }, 147 }, 148 ElemType: &design.AttributeDefinition{ 149 Type: design.String, 150 Validation: &dslengine.ValidationDefinition{ 151 Pattern: ".*", 152 }, 153 }, 154 } 155 validation = nil 156 }) 157 158 It("produces the validation go code", func() { 159 Ω(code).Should(Equal(hashKeyElemValCode)) 160 }) 161 }) 162 163 Context("of hash elements (key, _)", func() { 164 BeforeEach(func() { 165 attType = &design.Hash{ 166 KeyType: &design.AttributeDefinition{ 167 Type: design.String, 168 Validation: &dslengine.ValidationDefinition{ 169 Pattern: ".*", 170 }, 171 }, 172 ElemType: &design.AttributeDefinition{ 173 Type: design.String, 174 }, 175 } 176 validation = nil 177 }) 178 179 It("produces the validation go code", func() { 180 Ω(code).Should(Equal(hashKeyValCode)) 181 }) 182 }) 183 184 Context("of hash elements (_, elem)", func() { 185 BeforeEach(func() { 186 attType = &design.Hash{ 187 KeyType: &design.AttributeDefinition{ 188 Type: design.String, 189 }, 190 ElemType: &design.AttributeDefinition{ 191 Type: design.String, 192 Validation: &dslengine.ValidationDefinition{ 193 Pattern: ".*", 194 }, 195 }, 196 } 197 validation = nil 198 }) 199 200 It("produces the validation go code", func() { 201 Ω(code).Should(Equal(hashElemValCode)) 202 }) 203 }) 204 205 Context("of string min length 2", func() { 206 BeforeEach(func() { 207 attType = design.String 208 min := 2 209 validation = &dslengine.ValidationDefinition{ 210 MinLength: &min, 211 } 212 }) 213 214 It("produces the validation go code", func() { 215 Ω(code).Should(Equal(stringMinLengthValCode)) 216 }) 217 }) 218 219 Context("of embedded object", func() { 220 var catt, ccatt *design.AttributeDefinition 221 222 BeforeEach(func() { 223 enumVal := &dslengine.ValidationDefinition{ 224 Values: []interface{}{1, 2, 3}, 225 } 226 ccatt = &design.AttributeDefinition{ 227 Type: design.Integer, 228 Validation: enumVal, 229 } 230 catt = &design.AttributeDefinition{ 231 Type: design.Object{"bar": ccatt}, 232 } 233 234 attType = design.Object{"foo": catt} 235 }) 236 Context("and the parent is optional", func() { 237 BeforeEach(func() { 238 validation = nil 239 }) 240 It("checks the child & parent object are not nil", func() { 241 Ω(code).Should(Equal(embeddedValCode)) 242 }) 243 }) 244 Context("and the parent is required", func() { 245 BeforeEach(func() { 246 validation = &dslengine.ValidationDefinition{ 247 Required: []string{"foo"}, 248 } 249 }) 250 It("checks the child & parent object are not nil", func() { 251 Ω(code).Should(Equal(embeddedRequiredValCode)) 252 }) 253 }) 254 Context("with a child attribute with struct:tag:name metadata", func() { 255 const fieldTag = "FOO" 256 257 BeforeEach(func() { 258 catt.Metadata = dslengine.MetadataDefinition{"struct:field:name": []string{fieldTag}} 259 ccatt.Metadata = nil 260 validation = nil 261 }) 262 263 It("produces the validation go code using the field tag", func() { 264 Ω(code).Should(Equal(strings.Replace(tagCode, "__tag__", fieldTag, -1))) 265 }) 266 }) 267 Context("with a grand child attribute with struct:tag:name metadata", func() { 268 const fieldTag = "FOO" 269 270 BeforeEach(func() { 271 catt.Metadata = nil 272 ccatt.Metadata = dslengine.MetadataDefinition{"struct:field:name": []string{fieldTag}} 273 validation = nil 274 }) 275 276 It("produces the validation go code using the field tag", func() { 277 Ω(code).Should(Equal(strings.Replace(tagChildCode, "__tag__", fieldTag, -1))) 278 }) 279 }) 280 281 }) 282 283 Context("of required user type attribute with no validation", func() { 284 var ut *design.UserTypeDefinition 285 286 BeforeEach(func() { 287 ut = &design.UserTypeDefinition{ 288 TypeName: "UT", 289 AttributeDefinition: &design.AttributeDefinition{ 290 Type: design.Object{ 291 "bar": &design.AttributeDefinition{Type: design.String}, 292 }, 293 }, 294 } 295 uatt := &design.AttributeDefinition{Type: design.Dup(ut)} 296 arr := &design.AttributeDefinition{ 297 Type: &design.Array{ 298 ElemType: &design.AttributeDefinition{Type: ut}, 299 }, 300 } 301 302 attType = design.Object{"foo": arr, "foo2": uatt} 303 validation = &dslengine.ValidationDefinition{ 304 Required: []string{"foo"}, 305 } 306 }) 307 308 Context("with only direct required attributes", func() { 309 BeforeEach(func() { 310 validation = &dslengine.ValidationDefinition{ 311 Required: []string{"foo"}, 312 } 313 }) 314 315 It("does not call Validate on the user type attribute", func() { 316 Ω(code).Should(Equal(utCode)) 317 }) 318 }) 319 320 Context("with required attributes on inner attribute", func() { 321 BeforeEach(func() { 322 ut.AttributeDefinition.Validation = &dslengine.ValidationDefinition{ 323 Required: []string{"bar"}, 324 } 325 validation = nil 326 }) 327 328 It("calls Validate on the user type attribute", func() { 329 Ω(code).Should(Equal(utRequiredCode)) 330 }) 331 }) 332 }) 333 334 }) 335 }) 336 }) 337 338 const ( 339 enumValCode = ` if val != nil { 340 if !(*val == 1 || *val == 2 || *val == 3) { 341 err = goa.MergeErrors(err, goa.InvalidEnumValueError(` + "`context`" + `, *val, []interface{}{1, 2, 3})) 342 } 343 }` 344 345 patternValCode = ` if val != nil { 346 if ok := goa.ValidatePattern(` + "`.*`" + `, *val); !ok { 347 err = goa.MergeErrors(err, goa.InvalidPatternError(` + "`context`" + `, *val, ` + "`.*`" + `)) 348 } 349 }` 350 351 minValCode = ` if val != nil { 352 if *val < 0 { 353 err = goa.MergeErrors(err, goa.InvalidRangeError(` + "`" + `context` + "`" + `, *val, 0, true)) 354 } 355 }` 356 357 maxValCode = ` if val != nil { 358 if *val > 9223372036854775807 { 359 err = goa.MergeErrors(err, goa.InvalidRangeError(` + "`" + `context` + "`" + `, *val, 9223372036854775807, false)) 360 } 361 }` 362 363 minminValCode = ` if val != nil { 364 if *val < -9223372036854775808 { 365 err = goa.MergeErrors(err, goa.InvalidRangeError(` + "`" + `context` + "`" + `, *val, -9223372036854775808, true)) 366 } 367 }` 368 369 arrayMinLengthValCode = ` if val != nil { 370 if len(val) < 1 { 371 err = goa.MergeErrors(err, goa.InvalidLengthError(` + "`" + `context` + "`" + `, val, len(val), 1, true)) 372 } 373 }` 374 375 arrayElementsValCode = ` for _, e := range val { 376 if ok := goa.ValidatePattern(` + "`" + `.*` + "`" + `, e); !ok { 377 err = goa.MergeErrors(err, goa.InvalidPatternError(` + "`" + `context[*]` + "`" + `, e, ` + "`" + `.*` + "`" + `)) 378 } 379 }` 380 381 hashKeyElemValCode = ` for k, e := range val { 382 if ok := goa.ValidatePattern(` + "`" + `.*` + "`" + `, k); !ok { 383 err = goa.MergeErrors(err, goa.InvalidPatternError(` + "`" + `context[*]` + "`" + `, k, ` + "`" + `.*` + "`" + `)) 384 } 385 if ok := goa.ValidatePattern(` + "`" + `.*` + "`" + `, e); !ok { 386 err = goa.MergeErrors(err, goa.InvalidPatternError(` + "`" + `context[*]` + "`" + `, e, ` + "`" + `.*` + "`" + `)) 387 } 388 }` 389 390 hashKeyValCode = ` for k, _ := range val { 391 if ok := goa.ValidatePattern(` + "`" + `.*` + "`" + `, k); !ok { 392 err = goa.MergeErrors(err, goa.InvalidPatternError(` + "`" + `context[*]` + "`" + `, k, ` + "`" + `.*` + "`" + `)) 393 } 394 }` 395 396 hashElemValCode = ` for _, e := range val { 397 if ok := goa.ValidatePattern(` + "`" + `.*` + "`" + `, e); !ok { 398 err = goa.MergeErrors(err, goa.InvalidPatternError(` + "`" + `context[*]` + "`" + `, e, ` + "`" + `.*` + "`" + `)) 399 } 400 }` 401 402 stringMinLengthValCode = ` if val != nil { 403 if utf8.RuneCountInString(*val) < 2 { 404 err = goa.MergeErrors(err, goa.InvalidLengthError(` + "`" + `context` + "`" + `, *val, utf8.RuneCountInString(*val), 2, true)) 405 } 406 }` 407 408 embeddedValCode = ` if val.Foo != nil { 409 if val.Foo.Bar != nil { 410 if !(*val.Foo.Bar == 1 || *val.Foo.Bar == 2 || *val.Foo.Bar == 3) { 411 err = goa.MergeErrors(err, goa.InvalidEnumValueError(` + "`" + `context.foo.bar` + "`" + `, *val.Foo.Bar, []interface{}{1, 2, 3})) 412 } 413 } 414 }` 415 416 embeddedRequiredValCode = ` if val.Foo == nil { 417 err = goa.MergeErrors(err, goa.MissingAttributeError(` + "`context`" + `, "foo")) 418 } 419 if val.Foo != nil { 420 if val.Foo.Bar != nil { 421 if !(*val.Foo.Bar == 1 || *val.Foo.Bar == 2 || *val.Foo.Bar == 3) { 422 err = goa.MergeErrors(err, goa.InvalidEnumValueError(` + "`" + `context.foo.bar` + "`" + `, *val.Foo.Bar, []interface{}{1, 2, 3})) 423 } 424 } 425 }` 426 427 tagCode = ` if val.__tag__ != nil { 428 if val.__tag__.Bar != nil { 429 if !(*val.__tag__.Bar == 1 || *val.__tag__.Bar == 2 || *val.__tag__.Bar == 3) { 430 err = goa.MergeErrors(err, goa.InvalidEnumValueError(` + "`" + `context.foo.bar` + "`" + `, *val.__tag__.Bar, []interface{}{1, 2, 3})) 431 } 432 } 433 }` 434 435 tagChildCode = ` if val.Foo != nil { 436 if val.Foo.__tag__ != nil { 437 if !(*val.Foo.__tag__ == 1 || *val.Foo.__tag__ == 2 || *val.Foo.__tag__ == 3) { 438 err = goa.MergeErrors(err, goa.InvalidEnumValueError(` + "`" + `context.foo.bar` + "`" + `, *val.Foo.__tag__, []interface{}{1, 2, 3})) 439 } 440 } 441 }` 442 443 utCode = ` if val.Foo == nil { 444 err = goa.MergeErrors(err, goa.MissingAttributeError(` + "`context`" + `, "foo")) 445 }` 446 447 utRequiredCode = ` for _, e := range val.Foo { 448 if e != nil { 449 if err2 := e.Validate(); err2 != nil { 450 err = goa.MergeErrors(err, err2) 451 } 452 } 453 }` 454 )