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 })