github.com/ManabuSeki/goa-v1@v1.4.3/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  })