github.com/brycereitano/goa@v0.0.0-20170315073847-8ffa6c85e265/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  	"github.com/goadesign/goa/goagen/gen_schema"
    13  	"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 zero value validations", func() {
   285  			const (
   286  				intParam = "intParam"
   287  				numParam = "numParam"
   288  				strParam = "strParam"
   289  				intMin   = 0.0
   290  				floatMax = 0.0
   291  			)
   292  
   293  			BeforeEach(func() {
   294  				PayloadWithZeroValueValidations := Type("PayloadWithZeroValueValidations", func() {
   295  					Attribute(strParam, String, func() {
   296  						MinLength(0)
   297  						MaxLength(0)
   298  					})
   299  				})
   300  				Resource("res", func() {
   301  					Action("act", func() {
   302  						Routing(
   303  							PUT("/"),
   304  						)
   305  						Params(func() {
   306  							Param(intParam, Integer, func() {
   307  								Minimum(intMin)
   308  							})
   309  							Param(numParam, Number, func() {
   310  								Maximum(floatMax)
   311  							})
   312  						})
   313  						Payload(PayloadWithZeroValueValidations)
   314  					})
   315  				})
   316  			})
   317  
   318  			It("serializes into valid swagger JSON", func() {
   319  				validateSwaggerWithFragments(swagger, [][]byte{
   320  					// payload
   321  					[]byte(`"minLength":0`),
   322  					[]byte(`"maxLength":0`),
   323  					// param
   324  					[]byte(`"minimum":0`),
   325  					[]byte(`"maximum":0`),
   326  				})
   327  			})
   328  		})
   329  
   330  		Context("with response templates", func() {
   331  			const okName = "OK"
   332  			const okDesc = "OK description"
   333  			const notFoundName = "NotFound"
   334  			const notFoundDesc = "NotFound description"
   335  			const notFoundMt = "application/json"
   336  			const headerName = "headerName"
   337  
   338  			BeforeEach(func() {
   339  				account := MediaType("application/vnd.goa.test.account", func() {
   340  					Description("Account")
   341  					Attributes(func() {
   342  						Attribute("id", Integer)
   343  						Attribute("href", String)
   344  					})
   345  					View("default", func() {
   346  						Attribute("id")
   347  						Attribute("href")
   348  					})
   349  					View("link", func() {
   350  						Attribute("id")
   351  						Attribute("href")
   352  					})
   353  				})
   354  				mt := MediaType("application/vnd.goa.test.bottle", func() {
   355  					Description("A bottle of wine")
   356  					Attributes(func() {
   357  						Attribute("id", Integer, "ID of bottle")
   358  						Attribute("href", String, "API href of bottle")
   359  						Attribute("account", account, "Owner account")
   360  						Links(func() {
   361  							Link("account") // Defines a link to the Account media type
   362  						})
   363  						Required("id", "href")
   364  					})
   365  					View("default", func() {
   366  						Attribute("id")
   367  						Attribute("href")
   368  						Attribute("links") // Default view renders links
   369  					})
   370  					View("extended", func() {
   371  						Attribute("id")
   372  						Attribute("href")
   373  						Attribute("account") // Extended view renders account inline
   374  						Attribute("links")   // Extended view also renders links
   375  					})
   376  				})
   377  				base := Design.DSLFunc
   378  				Design.DSLFunc = func() {
   379  					base()
   380  					ResponseTemplate(okName, func() {
   381  						Description(okDesc)
   382  						Status(404)
   383  						Media(mt)
   384  						Headers(func() {
   385  							Header(headerName, func() {
   386  								Format("hostname")
   387  							})
   388  						})
   389  					})
   390  					ResponseTemplate(notFoundName, func() {
   391  						Description(notFoundDesc)
   392  						Status(404)
   393  
   394  						Media(notFoundMt)
   395  					})
   396  				}
   397  			})
   398  
   399  			It("sets the Responses fields", func() {
   400  				Ω(newErr).ShouldNot(HaveOccurred())
   401  				Ω(swagger.Responses).Should(HaveLen(2))
   402  				Ω(swagger.Responses[notFoundName]).ShouldNot(BeNil())
   403  				Ω(swagger.Responses[notFoundName].Description).Should(Equal(notFoundDesc))
   404  				Ω(swagger.Responses[okName]).ShouldNot(BeNil())
   405  				Ω(swagger.Responses[okName].Description).Should(Equal(okDesc))
   406  			})
   407  
   408  			It("serializes into valid swagger JSON", func() { validateSwagger(swagger) })
   409  		})
   410  
   411  		Context("with resources", func() {
   412  			var (
   413  				minLength1  = 1
   414  				maxLength10 = 10
   415  				minimum_2   = -2.0
   416  				maximum2    = 2.0
   417  				minItems1   = 1
   418  				maxItems5   = 5
   419  			)
   420  			BeforeEach(func() {
   421  				Country := MediaType("application/vnd.goa.example.origin", func() {
   422  					Description("Origin of bottle")
   423  					Attributes(func() {
   424  						Attribute("id")
   425  						Attribute("href")
   426  						Attribute("country")
   427  					})
   428  					View("default", func() {
   429  						Attribute("id")
   430  						Attribute("href")
   431  						Attribute("country")
   432  					})
   433  					View("tiny", func() {
   434  						Attribute("id")
   435  					})
   436  				})
   437  				BottleMedia := MediaType("application/vnd.goa.example.bottle", func() {
   438  					Description("A bottle of wine")
   439  					Attributes(func() {
   440  						Attribute("id", Integer, "ID of bottle")
   441  						Attribute("href", String, "API href of bottle")
   442  						Attribute("origin", Country, "Details on wine origin")
   443  						Links(func() {
   444  							Link("origin", "tiny")
   445  						})
   446  						Required("id", "href")
   447  					})
   448  					View("default", func() {
   449  						Attribute("id")
   450  						Attribute("href")
   451  						Attribute("links")
   452  					})
   453  					View("extended", func() {
   454  						Attribute("id")
   455  						Attribute("href")
   456  						Attribute("origin")
   457  						Attribute("links")
   458  					})
   459  				})
   460  				UpdatePayload := Type("UpdatePayload", func() {
   461  					Description("Type of create and upload action payloads")
   462  					Attribute("name", String, "name of bottle")
   463  					Attribute("origin", Country, "Details on wine origin")
   464  					Required("name")
   465  				})
   466  				Resource("res", func() {
   467  					Metadata("swagger:tag:res")
   468  					Description("A wine bottle")
   469  					DefaultMedia(BottleMedia)
   470  					BasePath("/bottles")
   471  					UseTrait("Authenticated")
   472  
   473  					Action("Update", func() {
   474  						Metadata("swagger:tag:Update")
   475  						Metadata("swagger:summary", "a summary")
   476  						Description("Update account")
   477  						Docs(func() {
   478  							Description("docs")
   479  							URL("http://cellarapi.com/docs/actions/update")
   480  						})
   481  						Routing(
   482  							PUT("/:id"),
   483  							PUT("//orgs/:org/accounts/:id"),
   484  						)
   485  						Params(func() {
   486  							Param("org", String)
   487  							Param("id", Integer)
   488  							Param("sort", func() {
   489  								Enum("asc", "desc")
   490  							})
   491  						})
   492  						Headers(func() {
   493  							Header("Authorization", String)
   494  							Header("X-Account", Integer)
   495  							Header("OptionalBoolWithDefault", Boolean, "defaults true", func() {
   496  								Default(true)
   497  							})
   498  							Header("OptionalRegex", String, func() {
   499  								Pattern(`[a-z]\d+`)
   500  								MinLength(minLength1)
   501  								MaxLength(maxLength10)
   502  							})
   503  							Header("OptionalInt", Integer, func() {
   504  								Minimum(minimum_2)
   505  								Maximum(maximum2)
   506  							})
   507  							Header("OptionalArray", ArrayOf(String), func() {
   508  								// interpreted as MinItems & MaxItems:
   509  								MinLength(minItems1)
   510  								MaxLength(maxItems5)
   511  							})
   512  							Header("OverrideRequiredHeader")
   513  							Header("OverrideOptionalHeader")
   514  							Required("Authorization", "X-Account", "OverrideOptionalHeader")
   515  						})
   516  						Payload(UpdatePayload)
   517  						Response(OK, func() {
   518  							Media(CollectionOf(BottleMedia), "extended")
   519  						})
   520  						Response(NoContent)
   521  						Response(NotFound)
   522  					})
   523  
   524  					Action("hidden", func() {
   525  						Description("Does not show up in Swagger spec")
   526  						Metadata("swagger:generate", "false")
   527  						Routing(GET("/hidden"))
   528  						Response(OK)
   529  					})
   530  				})
   531  				base := Design.DSLFunc
   532  				Design.DSLFunc = func() {
   533  					base()
   534  					Trait("Authenticated", func() {
   535  						Headers(func() {
   536  							Header("header")
   537  							Header("OverrideRequiredHeader", String, "to be overridden in Action and not marked Required")
   538  							Header("OverrideOptionalHeader", String, "to be overridden in Action and marked Required")
   539  							Header("OptionalResourceHeaderWithEnum", func() {
   540  								Enum("a", "b")
   541  							})
   542  							Required("header", "OverrideRequiredHeader")
   543  						})
   544  					})
   545  				}
   546  			})
   547  
   548  			It("sets the Path fields", func() {
   549  				Ω(newErr).ShouldNot(HaveOccurred())
   550  				Ω(swagger.Paths).Should(HaveLen(2))
   551  				Ω(swagger.Paths["/orgs/{org}/accounts/{id}"]).ShouldNot(BeNil())
   552  				a := swagger.Paths["/orgs/{org}/accounts/{id}"].(*genswagger.Path)
   553  				Ω(a.Put).ShouldNot(BeNil())
   554  				ps := a.Put.Parameters
   555  				Ω(ps).Should(HaveLen(14))
   556  				// check Headers in detail
   557  				Ω(ps[3]).Should(Equal(&genswagger.Parameter{In: "header", Name: "Authorization", Type: "string", Required: true}))
   558  				Ω(ps[4]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OptionalArray", Type: "array", CollectionFormat: "multi",
   559  					Items: &genswagger.Items{Type: "string"}, MinItems: &minItems1, MaxItems: &maxItems5}))
   560  				Ω(ps[5]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OptionalBoolWithDefault", Type: "boolean",
   561  					Description: "defaults true", Default: true}))
   562  				Ω(ps[6]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OptionalInt", Type: "integer", Minimum: &minimum_2, Maximum: &maximum2}))
   563  				Ω(ps[7]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OptionalRegex", Type: "string",
   564  					Pattern: `[a-z]\d+`, MinLength: &minLength1, MaxLength: &maxLength10}))
   565  				Ω(ps[8]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OptionalResourceHeaderWithEnum", Type: "string",
   566  					Enum: []interface{}{"a", "b"}}))
   567  				Ω(ps[9]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OverrideOptionalHeader", Type: "string", Required: true}))
   568  				Ω(ps[10]).Should(Equal(&genswagger.Parameter{In: "header", Name: "OverrideRequiredHeader", Type: "string", Required: true}))
   569  				Ω(ps[11]).Should(Equal(&genswagger.Parameter{In: "header", Name: "X-Account", Type: "integer", Required: true}))
   570  				Ω(ps[12]).Should(Equal(&genswagger.Parameter{In: "header", Name: "header", Type: "string", Required: true}))
   571  				Ω(swagger.Paths["/base/bottles/{id}"]).ShouldNot(BeNil())
   572  				b := swagger.Paths["/base/bottles/{id}"].(*genswagger.Path)
   573  				Ω(b.Put).ShouldNot(BeNil())
   574  				Ω(b.Put.Parameters).Should(HaveLen(14))
   575  				Ω(b.Put.Produces).Should(Equal([]string{"application/vnd.goa.example.bottle; type=collection"}))
   576  			})
   577  
   578  			It("should set the inherited tag and the action tag", func() {
   579  				tags := []string{"res", "Update"}
   580  				a := swagger.Paths["/orgs/{org}/accounts/{id}"].(*genswagger.Path)
   581  				Ω(a.Put).ShouldNot(BeNil())
   582  				Ω(a.Put.Tags).Should(Equal(tags))
   583  				b := swagger.Paths["/base/bottles/{id}"].(*genswagger.Path)
   584  				Ω(b.Put.Tags).Should(Equal(tags))
   585  			})
   586  
   587  			It("sets the summary from the summary tag", func() {
   588  				a := swagger.Paths["/orgs/{org}/accounts/{id}"].(*genswagger.Path)
   589  				Ω(a.Put.Summary).Should(Equal("a summary"))
   590  			})
   591  
   592  			It("generates the media type collection schema", func() {
   593  				Ω(swagger.Definitions).Should(HaveLen(6))
   594  				Ω(swagger.Definitions).Should(HaveKey("GoaExampleBottleExtendedCollection"))
   595  			})
   596  
   597  			It("serializes into valid swagger JSON", func() { validateSwagger(swagger) })
   598  		})
   599  
   600  		Context("with metadata", func() {
   601  			const gat = "gat"
   602  			const extension = `{"foo":"bar"}`
   603  			const stringExtension = "foo"
   604  
   605  			var (
   606  				unmarshaled map[string]interface{}
   607  				_           = json.Unmarshal([]byte(extension), &unmarshaled)
   608  			)
   609  
   610  			BeforeEach(func() {
   611  				Resource("res", func() {
   612  					Metadata("swagger:tag:res")
   613  					Metadata("struct:tag:json", "resource")
   614  					Metadata("swagger:extension:x-resource", extension)
   615  					Metadata("swagger:extension:x-string", stringExtension)
   616  					Action("act", func() {
   617  						Metadata("swagger:tag:Update")
   618  						Metadata("struct:tag:json", "action")
   619  						Metadata("swagger:extension:x-action", extension)
   620  						Security("password", func() {
   621  							Metadata("swagger:extension:x-security", extension)
   622  						})
   623  						Routing(
   624  							PUT("/", func() {
   625  								Metadata("swagger:extension:x-put", extension)
   626  							}),
   627  						)
   628  						Params(func() {
   629  							Param("param", func() {
   630  								Metadata("swagger:extension:x-param", extension)
   631  							})
   632  						})
   633  						Response(NoContent, func() {
   634  							Metadata("swagger:extension:x-response", extension)
   635  						})
   636  					})
   637  				})
   638  				base := Design.DSLFunc
   639  				Design.DSLFunc = func() {
   640  					base()
   641  					Metadata("swagger:tag:" + gat)
   642  					Metadata("struct:tag:json", "api")
   643  					Metadata("swagger:extension:x-api", extension)
   644  					BasicAuthSecurity("password")
   645  				}
   646  			})
   647  
   648  			It("should set the swagger object tags", func() {
   649  				Ω(swagger.Tags).Should(HaveLen(2))
   650  				tags := []*genswagger.Tag{
   651  					{Name: gat, Description: "", ExternalDocs: nil, Extensions: map[string]interface{}{"x-api": unmarshaled}},
   652  					{Name: tag, Description: "Tag desc.", ExternalDocs: &genswagger.ExternalDocs{URL: "http://example.com/tag", Description: "Huge docs"}, Extensions: map[string]interface{}{"x-api": unmarshaled}},
   653  				}
   654  				Ω(swagger.Tags).Should(Equal(tags))
   655  			})
   656  
   657  			It("should set the action tags", func() {
   658  				p := swagger.Paths[""].(*genswagger.Path)
   659  				Ω(p.Put.Tags).Should(HaveLen(2))
   660  				tags := []string{"res", "Update"}
   661  				Ω(p.Put.Tags).Should(Equal(tags))
   662  			})
   663  
   664  			It("should set the swagger extensions", func() {
   665  				Ω(swagger.Info.Extensions).Should(HaveLen(1))
   666  				Ω(swagger.Info.Extensions["x-api"]).Should(Equal(unmarshaled))
   667  				p := swagger.Paths[""].(*genswagger.Path)
   668  				Ω(p.Extensions).Should(HaveLen(1))
   669  				Ω(p.Extensions["x-action"]).Should(Equal(unmarshaled))
   670  				Ω(p.Put.Extensions).Should(HaveLen(1))
   671  				Ω(p.Put.Extensions["x-put"]).Should(Equal(unmarshaled))
   672  				Ω(p.Put.Parameters[0].Extensions).Should(HaveLen(1))
   673  				Ω(p.Put.Parameters[0].Extensions["x-param"]).Should(Equal(unmarshaled))
   674  				Ω(p.Put.Responses["204"].Extensions).Should(HaveLen(1))
   675  				Ω(p.Put.Responses["204"].Extensions["x-response"]).Should(Equal(unmarshaled))
   676  				Ω(swagger.Paths["x-resource"]).ShouldNot(BeNil())
   677  				rs := swagger.Paths["x-resource"].(map[string]interface{})
   678  				Ω(rs).Should(Equal(unmarshaled))
   679  				rs2 := swagger.Paths["x-string"].(string)
   680  				Ω(rs2).Should(Equal(stringExtension))
   681  				Ω(swagger.SecurityDefinitions["password"].Extensions).Should(HaveLen(1))
   682  				Ω(swagger.SecurityDefinitions["password"].Extensions["x-security"]).Should(Equal(unmarshaled))
   683  			})
   684  
   685  		})
   686  	})
   687  })