github.com/furusax0621/goa-v1@v1.4.3/goagen/gen_swagger/swagger_test.go (about) 1 package genswagger_test 2 3 import ( 4 "bytes" 5 "encoding/json" 6 7 "github.com/go-openapi/loads" 8 _ "github.com/goadesign/goa-cellar/design" 9 . "github.com/goadesign/goa/design" 10 . "github.com/goadesign/goa/design/apidsl" 11 "github.com/goadesign/goa/dslengine" 12 genschema "github.com/goadesign/goa/goagen/gen_schema" 13 genswagger "github.com/goadesign/goa/goagen/gen_swagger" 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 ) 17 18 // validateSwagger validates that the given swagger object represents a valid Swagger spec. 19 func validateSwagger(swagger *genswagger.Swagger) { 20 b, err := json.Marshal(swagger) 21 Ω(err).ShouldNot(HaveOccurred()) 22 doc, err := loads.Analyzed(json.RawMessage(b), "") 23 Ω(err).ShouldNot(HaveOccurred()) 24 Ω(doc).ShouldNot(BeNil()) 25 } 26 27 // validateSwaggerWithFragments validates that the given swagger object represents a valid Swagger spec 28 // and contains fragments 29 func validateSwaggerWithFragments(swagger *genswagger.Swagger, fragments [][]byte) { 30 b, err := json.Marshal(swagger) 31 Ω(err).ShouldNot(HaveOccurred()) 32 doc, err := loads.Analyzed(json.RawMessage(b), "") 33 Ω(err).ShouldNot(HaveOccurred()) 34 Ω(doc).ShouldNot(BeNil()) 35 for _, sub := range fragments { 36 Ω(bytes.Contains(b, sub)).Should(BeTrue()) 37 } 38 } 39 40 var _ = Describe("New", func() { 41 var swagger *genswagger.Swagger 42 var newErr error 43 44 BeforeEach(func() { 45 swagger = nil 46 newErr = nil 47 dslengine.Reset() 48 genschema.Definitions = make(map[string]*genschema.JSONSchema) 49 }) 50 51 JustBeforeEach(func() { 52 err := dslengine.Run() 53 Ω(err).ShouldNot(HaveOccurred()) 54 swagger, newErr = genswagger.New(Design) 55 }) 56 57 Context("with a valid API definition", func() { 58 const ( 59 title = "title" 60 description = "description" 61 terms = "terms" 62 contactEmail = "contactEmail@goa.design" 63 contactName = "contactName" 64 contactURL = "http://contactURL.com" 65 license = "license" 66 licenseURL = "http://licenseURL.com" 67 host = "host" 68 scheme = "https" 69 basePath = "/base" 70 tag = "tag" 71 docDesc = "doc description" 72 docURL = "http://docURL.com" 73 ) 74 75 BeforeEach(func() { 76 API("test", func() { 77 Title(title) 78 Metadata("swagger:tag:" + tag) 79 Metadata("swagger:tag:"+tag+":desc", "Tag desc.") 80 Metadata("swagger:tag:"+tag+":url", "http://example.com/tag") 81 Metadata("swagger:tag:"+tag+":url:desc", "Huge docs") 82 Description(description) 83 TermsOfService(terms) 84 Contact(func() { 85 Email(contactEmail) 86 Name(contactName) 87 URL(contactURL) 88 }) 89 License(func() { 90 Name(license) 91 URL(licenseURL) 92 }) 93 Docs(func() { 94 Description(docDesc) 95 URL(docURL) 96 }) 97 Host(host) 98 Scheme(scheme) 99 BasePath(basePath) 100 }) 101 }) 102 103 It("sets all the basic fields", func() { 104 Ω(newErr).ShouldNot(HaveOccurred()) 105 Ω(swagger).Should(Equal(&genswagger.Swagger{ 106 Swagger: "2.0", 107 Info: &genswagger.Info{ 108 Title: title, 109 Description: description, 110 TermsOfService: terms, 111 Contact: &ContactDefinition{ 112 Name: contactName, 113 Email: contactEmail, 114 URL: contactURL, 115 }, 116 License: &LicenseDefinition{ 117 Name: license, 118 URL: licenseURL, 119 }, 120 Version: "", 121 }, 122 Host: host, 123 BasePath: basePath, 124 Schemes: []string{"https"}, 125 Paths: make(map[string]interface{}), 126 Consumes: []string{"application/json", "application/xml", "application/gob", "application/x-gob"}, 127 Produces: []string{"application/json", "application/xml", "application/gob", "application/x-gob"}, 128 Tags: []*genswagger.Tag{{Name: tag, Description: "Tag desc.", ExternalDocs: &genswagger.ExternalDocs{ 129 URL: "http://example.com/tag", Description: "Huge docs", 130 }}}, 131 ExternalDocs: &genswagger.ExternalDocs{ 132 Description: docDesc, 133 URL: docURL, 134 }, 135 })) 136 }) 137 138 It("serializes into valid swagger JSON", func() { validateSwagger(swagger) }) 139 140 Context("with base params", func() { 141 const ( 142 basePath = "/s/:strParam/i/:intParam/n/:numParam/b/:boolParam" 143 strParam = "strParam" 144 intParam = "intParam" 145 numParam = "numParam" 146 boolParam = "boolParam" 147 queryParam = "queryParam" 148 description = "description" 149 intMin = 1.0 150 floatMax = 2.4 151 enum1 = "enum1" 152 enum2 = "enum2" 153 ) 154 155 BeforeEach(func() { 156 base := Design.DSLFunc 157 Design.DSLFunc = func() { 158 base() 159 BasePath(basePath) 160 Params(func() { 161 Param(strParam, String, func() { 162 Description(description) 163 Format("email") 164 }) 165 Param(intParam, Integer, func() { 166 Minimum(intMin) 167 }) 168 Param(numParam, Number, func() { 169 Maximum(floatMax) 170 }) 171 Param(boolParam, Boolean) 172 Param(queryParam, func() { 173 Enum(enum1, enum2) 174 }) 175 }) 176 } 177 }) 178 179 It("sets the BasePath and Parameters fields", func() { 180 Ω(newErr).ShouldNot(HaveOccurred()) 181 Ω(swagger.BasePath).Should(Equal(basePath)) 182 Ω(swagger.Parameters).Should(HaveLen(5)) 183 Ω(swagger.Parameters[strParam]).ShouldNot(BeNil()) 184 Ω(swagger.Parameters[strParam].Name).Should(Equal(strParam)) 185 Ω(swagger.Parameters[strParam].In).Should(Equal("path")) 186 Ω(swagger.Parameters[strParam].Description).Should(Equal("description")) 187 Ω(swagger.Parameters[strParam].Required).Should(BeTrue()) 188 Ω(swagger.Parameters[strParam].Type).Should(Equal("string")) 189 Ω(swagger.Parameters[strParam].Format).Should(Equal("email")) 190 Ω(swagger.Parameters[intParam]).ShouldNot(BeNil()) 191 Ω(swagger.Parameters[intParam].Name).Should(Equal(intParam)) 192 Ω(swagger.Parameters[intParam].In).Should(Equal("path")) 193 Ω(swagger.Parameters[intParam].Required).Should(BeTrue()) 194 Ω(swagger.Parameters[intParam].Type).Should(Equal("integer")) 195 Ω(*swagger.Parameters[intParam].Minimum).Should(Equal(intMin)) 196 Ω(swagger.Parameters[numParam]).ShouldNot(BeNil()) 197 Ω(swagger.Parameters[numParam].Name).Should(Equal(numParam)) 198 Ω(swagger.Parameters[numParam].In).Should(Equal("path")) 199 Ω(swagger.Parameters[numParam].Required).Should(BeTrue()) 200 Ω(swagger.Parameters[numParam].Type).Should(Equal("number")) 201 Ω(*swagger.Parameters[numParam].Maximum).Should(Equal(floatMax)) 202 Ω(swagger.Parameters[boolParam]).ShouldNot(BeNil()) 203 Ω(swagger.Parameters[boolParam].Name).Should(Equal(boolParam)) 204 Ω(swagger.Parameters[boolParam].In).Should(Equal("path")) 205 Ω(swagger.Parameters[boolParam].Required).Should(BeTrue()) 206 Ω(swagger.Parameters[boolParam].Type).Should(Equal("boolean")) 207 Ω(swagger.Parameters[queryParam]).ShouldNot(BeNil()) 208 Ω(swagger.Parameters[queryParam].Name).Should(Equal(queryParam)) 209 Ω(swagger.Parameters[queryParam].In).Should(Equal("query")) 210 Ω(swagger.Parameters[queryParam].Type).Should(Equal("string")) 211 Ω(swagger.Parameters[queryParam].Enum).Should(Equal([]interface{}{enum1, enum2})) 212 }) 213 214 It("serializes into valid swagger JSON", func() { validateSwagger(swagger) }) 215 }) 216 217 Context("with required payload", func() { 218 BeforeEach(func() { 219 p := Type("RequiredPayload", func() { 220 Member("m1", String) 221 }) 222 Resource("res", func() { 223 Action("act", func() { 224 Routing( 225 PUT("/"), 226 ) 227 Payload(p) 228 }) 229 }) 230 }) 231 232 It("serializes into valid swagger JSON", func() { 233 validateSwaggerWithFragments(swagger, [][]byte{ 234 []byte(`"required":true`), 235 }) 236 }) 237 }) 238 239 Context("with a payload of type Any", func() { 240 BeforeEach(func() { 241 Resource("res", func() { 242 Action("act", func() { 243 Routing( 244 PUT("/"), 245 ) 246 Payload(Any, func() { 247 Example("example") 248 }) 249 }) 250 }) 251 }) 252 253 It("serializes into valid swagger JSON", func() { 254 validateSwaggerWithFragments(swagger, [][]byte{ 255 []byte(`"ActResPayload":{"title":"ActResPayload","example":"example"}`), 256 }) 257 }) 258 259 }) 260 261 Context("with optional payload", func() { 262 BeforeEach(func() { 263 p := Type("OptionalPayload", func() { 264 Member("m1", String) 265 }) 266 Resource("res", func() { 267 Action("act", func() { 268 Routing( 269 PUT("/"), 270 ) 271 OptionalPayload(p) 272 }) 273 }) 274 }) 275 276 It("serializes into valid swagger JSON", func() { 277 validateSwaggerWithFragments(swagger, [][]byte{ 278 []byte(`"required":false`), 279 }) 280 }) 281 282 }) 283 284 Context("with multipart/form-data payload", func() { 285 BeforeEach(func() { 286 f := Type("MultipartPayload", func() { 287 Attribute("image", File, "Binary image data") 288 }) 289 Resource("res", func() { 290 Action("act", func() { 291 Routing( 292 PUT("/"), 293 ) 294 MultipartForm() 295 Payload(f) 296 }) 297 }) 298 }) 299 300 It("does not modify the API level consumes", func() { 301 Ω(newErr).ShouldNot(HaveOccurred()) 302 Ω(swagger.Consumes).Should(HaveLen(4)) 303 Ω(swagger.Consumes).Should(ConsistOf("application/json", "application/xml", "application/gob", "application/x-gob")) 304 }) 305 306 It("adds an Action level consumes for multipart/form-data", func() { 307 Ω(newErr).ShouldNot(HaveOccurred()) 308 Ω(swagger.Paths).Should(HaveLen(1)) 309 Ω(swagger.Paths["/"]).ShouldNot(BeNil()) 310 311 a := swagger.Paths["/"].(*genswagger.Path) 312 Ω(a.Put).ShouldNot(BeNil()) 313 cs := a.Put.Consumes 314 Ω(cs).Should(HaveLen(1)) 315 Ω(cs[0]).Should(Equal("multipart/form-data")) 316 }) 317 318 It("adds an File parameter", func() { 319 Ω(newErr).ShouldNot(HaveOccurred()) 320 Ω(swagger.Paths).Should(HaveLen(1)) 321 Ω(swagger.Paths["/"]).ShouldNot(BeNil()) 322 323 a := swagger.Paths["/"].(*genswagger.Path) 324 Ω(a.Put).ShouldNot(BeNil()) 325 ps := a.Put.Parameters 326 Ω(ps).Should(HaveLen(1)) 327 Ω(ps[0]).Should(Equal(&genswagger.Parameter{In: "formData", Name: "image", Type: "file", Description: "Binary image data", Required: false})) 328 }) 329 330 It("serializes into valid swagger JSON", func() { validateSwagger(swagger) }) 331 }) 332 333 Context("with recursive payload", func() { 334 BeforeEach(func() { 335 p := Type("RecursivePayload", func() { 336 Member("m1", "RecursivePayload") 337 Member("m2", ArrayOf("RecursivePayload")) 338 Member("m3", HashOf(String, "RecursivePayload")) 339 Member("m4", func() { 340 Member("m5", String) 341 Member("m6", "RecursivePayload") 342 }) 343 }) 344 Resource("res", func() { 345 Action("act", func() { 346 Routing( 347 PUT("/"), 348 ) 349 Payload(p) 350 }) 351 }) 352 }) 353 354 It("serializes into valid swagger JSON", func() { validateSwagger(swagger) }) 355 }) 356 357 Context("with zero value validations", func() { 358 const ( 359 intParam = "intParam" 360 numParam = "numParam" 361 strParam = "strParam" 362 intMin = 0.0 363 floatMax = 0.0 364 ) 365 366 BeforeEach(func() { 367 PayloadWithZeroValueValidations := Type("PayloadWithZeroValueValidations", func() { 368 Attribute(strParam, String, func() { 369 MinLength(0) 370 MaxLength(0) 371 }) 372 }) 373 Resource("res", func() { 374 Action("act", func() { 375 Routing( 376 PUT("/"), 377 ) 378 Params(func() { 379 Param(intParam, Integer, func() { 380 Minimum(intMin) 381 }) 382 Param(numParam, Number, func() { 383 Maximum(floatMax) 384 }) 385 }) 386 Payload(PayloadWithZeroValueValidations) 387 }) 388 }) 389 }) 390 391 It("serializes into valid swagger JSON", func() { 392 validateSwaggerWithFragments(swagger, [][]byte{ 393 // payload 394 []byte(`"minLength":0`), 395 []byte(`"maxLength":0`), 396 // param 397 []byte(`"minimum":0`), 398 []byte(`"maximum":0`), 399 }) 400 }) 401 }) 402 403 Context("with minItems and maxItems validations in payload's attribute", func() { 404 const ( 405 arrParam = "arrParam" 406 minVal = 0 407 maxVal = 42 408 ) 409 410 BeforeEach(func() { 411 PayloadWithValidations := Type("Payload", func() { 412 Attribute(arrParam, ArrayOf(String), func() { 413 MinLength(minVal) 414 MaxLength(maxVal) 415 }) 416 }) 417 Resource("res", func() { 418 Action("act", func() { 419 Routing( 420 PUT("/"), 421 ) 422 Payload(PayloadWithValidations) 423 }) 424 }) 425 }) 426 427 It("serializes into valid swagger JSON", func() { 428 validateSwaggerWithFragments(swagger, [][]byte{ 429 // payload 430 []byte(`"minItems":0`), 431 []byte(`"maxItems":42`), 432 }) 433 }) 434 }) 435 436 Context("with minItems and maxItems validations in payload", func() { 437 const ( 438 strParam = "strParam" 439 minVal = 0 440 maxVal = 42 441 ) 442 443 BeforeEach(func() { 444 PayloadWithValidations := Type("Payload", func() { 445 Attribute(strParam, String) 446 }) 447 Resource("res", func() { 448 Action("act", func() { 449 Routing( 450 PUT("/"), 451 ) 452 Payload(ArrayOf(PayloadWithValidations), func() { 453 MinLength(minVal) 454 MaxLength(maxVal) 455 }) 456 }) 457 }) 458 }) 459 460 It("serializes into valid swagger JSON", func() { 461 validateSwaggerWithFragments(swagger, [][]byte{ 462 // payload 463 []byte(`"minItems":0`), 464 []byte(`"maxItems":42`), 465 }) 466 }) 467 }) 468 469 Context("with response templates", func() { 470 const okName = "OK" 471 const okDesc = "OK description" 472 const notFoundName = "NotFound" 473 const notFoundDesc = "NotFound description" 474 const notFoundMt = "application/json" 475 const headerName = "headerName" 476 477 BeforeEach(func() { 478 account := MediaType("application/vnd.goa.test.account", func() { 479 Description("Account") 480 Attributes(func() { 481 Attribute("id", Integer) 482 Attribute("href", String) 483 }) 484 View("default", func() { 485 Attribute("id") 486 Attribute("href") 487 }) 488 View("link", func() { 489 Attribute("id") 490 Attribute("href") 491 }) 492 }) 493 mt := MediaType("application/vnd.goa.test.bottle", func() { 494 Description("A bottle of wine") 495 Attributes(func() { 496 Attribute("id", Integer, "ID of bottle") 497 Attribute("href", String, "API href of bottle") 498 Attribute("account", account, "Owner account") 499 Links(func() { 500 Link("account") // Defines a link to the Account media type 501 }) 502 Required("id", "href") 503 }) 504 View("default", func() { 505 Attribute("id") 506 Attribute("href") 507 Attribute("links") // Default view renders links 508 }) 509 View("extended", func() { 510 Attribute("id") 511 Attribute("href") 512 Attribute("account") // Extended view renders account inline 513 Attribute("links") // Extended view also renders links 514 }) 515 }) 516 base := Design.DSLFunc 517 Design.DSLFunc = func() { 518 base() 519 ResponseTemplate(okName, func() { 520 Description(okDesc) 521 Status(404) 522 Media(mt) 523 Headers(func() { 524 Header(headerName, func() { 525 Format("hostname") 526 }) 527 }) 528 }) 529 ResponseTemplate(notFoundName, func() { 530 Description(notFoundDesc) 531 Status(404) 532 533 Media(notFoundMt) 534 }) 535 } 536 }) 537 538 It("sets the Responses fields", func() { 539 Ω(newErr).ShouldNot(HaveOccurred()) 540 Ω(swagger.Responses).Should(HaveLen(2)) 541 Ω(swagger.Responses[notFoundName]).ShouldNot(BeNil()) 542 Ω(swagger.Responses[notFoundName].Description).Should(Equal(notFoundDesc)) 543 Ω(swagger.Responses[okName]).ShouldNot(BeNil()) 544 Ω(swagger.Responses[okName].Description).Should(Equal(okDesc)) 545 }) 546 547 It("serializes into valid swagger JSON", func() { validateSwagger(swagger) }) 548 }) 549 550 Context("with resources", func() { 551 var ( 552 minLength1 = 1 553 maxLength10 = 10 554 minimum_2 = -2.0 555 maximum2 = 2.0 556 minItems1 = 1 557 maxItems5 = 5 558 ) 559 BeforeEach(func() { 560 Country := MediaType("application/vnd.goa.example.origin", func() { 561 Description("Origin of bottle") 562 Attributes(func() { 563 Attribute("id") 564 Attribute("href") 565 Attribute("country") 566 }) 567 View("default", func() { 568 Attribute("id") 569 Attribute("href") 570 Attribute("country") 571 }) 572 View("tiny", func() { 573 Attribute("id") 574 }) 575 }) 576 BottleMedia := MediaType("application/vnd.goa.example.bottle", func() { 577 Description("A bottle of wine") 578 Attributes(func() { 579 Attribute("id", Integer, "ID of bottle") 580 Attribute("href", String, "API href of bottle") 581 Attribute("origin", Country, "Details on wine origin") 582 Links(func() { 583 Link("origin", "tiny") 584 }) 585 Required("id", "href") 586 }) 587 View("default", func() { 588 Attribute("id") 589 Attribute("href") 590 Attribute("links") 591 }) 592 View("extended", func() { 593 Attribute("id") 594 Attribute("href") 595 Attribute("origin") 596 Attribute("links") 597 }) 598 }) 599 UpdatePayload := Type("UpdatePayload", func() { 600 Description("Type of create and upload action payloads") 601 Attribute("name", String, "name of bottle") 602 Attribute("origin", Country, "Details on wine origin") 603 Required("name") 604 }) 605 Resource("res", func() { 606 Metadata("swagger:tag:res") 607 Description("A wine bottle") 608 DefaultMedia(BottleMedia) 609 BasePath("/bottles") 610 UseTrait("Authenticated") 611 612 Action("Update", func() { 613 Metadata("swagger:tag:Update") 614 Metadata("swagger:summary", "a summary") 615 Description("Update account") 616 Docs(func() { 617 Description("docs") 618 URL("http://cellarapi.com/docs/actions/update") 619 }) 620 Routing( 621 PUT("/:id"), 622 PUT("//orgs/:org/accounts/:id"), 623 ) 624 Params(func() { 625 Param("org", String) 626 Param("id", Integer) 627 Param("sort", func() { 628 Enum("asc", "desc") 629 }) 630 }) 631 Headers(func() { 632 Header("Authorization", String) 633 Header("X-Account", Integer) 634 Header("OptionalBoolWithDefault", Boolean, "defaults true", func() { 635 Default(true) 636 }) 637 Header("OptionalRegex", String, func() { 638 Pattern(`[a-z]\d+`) 639 MinLength(minLength1) 640 MaxLength(maxLength10) 641 }) 642 Header("OptionalInt", Integer, func() { 643 Minimum(minimum_2) 644 Maximum(maximum2) 645 }) 646 Header("OptionalArray", ArrayOf(String), func() { 647 // interpreted as MinItems & MaxItems: 648 MinLength(minItems1) 649 MaxLength(maxItems5) 650 }) 651 Header("OverrideRequiredHeader") 652 Header("OverrideOptionalHeader") 653 Required("Authorization", "X-Account", "OverrideOptionalHeader") 654 }) 655 Payload(UpdatePayload) 656 Response(OK, func() { 657 Media(CollectionOf(BottleMedia), "extended") 658 }) 659 Response(NoContent) 660 Response(NotFound, ErrorMedia) 661 Response(BadRequest, ErrorMedia) 662 }) 663 664 Action("hidden", func() { 665 Description("Does not show up in Swagger spec") 666 Metadata("swagger:generate", "false") 667 Routing(GET("/hidden")) 668 Response(OK) 669 }) 670 }) 671 base := Design.DSLFunc 672 Design.DSLFunc = func() { 673 base() 674 Trait("Authenticated", func() { 675 Headers(func() { 676 Header("header") 677 Header("OverrideRequiredHeader", String, "to be overridden in Action and not marked Required") 678 Header("OverrideOptionalHeader", String, "to be overridden in Action and marked Required") 679 Header("OptionalResourceHeaderWithEnum", func() { 680 Enum("a", "b") 681 }) 682 Required("header", "OverrideRequiredHeader") 683 }) 684 }) 685 } 686 }) 687 688 It("sets the Path fields", func() { 689 Ω(newErr).ShouldNot(HaveOccurred()) 690 Ω(swagger.Paths).Should(HaveLen(2)) 691 Ω(swagger.Paths["/orgs/{org}/accounts/{id}"]).ShouldNot(BeNil()) 692 a := swagger.Paths["/orgs/{org}/accounts/{id}"].(*genswagger.Path) 693 Ω(a.Put).ShouldNot(BeNil()) 694 ps := a.Put.Parameters 695 Ω(ps).Should(HaveLen(14)) 696 // check Headers in detail 697 Ω(ps[3]).Should(Equal(&genswagger.Parameter{In: "header", Name: "Authorization", Type: "string", Required: true})) 698 Ω(ps[4]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OptionalArray", Type: "array", CollectionFormat: "multi", 699 Items: &genswagger.Items{Type: "string"}, MinItems: &minItems1, MaxItems: &maxItems5})) 700 Ω(ps[5]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OptionalBoolWithDefault", Type: "boolean", 701 Description: "defaults true", Default: true})) 702 Ω(ps[6]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OptionalInt", Type: "integer", Minimum: &minimum_2, Maximum: &maximum2})) 703 Ω(ps[7]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OptionalRegex", Type: "string", 704 Pattern: `[a-z]\d+`, MinLength: &minLength1, MaxLength: &maxLength10})) 705 Ω(ps[8]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OptionalResourceHeaderWithEnum", Type: "string", 706 Enum: []interface{}{"a", "b"}})) 707 Ω(ps[9]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OverrideOptionalHeader", Type: "string", Required: true})) 708 Ω(ps[10]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OverrideRequiredHeader", Type: "string", Required: true})) 709 Ω(ps[11]).Should(Equal(&genswagger.Parameter{In: "header", Name: "X-Account", Type: "integer", Required: true})) 710 Ω(ps[12]).Should(Equal(&genswagger.Parameter{In: "header", Name: "header", Type: "string", Required: true})) 711 Ω(swagger.Paths["/base/bottles/{id}"]).ShouldNot(BeNil()) 712 b := swagger.Paths["/base/bottles/{id}"].(*genswagger.Path) 713 Ω(b.Put).ShouldNot(BeNil()) 714 Ω(b.Put.Parameters).Should(HaveLen(14)) 715 Ω(b.Put.Produces).Should(Equal([]string{"application/vnd.goa.error", "application/vnd.goa.example.bottle; type=collection"})) 716 }) 717 718 It("should set the inherited tag and the action tag", func() { 719 tags := []string{"res", "Update"} 720 a := swagger.Paths["/orgs/{org}/accounts/{id}"].(*genswagger.Path) 721 Ω(a.Put).ShouldNot(BeNil()) 722 Ω(a.Put.Tags).Should(Equal(tags)) 723 b := swagger.Paths["/base/bottles/{id}"].(*genswagger.Path) 724 Ω(b.Put.Tags).Should(Equal(tags)) 725 }) 726 727 It("sets the summary from the summary tag", func() { 728 a := swagger.Paths["/orgs/{org}/accounts/{id}"].(*genswagger.Path) 729 Ω(a.Put.Summary).Should(Equal("a summary")) 730 }) 731 732 It("generates the media type collection schema", func() { 733 Ω(swagger.Definitions).Should(HaveLen(7)) 734 Ω(swagger.Definitions).Should(HaveKey("GoaExampleBottleExtendedCollection")) 735 }) 736 737 It("serializes into valid swagger JSON", func() { validateSwagger(swagger) }) 738 }) 739 740 Context("with metadata", func() { 741 const gat = "gat" 742 const extension = `{"foo":"bar"}` 743 const stringExtension = "foo" 744 745 var ( 746 unmarshaled map[string]interface{} 747 _ = json.Unmarshal([]byte(extension), &unmarshaled) 748 ) 749 750 BeforeEach(func() { 751 Resource("res", func() { 752 Metadata("swagger:tag:res") 753 Metadata("struct:tag:json", "resource") 754 Metadata("swagger:extension:x-resource", extension) 755 Metadata("swagger:extension:x-string", stringExtension) 756 Action("act", func() { 757 Metadata("swagger:tag:Update") 758 Metadata("struct:tag:json", "action") 759 Metadata("swagger:extension:x-action", extension) 760 Security("password", func() { 761 Metadata("swagger:extension:x-security", extension) 762 }) 763 Routing( 764 PUT("/", func() { 765 Metadata("swagger:extension:x-put", extension) 766 }), 767 ) 768 Params(func() { 769 Param("param", func() { 770 Metadata("swagger:extension:x-param", extension) 771 }) 772 }) 773 Response(NoContent, func() { 774 Metadata("swagger:extension:x-response", extension) 775 }) 776 }) 777 }) 778 base := Design.DSLFunc 779 Design.DSLFunc = func() { 780 base() 781 Metadata("swagger:tag:" + gat) 782 Metadata("struct:tag:json", "api") 783 Metadata("swagger:extension:x-api", extension) 784 BasicAuthSecurity("password") 785 } 786 }) 787 788 It("should set the swagger object tags", func() { 789 Ω(swagger.Tags).Should(HaveLen(2)) 790 tags := []*genswagger.Tag{ 791 {Name: gat, Description: "", ExternalDocs: nil, Extensions: map[string]interface{}{"x-api": unmarshaled}}, 792 {Name: tag, Description: "Tag desc.", ExternalDocs: &genswagger.ExternalDocs{URL: "http://example.com/tag", Description: "Huge docs"}, Extensions: map[string]interface{}{"x-api": unmarshaled}}, 793 } 794 Ω(swagger.Tags).Should(Equal(tags)) 795 }) 796 797 It("should set the action tags", func() { 798 p := swagger.Paths["/"].(*genswagger.Path) 799 Ω(p.Put.Tags).Should(HaveLen(2)) 800 tags := []string{"res", "Update"} 801 Ω(p.Put.Tags).Should(Equal(tags)) 802 }) 803 804 It("should set the swagger extensions", func() { 805 Ω(swagger.Info.Extensions).Should(HaveLen(1)) 806 Ω(swagger.Info.Extensions["x-api"]).Should(Equal(unmarshaled)) 807 p := swagger.Paths["/"].(*genswagger.Path) 808 Ω(p.Extensions).Should(HaveLen(1)) 809 Ω(p.Extensions["x-action"]).Should(Equal(unmarshaled)) 810 Ω(p.Put.Extensions).Should(HaveLen(1)) 811 Ω(p.Put.Extensions["x-put"]).Should(Equal(unmarshaled)) 812 Ω(p.Put.Parameters[0].Extensions).Should(HaveLen(1)) 813 Ω(p.Put.Parameters[0].Extensions["x-param"]).Should(Equal(unmarshaled)) 814 Ω(p.Put.Responses["204"].Extensions).Should(HaveLen(1)) 815 Ω(p.Put.Responses["204"].Extensions["x-response"]).Should(Equal(unmarshaled)) 816 Ω(swagger.Paths["x-resource"]).ShouldNot(BeNil()) 817 rs := swagger.Paths["x-resource"].(map[string]interface{}) 818 Ω(rs).Should(Equal(unmarshaled)) 819 rs2 := swagger.Paths["x-string"].(string) 820 Ω(rs2).Should(Equal(stringExtension)) 821 Ω(swagger.SecurityDefinitions["password"].Extensions).Should(HaveLen(1)) 822 Ω(swagger.SecurityDefinitions["password"].Extensions["x-security"]).Should(Equal(unmarshaled)) 823 }) 824 825 }) 826 }) 827 })