github.com/NewMillennialGames/goa@v1.4.0/design/validation_test.go (about) 1 package design_test 2 3 import ( 4 "go/build" 5 "io/ioutil" 6 "os" 7 "path" 8 9 . "github.com/goadesign/goa/design" 10 . "github.com/goadesign/goa/design/apidsl" 11 "github.com/goadesign/goa/dslengine" 12 . "github.com/onsi/ginkgo" 13 . "github.com/onsi/gomega" 14 ) 15 16 var _ = Describe("Validation", func() { 17 Context("with a type attribute", func() { 18 const attName = "attName" 19 var dsl func() 20 21 var att *AttributeDefinition 22 23 JustBeforeEach(func() { 24 dslengine.Reset() 25 Type("bar", func() { 26 dsl() 27 }) 28 dslengine.Run() 29 if dslengine.Errors == nil { 30 Ω(Design.Types).ShouldNot(BeNil()) 31 Ω(Design.Types).Should(HaveKey("bar")) 32 Ω(Design.Types["bar"]).ShouldNot(BeNil()) 33 Ω(Design.Types["bar"].Type).Should(BeAssignableToTypeOf(Object{})) 34 o := Design.Types["bar"].Type.(Object) 35 Ω(o).Should(HaveKey(attName)) 36 att = o[attName] 37 } 38 }) 39 40 Context("with a valid enum validation", func() { 41 BeforeEach(func() { 42 dsl = func() { 43 Attribute(attName, String, func() { 44 Enum("red", "blue") 45 }) 46 } 47 }) 48 49 It("records the validation", func() { 50 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 51 Ω(att.Validation).ShouldNot(BeNil()) 52 Ω(att.Validation.Values).Should(Equal([]interface{}{"red", "blue"})) 53 }) 54 }) 55 56 Context("with an incompatible enum validation type", func() { 57 BeforeEach(func() { 58 dsl = func() { 59 Attribute(attName, Integer, func() { 60 Enum(1, "blue") 61 }) 62 } 63 }) 64 65 It("produces an error", func() { 66 Ω(dslengine.Errors).Should(HaveOccurred()) 67 }) 68 }) 69 70 Context("with a default value that doesn't exist in enum", func() { 71 BeforeEach(func() { 72 dsl = func() { 73 Attribute(attName, Integer, func() { 74 Enum(1, 2, 3) 75 Default(4) 76 }) 77 } 78 }) 79 It("produces an error", func() { 80 Ω(dslengine.Errors).Should(HaveOccurred()) 81 Ω(dslengine.Errors.Error()).Should(Equal( 82 `type "bar": field attName - default value 4 is not one of the accepted values: []interface {}{1, 2, 3}`)) 83 }) 84 }) 85 86 Context("with a valid format validation", func() { 87 BeforeEach(func() { 88 dsl = func() { 89 Attribute(attName, String, func() { 90 Format("email") 91 }) 92 } 93 }) 94 95 It("records the validation", func() { 96 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 97 Ω(att.Validation).ShouldNot(BeNil()) 98 Ω(att.Validation.Format).Should(Equal("email")) 99 }) 100 }) 101 102 Context("with an invalid format validation", func() { 103 BeforeEach(func() { 104 dsl = func() { 105 Attribute(attName, String, func() { 106 Format("emailz") 107 }) 108 } 109 }) 110 111 It("produces an error", func() { 112 Ω(dslengine.Errors).Should(HaveOccurred()) 113 }) 114 }) 115 116 Context("with a valid pattern validation", func() { 117 BeforeEach(func() { 118 dsl = func() { 119 Attribute(attName, String, func() { 120 Pattern("^foo$") 121 }) 122 } 123 }) 124 125 It("records the validation", func() { 126 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 127 Ω(att.Validation).ShouldNot(BeNil()) 128 Ω(att.Validation.Pattern).Should(Equal("^foo$")) 129 }) 130 }) 131 132 Context("with an invalid pattern validation", func() { 133 BeforeEach(func() { 134 dsl = func() { 135 Attribute(attName, String, func() { 136 Pattern("[invalid") 137 }) 138 } 139 }) 140 141 It("produces an error", func() { 142 Ω(dslengine.Errors).Should(HaveOccurred()) 143 }) 144 }) 145 146 Context("with an invalid format validation type", func() { 147 BeforeEach(func() { 148 dsl = func() { 149 Attribute(attName, Integer, func() { 150 Format("email") 151 }) 152 } 153 }) 154 155 It("produces an error", func() { 156 Ω(dslengine.Errors).Should(HaveOccurred()) 157 }) 158 }) 159 160 Context("with a valid min value validation", func() { 161 BeforeEach(func() { 162 dsl = func() { 163 Attribute(attName, Integer, func() { 164 Minimum(2) 165 }) 166 } 167 }) 168 169 It("records the validation", func() { 170 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 171 Ω(att.Validation).ShouldNot(BeNil()) 172 Ω(*att.Validation.Minimum).Should(Equal(2.0)) 173 }) 174 }) 175 176 Context("with an invalid min value validation", func() { 177 BeforeEach(func() { 178 dsl = func() { 179 Attribute(attName, String, func() { 180 Minimum(2) 181 }) 182 } 183 }) 184 185 It("produces an error", func() { 186 Ω(dslengine.Errors).Should(HaveOccurred()) 187 }) 188 }) 189 190 Context("with a valid max value validation", func() { 191 BeforeEach(func() { 192 dsl = func() { 193 Attribute(attName, Integer, func() { 194 Maximum(2) 195 }) 196 } 197 }) 198 199 It("records the validation", func() { 200 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 201 Ω(att.Validation).ShouldNot(BeNil()) 202 Ω(*att.Validation.Maximum).Should(Equal(2.0)) 203 }) 204 }) 205 206 Context("with an invalid max value validation", func() { 207 BeforeEach(func() { 208 dsl = func() { 209 Attribute(attName, String, func() { 210 Maximum(2) 211 }) 212 } 213 }) 214 215 It("produces an error", func() { 216 Ω(dslengine.Errors).Should(HaveOccurred()) 217 }) 218 }) 219 220 Context("with a valid min length validation", func() { 221 BeforeEach(func() { 222 dsl = func() { 223 Attribute(attName, ArrayOf(Integer), func() { 224 MinLength(2) 225 }) 226 } 227 }) 228 229 It("records the validation", func() { 230 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 231 Ω(att.Validation).ShouldNot(BeNil()) 232 Ω(*att.Validation.MinLength).Should(Equal(2)) 233 }) 234 }) 235 236 Context("with an invalid min length validation", func() { 237 BeforeEach(func() { 238 dsl = func() { 239 Attribute(attName, Integer, func() { 240 MinLength(2) 241 }) 242 } 243 }) 244 245 It("produces an error", func() { 246 Ω(dslengine.Errors).Should(HaveOccurred()) 247 }) 248 }) 249 250 Context("with a valid max length validation", func() { 251 BeforeEach(func() { 252 dsl = func() { 253 Attribute(attName, String, func() { 254 MaxLength(2) 255 }) 256 } 257 }) 258 259 It("records the validation", func() { 260 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 261 Ω(att.Validation).ShouldNot(BeNil()) 262 Ω(*att.Validation.MaxLength).Should(Equal(2)) 263 }) 264 }) 265 266 Context("with an invalid max length validation", func() { 267 BeforeEach(func() { 268 dsl = func() { 269 Attribute(attName, Integer, func() { 270 MaxLength(2) 271 }) 272 } 273 }) 274 275 It("produces an error", func() { 276 Ω(dslengine.Errors).Should(HaveOccurred()) 277 }) 278 }) 279 280 Context("with a required field validation", func() { 281 BeforeEach(func() { 282 dsl = func() { 283 Attribute(attName, String) 284 Required(attName) 285 } 286 }) 287 288 It("records the validation", func() { 289 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 290 Ω(Design.Types["bar"].Validation).ShouldNot(BeNil()) 291 Ω(Design.Types["bar"].Validation.Required).Should(Equal([]string{attName})) 292 }) 293 }) 294 }) 295 296 Context("actions with different http methods", func() { 297 It("should be valid because methods are different", func() { 298 dslengine.Reset() 299 300 Resource("one", func() { 301 Action("first", func() { 302 Routing(GET("/:first")) 303 }) 304 Action("second", func() { 305 Routing(DELETE("/:second")) 306 }) 307 }) 308 309 dslengine.Run() 310 311 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 312 }) 313 }) 314 315 Context("with an action", func() { 316 var dsl func() 317 318 JustBeforeEach(func() { 319 dslengine.Reset() 320 Resource("foo", func() { 321 Action("bar", func() { 322 Routing(GET("/buz")) 323 dsl() 324 }) 325 }) 326 dslengine.Run() 327 }) 328 329 Context("which has a file type param", func() { 330 BeforeEach(func() { 331 dsl = func() { 332 Params(func() { 333 Param("file", File) 334 }) 335 } 336 }) 337 338 It("produces an error", func() { 339 Ω(dslengine.Errors.Error()).Should(Equal( 340 `resource "foo" action "bar": Param file has an invalid type, action params cannot be a file`, 341 )) 342 }) 343 }) 344 345 Context("which has a file array type param", func() { 346 BeforeEach(func() { 347 dsl = func() { 348 Params(func() { 349 Param("file_array", ArrayOf(File)) 350 }) 351 } 352 }) 353 354 It("produces an error", func() { 355 Ω(dslengine.Errors.Error()).Should(Equal( 356 `resource "foo" action "bar": Param file_array has an invalid type, action params cannot be a file array`, 357 )) 358 }) 359 }) 360 361 Context("which has a payload contains a file", func() { 362 dslengine.Reset() 363 var payload = Type("qux", func() { 364 Attribute("file", File) 365 Required("file") 366 }) 367 dslengine.Run() 368 369 BeforeEach(func() { 370 dsl = func() { 371 Payload(payload) 372 } 373 }) 374 375 It("produces an error", func() { 376 Ω(dslengine.Errors.Error()).Should(Equal( 377 `resource "foo" action "bar": Payload qux contains an invalid type, action payloads cannot contain a file`, 378 )) 379 }) 380 381 Context("and multipart form", func() { 382 BeforeEach(func() { 383 dsl = func() { 384 Payload(payload) 385 MultipartForm() 386 } 387 }) 388 389 It("produces no error", func() { 390 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 391 }) 392 }) 393 }) 394 395 Context("which has a response contains a file", func() { 396 BeforeEach(func() { 397 dslengine.Reset() 398 var response = MediaType("application/vnd.goa.example", func() { 399 TypeName("quux") 400 Attributes(func() { 401 Attribute("file", File) 402 Required("file") 403 }) 404 View("default", func() { 405 Attribute("file") 406 }) 407 }) 408 dslengine.Run() 409 dsl = func() { 410 Response(OK, response) 411 } 412 }) 413 414 It("produces an error", func() { 415 Ω(dslengine.Errors.Error()).Should(Equal( 416 `resource "foo" action "bar": Response OK contains an invalid type, action responses cannot contain a file`, 417 )) 418 }) 419 }) 420 }) 421 422 Describe("EncoderDefinition", func() { 423 var ( 424 enc *EncodingDefinition 425 oldGoPath string 426 oldWorkingDir string 427 cellarPath string 428 ) 429 430 BeforeEach(func() { 431 enc = &EncodingDefinition{MIMETypes: []string{"application/foo"}, Encoder: true, PackagePath: "github.com/goadesign/goa/encoding/foo"} 432 oldGoPath = build.Default.GOPATH 433 434 var err error 435 oldWorkingDir, err = os.Getwd() 436 Ω(err).ShouldNot(HaveOccurred()) 437 438 cellarPath = path.Join(oldWorkingDir, "tmp_gopath/src/github.com/goadesign/goa_fake_cellar") 439 Ω(os.MkdirAll(cellarPath, 0777)).ShouldNot(HaveOccurred()) 440 }) 441 442 JustBeforeEach(func() { 443 build.Default.GOPATH = path.Join(oldWorkingDir, "tmp_gopath") 444 Ω(os.Chdir(cellarPath)).ShouldNot(HaveOccurred()) 445 }) 446 447 AfterEach(func() { 448 build.Default.GOPATH = oldGoPath 449 os.Chdir(oldWorkingDir) 450 Ω(os.RemoveAll("tmp_gopath")).ShouldNot(HaveOccurred()) 451 }) 452 453 Context("with package is not found", func() { 454 It("returns a validation error", func() { 455 Ω(len(enc.Validate().Errors)).Should(Equal(1)) 456 Ω(enc.Validate().Errors[0].Error()).Should(MatchRegexp("^invalid Go package path")) 457 }) 458 }) 459 460 Context("with package in gopath", func() { 461 BeforeEach(func() { 462 packagePath := path.Join(cellarPath, "../goa/encoding/foo") 463 464 Ω(os.MkdirAll(packagePath, 0777)).ShouldNot(HaveOccurred()) 465 Ω(ioutil.WriteFile(path.Join(packagePath, "encoding.go"), []byte("package foo"), 0777)).ShouldNot(HaveOccurred()) 466 }) 467 468 It("validates EncoderDefinition", func() { 469 Ω(enc.Validate().Errors).Should(BeNil()) 470 }) 471 }) 472 473 Context("with package in vendor", func() { 474 BeforeEach(func() { 475 packagePath := path.Join(cellarPath, "vendor/github.com/goadesign/goa/encoding/foo") 476 477 Ω(os.MkdirAll(packagePath, 0777)).ShouldNot(HaveOccurred()) 478 Ω(ioutil.WriteFile(path.Join(packagePath, "encoding.go"), []byte("package foo"), 0777)).ShouldNot(HaveOccurred()) 479 }) 480 481 It("validates EncoderDefinition", func() { 482 Ω(enc.Validate().Errors).Should(BeNil()) 483 }) 484 }) 485 }) 486 })