github.com/brycereitano/goa@v0.0.0-20170315073847-8ffa6c85e265/goagen/codegen/types_test.go (about)

     1  package codegen_test
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	. "github.com/goadesign/goa/design"
     8  	. "github.com/goadesign/goa/design/apidsl"
     9  	"github.com/goadesign/goa/dslengine"
    10  	"github.com/goadesign/goa/goagen/codegen"
    11  	. "github.com/onsi/ginkgo"
    12  	. "github.com/onsi/gomega"
    13  )
    14  
    15  var _ = Describe("code generation", func() {
    16  	BeforeEach(func() {
    17  		codegen.TempCount = 0
    18  	})
    19  
    20  	Describe("Goify", func() {
    21  		Context("given a string with an initialism", func() {
    22  			var str, goified, expected string
    23  			var firstUpper bool
    24  			JustBeforeEach(func() {
    25  				goified = codegen.Goify(str, firstUpper)
    26  			})
    27  
    28  			Context("with first upper false", func() {
    29  				BeforeEach(func() {
    30  					firstUpper = false
    31  					str = "blue_id"
    32  					expected = "blueID"
    33  				})
    34  				It("creates a lowercased camelcased string", func() {
    35  					Ω(goified).Should(Equal(expected))
    36  				})
    37  			})
    38  			Context("with first upper false normal identifier", func() {
    39  				BeforeEach(func() {
    40  					firstUpper = false
    41  					str = "blue"
    42  					expected = "blue"
    43  				})
    44  				It("creates an uppercased camelcased string", func() {
    45  					Ω(goified).Should(Equal(expected))
    46  				})
    47  			})
    48  			Context("with first upper false and UUID", func() {
    49  				BeforeEach(func() {
    50  					firstUpper = false
    51  					str = "blue_uuid"
    52  					expected = "blueUUID"
    53  				})
    54  				It("creates an uppercased camelcased string", func() {
    55  					Ω(goified).Should(Equal(expected))
    56  				})
    57  			})
    58  			Context("with first upper true", func() {
    59  				BeforeEach(func() {
    60  					firstUpper = true
    61  					str = "blue_id"
    62  					expected = "BlueID"
    63  				})
    64  				It("creates an uppercased camelcased string", func() {
    65  					Ω(goified).Should(Equal(expected))
    66  				})
    67  			})
    68  			Context("with first upper true and UUID", func() {
    69  				BeforeEach(func() {
    70  					firstUpper = true
    71  					str = "blue_uuid"
    72  					expected = "BlueUUID"
    73  				})
    74  				It("creates an uppercased camelcased string", func() {
    75  					Ω(goified).Should(Equal(expected))
    76  				})
    77  			})
    78  			Context("with first upper true normal identifier", func() {
    79  				BeforeEach(func() {
    80  					firstUpper = true
    81  					str = "blue"
    82  					expected = "Blue"
    83  				})
    84  				It("creates an uppercased camelcased string", func() {
    85  					Ω(goified).Should(Equal(expected))
    86  				})
    87  			})
    88  			Context("with first upper false normal identifier", func() {
    89  				BeforeEach(func() {
    90  					firstUpper = false
    91  					str = "Blue"
    92  					expected = "blue"
    93  				})
    94  				It("creates a lowercased string", func() {
    95  					Ω(goified).Should(Equal(expected))
    96  				})
    97  			})
    98  			Context("with first upper true normal identifier", func() {
    99  				BeforeEach(func() {
   100  					firstUpper = true
   101  					str = "Blue"
   102  					expected = "Blue"
   103  				})
   104  				It("creates an uppercased string", func() {
   105  					Ω(goified).Should(Equal(expected))
   106  				})
   107  			})
   108  			Context("with invalid identifier", func() {
   109  				BeforeEach(func() {
   110  					firstUpper = true
   111  					str = "Blue%50"
   112  					expected = "Blue50"
   113  				})
   114  				It("creates an uppercased string", func() {
   115  					Ω(goified).Should(Equal(expected))
   116  				})
   117  			})
   118  
   119  			Context("with invalid identifier firstupper false", func() {
   120  				BeforeEach(func() {
   121  					firstUpper = false
   122  					str = "Blue%50"
   123  					expected = "blue50"
   124  				})
   125  				It("creates an uppercased string", func() {
   126  					Ω(goified).Should(Equal(expected))
   127  				})
   128  			})
   129  
   130  			Context("with only UUID and firstupper false", func() {
   131  				BeforeEach(func() {
   132  					firstUpper = false
   133  					str = "UUID"
   134  					expected = "uuid"
   135  				})
   136  				It("creates a lowercased string", func() {
   137  					Ω(goified).Should(Equal(expected))
   138  				})
   139  			})
   140  
   141  			Context("with consecutives invalid identifiers", func() {
   142  				BeforeEach(func() {
   143  					firstUpper = false
   144  					str = "[[fields___type]]"
   145  					expected = "fieldsType"
   146  				})
   147  				It("creates a camelcased string", func() {
   148  					Ω(goified).Should(Equal(expected))
   149  				})
   150  			})
   151  
   152  			Context("with consecutives invalid identifiers", func() {
   153  				BeforeEach(func() {
   154  					firstUpper = true
   155  					str = "[[fields___type]]"
   156  					expected = "FieldsType"
   157  				})
   158  				It("creates a camelcased string", func() {
   159  					Ω(goified).Should(Equal(expected))
   160  				})
   161  			})
   162  
   163  			Context("with all invalid identifiers", func() {
   164  				BeforeEach(func() {
   165  					firstUpper = false
   166  					str = "[["
   167  					expected = ""
   168  				})
   169  				It("creates an empty string", func() {
   170  					Ω(goified).Should(Equal(expected))
   171  				})
   172  			})
   173  
   174  		})
   175  
   176  	})
   177  
   178  	Describe("GoTypeDef", func() {
   179  		Context("given an attribute definition with fields", func() {
   180  			var att *AttributeDefinition
   181  			var object Object
   182  			var required *dslengine.ValidationDefinition
   183  			var st string
   184  
   185  			JustBeforeEach(func() {
   186  				att = new(AttributeDefinition)
   187  				att.Type = object
   188  				if required != nil {
   189  					att.Validation = required
   190  				}
   191  				st = codegen.GoTypeDef(att, 0, true, false)
   192  			})
   193  
   194  			Context("of primitive types", func() {
   195  				BeforeEach(func() {
   196  					object = Object{
   197  						"foo": &AttributeDefinition{Type: Integer},
   198  						"bar": &AttributeDefinition{Type: String},
   199  						"baz": &AttributeDefinition{Type: DateTime},
   200  						"qux": &AttributeDefinition{Type: UUID},
   201  					}
   202  					required = nil
   203  				})
   204  
   205  				It("produces the struct go code", func() {
   206  					expected := "struct {\n" +
   207  						"	Bar *string `form:\"bar,omitempty\" json:\"bar,omitempty\" xml:\"bar,omitempty\"`\n" +
   208  						"	Baz *time.Time `form:\"baz,omitempty\" json:\"baz,omitempty\" xml:\"baz,omitempty\"`\n" +
   209  						"	Foo *int `form:\"foo,omitempty\" json:\"foo,omitempty\" xml:\"foo,omitempty\"`\n" +
   210  						"	Qux *uuid.UUID `form:\"qux,omitempty\" json:\"qux,omitempty\" xml:\"qux,omitempty\"`\n" +
   211  						"}"
   212  					Ω(st).Should(Equal(expected))
   213  				})
   214  
   215  				Context("using struct tags metadata", func() {
   216  					tn1 := "struct:tag:foo"
   217  					tv11 := "bar"
   218  					tv12 := "baz"
   219  					tn2 := "struct:tag:foo2"
   220  					tv21 := "bar2"
   221  
   222  					BeforeEach(func() {
   223  						object["foo"].Metadata = dslengine.MetadataDefinition{
   224  							tn1: []string{tv11, tv12},
   225  							tn2: []string{tv21},
   226  						}
   227  					})
   228  
   229  					It("produces the struct tags", func() {
   230  						expected := fmt.Sprintf("struct {\n"+
   231  							"	Bar *string `form:\"bar,omitempty\" json:\"bar,omitempty\" xml:\"bar,omitempty\"`\n"+
   232  							"	Baz *time.Time `form:\"baz,omitempty\" json:\"baz,omitempty\" xml:\"baz,omitempty\"`\n"+
   233  							"	Foo *int `%s:\"%s,%s\" %s:\"%s\"`\n"+
   234  							"	Qux *uuid.UUID `form:\"qux,omitempty\" json:\"qux,omitempty\" xml:\"qux,omitempty\"`\n"+
   235  							"}", tn1[11:], tv11, tv12, tn2[11:], tv21)
   236  						Ω(st).Should(Equal(expected))
   237  					})
   238  				})
   239  
   240  				Context("using struct field name metadata", func() {
   241  					BeforeEach(func() {
   242  						object["foo"].Metadata = dslengine.MetadataDefinition{
   243  							"struct:field:name": []string{"serviceName", "unused"},
   244  						}
   245  					})
   246  
   247  					It("produces the struct tags", func() {
   248  						expected := "struct {\n" +
   249  							"	Bar *string `form:\"bar,omitempty\" json:\"bar,omitempty\" xml:\"bar,omitempty\"`\n" +
   250  							"	Baz *time.Time `form:\"baz,omitempty\" json:\"baz,omitempty\" xml:\"baz,omitempty\"`\n" +
   251  							"	ServiceName *int `form:\"foo,omitempty\" json:\"foo,omitempty\" xml:\"foo,omitempty\"`\n" +
   252  							"	Qux *uuid.UUID `form:\"qux,omitempty\" json:\"qux,omitempty\" xml:\"qux,omitempty\"`\n" +
   253  							"}"
   254  						Ω(st).Should(Equal(expected))
   255  					})
   256  				})
   257  
   258  				Context("using struct field type metadata", func() {
   259  					BeforeEach(func() {
   260  						object["foo"].Metadata = dslengine.MetadataDefinition{
   261  							"struct:field:type": []string{"[]byte"},
   262  						}
   263  					})
   264  
   265  					It("produces the struct tags", func() {
   266  						expected := "struct {\n" +
   267  							"	Bar *string `form:\"bar,omitempty\" json:\"bar,omitempty\" xml:\"bar,omitempty\"`\n" +
   268  							"	Baz *time.Time `form:\"baz,omitempty\" json:\"baz,omitempty\" xml:\"baz,omitempty\"`\n" +
   269  							"	Foo []byte `form:\"foo,omitempty\" json:\"foo,omitempty\" xml:\"foo,omitempty\"`\n" +
   270  							"	Qux *uuid.UUID `form:\"qux,omitempty\" json:\"qux,omitempty\" xml:\"qux,omitempty\"`\n" +
   271  							"}"
   272  						Ω(st).Should(Equal(expected))
   273  					})
   274  				})
   275  			})
   276  
   277  			Context("of hash of primitive types", func() {
   278  				BeforeEach(func() {
   279  					elemType := &AttributeDefinition{Type: Integer}
   280  					keyType := &AttributeDefinition{Type: Integer}
   281  					hash := &Hash{KeyType: keyType, ElemType: elemType}
   282  					object = Object{
   283  						"foo": &AttributeDefinition{Type: hash},
   284  					}
   285  					required = nil
   286  				})
   287  
   288  				It("produces the struct go code", func() {
   289  					Ω(st).Should(Equal("struct {\n\tFoo map[int]int `form:\"foo,omitempty\" json:\"foo,omitempty\" xml:\"foo,omitempty\"`\n}"))
   290  				})
   291  			})
   292  
   293  			Context("of array of primitive types", func() {
   294  				BeforeEach(func() {
   295  					elemType := &AttributeDefinition{Type: Integer}
   296  					array := &Array{ElemType: elemType}
   297  					object = Object{
   298  						"foo": &AttributeDefinition{Type: array},
   299  					}
   300  					required = nil
   301  				})
   302  
   303  				It("produces the struct go code", func() {
   304  					Ω(st).Should(Equal("struct {\n\tFoo []int `form:\"foo,omitempty\" json:\"foo,omitempty\" xml:\"foo,omitempty\"`\n}"))
   305  				})
   306  			})
   307  
   308  			Context("of hash of objects", func() {
   309  				BeforeEach(func() {
   310  					elem := Object{
   311  						"elemAtt": &AttributeDefinition{Type: Integer},
   312  					}
   313  					key := Object{
   314  						"keyAtt": &AttributeDefinition{Type: String},
   315  					}
   316  					elemType := &AttributeDefinition{Type: elem}
   317  					keyType := &AttributeDefinition{Type: key}
   318  					hash := &Hash{KeyType: keyType, ElemType: elemType}
   319  					object = Object{
   320  						"foo": &AttributeDefinition{Type: hash},
   321  					}
   322  					required = nil
   323  				})
   324  
   325  				It("produces the struct go code", func() {
   326  					expected := "struct {\n" +
   327  						"	Foo map[*struct {\n" +
   328  						"		KeyAtt *string `form:\"keyAtt,omitempty\" json:\"keyAtt,omitempty\" xml:\"keyAtt,omitempty\"`\n" +
   329  						"	}]*struct {\n" +
   330  						"		ElemAtt *int `form:\"elemAtt,omitempty\" json:\"elemAtt,omitempty\" xml:\"elemAtt,omitempty\"`\n" +
   331  						"	} `form:\"foo,omitempty\" json:\"foo,omitempty\" xml:\"foo,omitempty\"`\n" +
   332  						"}"
   333  					Ω(st).Should(Equal(expected))
   334  				})
   335  			})
   336  
   337  			Context("of array of objects", func() {
   338  				BeforeEach(func() {
   339  					obj := Object{
   340  						"bar": &AttributeDefinition{Type: Integer},
   341  					}
   342  					elemType := &AttributeDefinition{Type: obj}
   343  					array := &Array{ElemType: elemType}
   344  					object = Object{
   345  						"foo": &AttributeDefinition{Type: array},
   346  					}
   347  					required = nil
   348  				})
   349  
   350  				It("produces the struct go code", func() {
   351  					expected := "struct {\n" +
   352  						"	Foo []*struct {\n" +
   353  						"		Bar *int `form:\"bar,omitempty\" json:\"bar,omitempty\" xml:\"bar,omitempty\"`\n" +
   354  						"	} `form:\"foo,omitempty\" json:\"foo,omitempty\" xml:\"foo,omitempty\"`\n" +
   355  						"}"
   356  					Ω(st).Should(Equal(expected))
   357  				})
   358  
   359  				Context("that are required", func() {
   360  					BeforeEach(func() {
   361  						required = &dslengine.ValidationDefinition{
   362  							Required: []string{"foo"},
   363  						}
   364  					})
   365  
   366  					It("produces the struct go code", func() {
   367  						expected := "struct {\n" +
   368  							"	Foo []*struct {\n" +
   369  							"		Bar *int `form:\"bar,omitempty\" json:\"bar,omitempty\" xml:\"bar,omitempty\"`\n" +
   370  							"	} `form:\"foo\" json:\"foo\" xml:\"foo\"`\n" +
   371  							"}"
   372  						Ω(st).Should(Equal(expected))
   373  					})
   374  				})
   375  			})
   376  
   377  			Context("that are required", func() {
   378  				BeforeEach(func() {
   379  					object = Object{
   380  						"foo": &AttributeDefinition{Type: Integer},
   381  					}
   382  					required = &dslengine.ValidationDefinition{
   383  						Required: []string{"foo"},
   384  					}
   385  				})
   386  
   387  				It("produces the struct go code", func() {
   388  					expected := "struct {\n" +
   389  						"	Foo int `form:\"foo\" json:\"foo\" xml:\"foo\"`\n" +
   390  						"}"
   391  					Ω(st).Should(Equal(expected))
   392  				})
   393  			})
   394  
   395  		})
   396  
   397  		Context("given an array", func() {
   398  			var elemType *AttributeDefinition
   399  			var source string
   400  
   401  			JustBeforeEach(func() {
   402  				array := &Array{ElemType: elemType}
   403  				att := &AttributeDefinition{Type: array}
   404  				source = codegen.GoTypeDef(att, 0, true, false)
   405  			})
   406  
   407  			Context("of primitive type", func() {
   408  				BeforeEach(func() {
   409  					elemType = &AttributeDefinition{Type: Integer}
   410  				})
   411  
   412  				It("produces the array go code", func() {
   413  					Ω(source).Should(Equal("[]int"))
   414  				})
   415  
   416  			})
   417  
   418  			Context("of object type", func() {
   419  				BeforeEach(func() {
   420  					object := Object{
   421  						"foo": &AttributeDefinition{Type: Integer},
   422  						"bar": &AttributeDefinition{Type: String},
   423  					}
   424  					elemType = &AttributeDefinition{Type: object}
   425  				})
   426  
   427  				It("produces the array go code", func() {
   428  					Ω(source).Should(Equal("[]*struct {\n\tBar *string `form:\"bar,omitempty\" json:\"bar,omitempty\" xml:\"bar,omitempty\"`\n\tFoo *int `form:\"foo,omitempty\" json:\"foo,omitempty\" xml:\"foo,omitempty\"`\n}"))
   429  				})
   430  			})
   431  		})
   432  
   433  	})
   434  })
   435  
   436  var _ = Describe("GoTypeTransform", func() {
   437  	var source, target *UserTypeDefinition
   438  	var targetPkg, funcName string
   439  
   440  	var transform string
   441  
   442  	BeforeEach(func() {
   443  		dslengine.Reset()
   444  	})
   445  	JustBeforeEach(func() {
   446  		err := dslengine.Run()
   447  		Ω(err).ShouldNot(HaveOccurred())
   448  		transform, _ = codegen.GoTypeTransform(source, target, targetPkg, funcName)
   449  	})
   450  
   451  	Context("transforming simple objects", func() {
   452  		const attName = "att"
   453  		BeforeEach(func() {
   454  			source = Type("Source", func() {
   455  				Attribute(attName)
   456  			})
   457  			target = Type("Target", func() {
   458  				Attribute(attName)
   459  			})
   460  			funcName = "Transform"
   461  		})
   462  
   463  		It("generates a simple assignment", func() {
   464  			Ω(transform).Should(Equal(`func Transform(source *Source) (target *Target) {
   465  	target = new(Target)
   466  	target.Att = source.Att
   467  	return
   468  }
   469  `))
   470  		})
   471  	})
   472  
   473  	Context("transforming objects with attributes with map key metadata", func() {
   474  		const mapKey = "key"
   475  		BeforeEach(func() {
   476  			source = Type("Source", func() {
   477  				Attribute("foo", func() {
   478  					Metadata(codegen.TransformMapKey, mapKey)
   479  				})
   480  			})
   481  			target = Type("Target", func() {
   482  				Attribute("bar", func() {
   483  					Metadata(codegen.TransformMapKey, mapKey)
   484  				})
   485  			})
   486  			funcName = "Transform"
   487  		})
   488  
   489  		It("generates a simple assignment", func() {
   490  			Ω(transform).Should(Equal(`func Transform(source *Source) (target *Target) {
   491  	target = new(Target)
   492  	target.Bar = source.Foo
   493  	return
   494  }
   495  `))
   496  		})
   497  	})
   498  
   499  	Context("transforming objects with array attributes", func() {
   500  		const attName = "att"
   501  		BeforeEach(func() {
   502  			source = Type("Source", func() {
   503  				Attribute(attName, ArrayOf(Integer))
   504  			})
   505  			target = Type("Target", func() {
   506  				Attribute(attName, ArrayOf(Integer))
   507  			})
   508  			funcName = "Transform"
   509  		})
   510  
   511  		It("generates a simple assignment", func() {
   512  			Ω(transform).Should(Equal(`func Transform(source *Source) (target *Target) {
   513  	target = new(Target)
   514  	target.Att = make([]int, len(source.Att))
   515  	for i, v := range source.Att {
   516  		target.Att[i] = source.Att[i]
   517  	}
   518  	return
   519  }
   520  `))
   521  		})
   522  	})
   523  
   524  	Context("transforming objects with hash attributes", func() {
   525  		const attName = "att"
   526  		BeforeEach(func() {
   527  			elem := Type("elem", func() {
   528  				Attribute("foo", Integer)
   529  				Attribute("bar")
   530  			})
   531  			source = Type("Source", func() {
   532  				Attribute(attName, HashOf(String, elem))
   533  			})
   534  			target = Type("Target", func() {
   535  				Attribute(attName, HashOf(String, elem))
   536  			})
   537  			funcName = "Transform"
   538  		})
   539  
   540  		It("generates a simple assignment", func() {
   541  			Ω(transform).Should(Equal(`func Transform(source *Source) (target *Target) {
   542  	target = new(Target)
   543  	target.Att = make(map[string]*Elem, len(source.Att))
   544  	for k, v := range source.Att {
   545  		var tk string
   546  		tk = k
   547  		var tv *Elem
   548  		tv = new(Elem)
   549  		tv.Bar = v.Bar
   550  		tv.Foo = v.Foo
   551  		target.Att[tk] = tv
   552  	}
   553  	return
   554  }
   555  `))
   556  		})
   557  	})
   558  
   559  	Context("transforming objects with recursive attributes", func() {
   560  		const attName = "att"
   561  		BeforeEach(func() {
   562  			inner := Type("inner", func() {
   563  				Attribute("foo", Integer)
   564  			})
   565  			outer := Type("outer", func() {
   566  				Attribute("in", inner)
   567  			})
   568  			array := Type("array", func() {
   569  				Attribute("elem", ArrayOf(outer))
   570  			})
   571  			hash := Type("hash", func() {
   572  				Attribute("elem", HashOf(Integer, outer))
   573  			})
   574  			source = Type("Source", func() {
   575  				Attribute("outer", outer)
   576  				Attribute("array", array)
   577  				Attribute("hash", hash)
   578  			})
   579  			target = Type("Target", func() {
   580  				Attribute("outer", outer)
   581  				Attribute("array", array)
   582  				Attribute("hash", hash)
   583  			})
   584  			funcName = "Transform"
   585  		})
   586  
   587  		It("generates the proper assignments", func() {
   588  			Ω(transform).Should(Equal(`func Transform(source *Source) (target *Target) {
   589  	target = new(Target)
   590  	target.Array = new(Array)
   591  	target.Array.Elem = make([]*Outer, len(source.Array.Elem))
   592  	for i, v := range source.Array.Elem {
   593  		target.Array.Elem[i] = new(Outer)
   594  		target.Array.Elem[i].In = new(Inner)
   595  		target.Array.Elem[i].In.Foo = source.Array.Elem[i].In.Foo
   596  	}
   597  	target.Hash = new(Hash)
   598  	target.Hash.Elem = make(map[int]*Outer, len(source.Hash.Elem))
   599  	for k, v := range source.Hash.Elem {
   600  		var tk int
   601  		tk = k
   602  		var tv *Outer
   603  		tv = new(Outer)
   604  		tv.In = new(Inner)
   605  		tv.In.Foo = v.In.Foo
   606  		target.Hash.Elem[tk] = tv
   607  	}
   608  	target.Outer = new(Outer)
   609  	target.Outer.In = new(Inner)
   610  	target.Outer.In.Foo = source.Outer.In.Foo
   611  	return
   612  }
   613  `))
   614  		})
   615  	})
   616  })
   617  
   618  var _ = Describe("GoTypeDesc", func() {
   619  	Context("With a type with a description", func() {
   620  		var description string
   621  		var ut *UserTypeDefinition
   622  
   623  		var desc string
   624  
   625  		BeforeEach(func() {
   626  			description = "foo"
   627  		})
   628  
   629  		JustBeforeEach(func() {
   630  			ut = &UserTypeDefinition{AttributeDefinition: &AttributeDefinition{Description: description}}
   631  			desc = codegen.GoTypeDesc(ut, false)
   632  		})
   633  
   634  		It("uses the description", func() {
   635  			Ω(desc).Should(Equal(description))
   636  		})
   637  
   638  		Context("containing newlines", func() {
   639  			BeforeEach(func() {
   640  				description = "foo\nbar"
   641  			})
   642  
   643  			It("escapes the new lines", func() {
   644  				Ω(desc).Should(Equal(strings.Replace(description, "\n", "\n// ", -1)))
   645  			})
   646  		})
   647  	})
   648  })