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