github.com/blp1526/goa@v1.4.0/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  )