github.com/zak-blake/goa@v1.4.1/design/apidsl/api_test.go (about)

     1  package apidsl_test
     2  
     3  import (
     4  	. "github.com/goadesign/goa/design"
     5  	. "github.com/goadesign/goa/design/apidsl"
     6  	"github.com/goadesign/goa/dslengine"
     7  	. "github.com/onsi/ginkgo"
     8  	. "github.com/onsi/gomega"
     9  )
    10  
    11  var _ = Describe("API", func() {
    12  	var name string
    13  	var dsl func()
    14  
    15  	BeforeEach(func() {
    16  		dslengine.Reset()
    17  		name = ""
    18  		dsl = nil
    19  	})
    20  
    21  	JustBeforeEach(func() {
    22  		API(name, dsl)
    23  		dslengine.Run()
    24  	})
    25  
    26  	Context("with no DSL", func() {
    27  		BeforeEach(func() {
    28  			name = "foo"
    29  		})
    30  
    31  		It("produces a valid API definition", func() {
    32  			Ω(Design.Validate()).ShouldNot(HaveOccurred())
    33  			Ω(Design.Name).Should(Equal(name))
    34  		})
    35  	})
    36  
    37  	Context("with an already defined API with the same name", func() {
    38  		BeforeEach(func() {
    39  			name = "foo"
    40  		})
    41  
    42  		It("produces an error", func() {
    43  			API(name, dsl)
    44  			Ω(dslengine.Errors).Should(HaveOccurred())
    45  		})
    46  	})
    47  
    48  	Context("with an already defined API with a different name", func() {
    49  		BeforeEach(func() {
    50  			name = "foo"
    51  		})
    52  
    53  		It("returns an error", func() {
    54  			API("news", dsl)
    55  			Ω(dslengine.Errors).Should(HaveOccurred())
    56  		})
    57  	})
    58  
    59  	Context("with valid DSL", func() {
    60  		JustBeforeEach(func() {
    61  			Ω(dslengine.Errors).ShouldNot(HaveOccurred())
    62  			Ω(Design.Validate()).ShouldNot(HaveOccurred())
    63  		})
    64  
    65  		Context("with a description", func() {
    66  			const description = "description"
    67  
    68  			BeforeEach(func() {
    69  				dsl = func() {
    70  					Description(description)
    71  				}
    72  			})
    73  
    74  			It("sets the API description", func() {
    75  				Ω(Design.Description).Should(Equal(description))
    76  			})
    77  		})
    78  
    79  		Context("with a title", func() {
    80  			const title = "title"
    81  
    82  			BeforeEach(func() {
    83  				dsl = func() {
    84  					Title(title)
    85  				}
    86  			})
    87  
    88  			It("sets the API title", func() {
    89  				Ω(Design.Title).Should(Equal(title))
    90  			})
    91  		})
    92  
    93  		Context("with a version", func() {
    94  			const version = "2.0"
    95  
    96  			BeforeEach(func() {
    97  				dsl = func() {
    98  					Version(version)
    99  				}
   100  			})
   101  
   102  			It("sets the API version", func() {
   103  				Ω(Design.Version).Should(Equal(version))
   104  			})
   105  		})
   106  
   107  		Context("with a terms of service", func() {
   108  			const terms = "terms"
   109  
   110  			BeforeEach(func() {
   111  				dsl = func() {
   112  					TermsOfService(terms)
   113  				}
   114  			})
   115  
   116  			It("sets the API terms of service", func() {
   117  				Ω(Design.TermsOfService).Should(Equal(terms))
   118  			})
   119  		})
   120  
   121  		Context("with contact information", func() {
   122  			const contactName = "contactName"
   123  			const contactEmail = "contactEmail"
   124  			const contactURL = "http://contactURL.com"
   125  
   126  			BeforeEach(func() {
   127  				dsl = func() {
   128  					Contact(func() {
   129  						Name(contactName)
   130  						Email(contactEmail)
   131  						URL(contactURL)
   132  					})
   133  				}
   134  			})
   135  
   136  			It("sets the contact information", func() {
   137  				Ω(Design.Contact).Should(Equal(&ContactDefinition{
   138  					Name:  contactName,
   139  					Email: contactEmail,
   140  					URL:   contactURL,
   141  				}))
   142  			})
   143  		})
   144  
   145  		Context("with license information", func() {
   146  			const licenseName = "licenseName"
   147  			const licenseURL = "http://licenseURL.com"
   148  
   149  			BeforeEach(func() {
   150  				dsl = func() {
   151  					License(func() {
   152  						Name(licenseName)
   153  						URL(licenseURL)
   154  					})
   155  				}
   156  			})
   157  
   158  			It("sets the API license information", func() {
   159  				Ω(Design.License).Should(Equal(&LicenseDefinition{
   160  					Name: licenseName,
   161  					URL:  licenseURL,
   162  				}))
   163  			})
   164  		})
   165  
   166  		Context("with Consumes", func() {
   167  			const consumesMT = "application/json"
   168  
   169  			BeforeEach(func() {
   170  				dsl = func() {
   171  					Consumes("application/json")
   172  				}
   173  			})
   174  
   175  			It("sets the API consumes", func() {
   176  				Ω(Design.Consumes).Should(HaveLen(1))
   177  				Ω(Design.Consumes[0].MIMETypes).Should(Equal([]string{consumesMT}))
   178  				Ω(Design.Consumes[0].PackagePath).Should(BeEmpty())
   179  			})
   180  
   181  			Context("using a custom encoding package", func() {
   182  				const pkgPath = "github.com/goadesign/goa/encoding/json"
   183  				const fn = "NewFoo"
   184  
   185  				BeforeEach(func() {
   186  					dsl = func() {
   187  						Consumes("application/json", func() {
   188  							Package(pkgPath)
   189  							Function(fn)
   190  						})
   191  					}
   192  				})
   193  
   194  				It("sets the API consumes", func() {
   195  					Ω(Design.Consumes).Should(HaveLen(1))
   196  					Ω(Design.Consumes[0].MIMETypes).Should(Equal([]string{consumesMT}))
   197  					Ω(Design.Consumes[0].PackagePath).Should(Equal(pkgPath))
   198  					Ω(Design.Consumes[0].Function).Should(Equal(fn))
   199  				})
   200  			})
   201  		})
   202  
   203  		Context("with a BasePath", func() {
   204  			const basePath = "basePath"
   205  
   206  			BeforeEach(func() {
   207  				dsl = func() {
   208  					BasePath(basePath)
   209  				}
   210  			})
   211  
   212  			It("sets the API base path", func() {
   213  				Ω(Design.BasePath).Should(Equal(basePath))
   214  			})
   215  		})
   216  
   217  		Context("with Params", func() {
   218  			const param1Name = "accountID"
   219  			const param1Type = Integer
   220  			const param1Desc = "the account ID"
   221  			const param2Name = "id"
   222  			const param2Type = String
   223  			const param2Desc = "the widget ID"
   224  
   225  			BeforeEach(func() {
   226  				dsl = func() {
   227  					Params(func() {
   228  						Param(param1Name, param1Type, param1Desc)
   229  						Param(param2Name, param2Type, param2Desc)
   230  					})
   231  				}
   232  			})
   233  
   234  			It("sets the API base parameters", func() {
   235  				Ω(Design.Params.Type).Should(BeAssignableToTypeOf(Object{}))
   236  				params := Design.Params.Type.ToObject()
   237  				Ω(params).Should(HaveLen(2))
   238  				Ω(params).Should(HaveKey(param1Name))
   239  				Ω(params).Should(HaveKey(param2Name))
   240  				Ω(params[param1Name].Type).Should(Equal(param1Type))
   241  				Ω(params[param2Name].Type).Should(Equal(param2Type))
   242  				Ω(params[param1Name].Description).Should(Equal(param1Desc))
   243  				Ω(params[param2Name].Description).Should(Equal(param2Desc))
   244  			})
   245  
   246  			Context("and a BasePath using them", func() {
   247  				const basePath = "/:accountID/:id"
   248  
   249  				BeforeEach(func() {
   250  					prevDSL := dsl
   251  					dsl = func() {
   252  						BasePath(basePath)
   253  						prevDSL()
   254  					}
   255  				})
   256  
   257  				It("sets both the base path and parameters", func() {
   258  					Ω(Design.Params.Type).Should(BeAssignableToTypeOf(Object{}))
   259  					params := Design.Params.Type.ToObject()
   260  					Ω(params).Should(HaveLen(2))
   261  					Ω(params).Should(HaveKey(param1Name))
   262  					Ω(params).Should(HaveKey(param2Name))
   263  					Ω(params[param1Name].Type).Should(Equal(param1Type))
   264  					Ω(params[param2Name].Type).Should(Equal(param2Type))
   265  					Ω(params[param1Name].Description).Should(Equal(param1Desc))
   266  					Ω(params[param2Name].Description).Should(Equal(param2Desc))
   267  					Ω(Design.BasePath).Should(Equal(basePath))
   268  				})
   269  
   270  				Context("with conflicting resource and API base params", func() {
   271  					JustBeforeEach(func() {
   272  						Resource("foo", func() {
   273  							BasePath("/:accountID")
   274  						})
   275  						dslengine.Run()
   276  					})
   277  
   278  					It("returns an error", func() {
   279  						Ω(dslengine.Errors).Should(HaveOccurred())
   280  					})
   281  				})
   282  
   283  				Context("with an absolute resource base path", func() {
   284  					JustBeforeEach(func() {
   285  						Resource("foo", func() {
   286  							Params(func() {
   287  								Param(param1Name, param1Type, param1Desc)
   288  							})
   289  							BasePath("//:accountID")
   290  						})
   291  						dslengine.Run()
   292  					})
   293  
   294  					It("does not return an error", func() {
   295  						Ω(dslengine.Errors).ShouldNot(HaveOccurred())
   296  					})
   297  				})
   298  			})
   299  		})
   300  
   301  		Context("with ResponseTemplates", func() {
   302  			const respName = "NotFound2"
   303  			const respDesc = "Resource Not Found"
   304  			const respStatus = 404
   305  			const respMediaType = "text/plain"
   306  			const respTName = "OK"
   307  			const respTDesc = "All good"
   308  			const respTStatus = 200
   309  
   310  			BeforeEach(func() {
   311  				dsl = func() {
   312  					ResponseTemplate(respName, func() {
   313  						Description(respDesc)
   314  						Status(respStatus)
   315  						Media(respMediaType)
   316  					})
   317  					ResponseTemplate(respTName, func(mt string) {
   318  						Description(respTDesc)
   319  						Status(respTStatus)
   320  						Media(mt)
   321  					})
   322  				}
   323  			})
   324  
   325  			It("sets the API responses and response templates", func() {
   326  				Ω(Design.Responses).Should(HaveKey(respName))
   327  				Ω(Design.Responses[respName]).ShouldNot(BeNil())
   328  				expected := ResponseDefinition{
   329  					Name:        respName,
   330  					Description: respDesc,
   331  					Status:      respStatus,
   332  					MediaType:   respMediaType,
   333  				}
   334  				actual := *Design.Responses[respName]
   335  				Ω(actual).Should(Equal(expected))
   336  
   337  				Ω(Design.ResponseTemplates).Should(HaveLen(1))
   338  				Ω(Design.ResponseTemplates).Should(HaveKey(respTName))
   339  				Ω(Design.ResponseTemplates[respTName]).ShouldNot(BeNil())
   340  			})
   341  		})
   342  
   343  		Context("with Traits", func() {
   344  			const traitName = "Authenticated"
   345  
   346  			BeforeEach(func() {
   347  				dsl = func() {
   348  					Trait(traitName, func() {
   349  						Headers(func() {
   350  							Header("Auth-Token")
   351  							Required("Auth-Token")
   352  						})
   353  					})
   354  				}
   355  			})
   356  
   357  			It("sets the API traits", func() {
   358  				Ω(Design.Traits).Should(HaveLen(1))
   359  				Ω(Design.Traits).Should(HaveKey(traitName))
   360  			})
   361  		})
   362  
   363  		Context("using Traits", func() {
   364  			const traitName = "Authenticated"
   365  
   366  			BeforeEach(func() {
   367  				dsl = func() {
   368  					Trait(traitName, func() {
   369  						Attributes(func() {
   370  							Attribute("foo")
   371  						})
   372  					})
   373  				}
   374  			})
   375  
   376  			JustBeforeEach(func() {
   377  				API(name, dsl)
   378  				MediaType("application/vnd.foo", func() {
   379  					UseTrait(traitName)
   380  					Attributes(func() {
   381  						Attribute("bar")
   382  					})
   383  					View("default", func() {
   384  						Attribute("bar")
   385  						Attribute("foo")
   386  					})
   387  				})
   388  				dslengine.Run()
   389  			})
   390  
   391  			It("sets the API traits", func() {
   392  				Ω(Design.Traits).Should(HaveLen(1))
   393  				Ω(Design.Traits).Should(HaveKey(traitName))
   394  				Ω(Design.MediaTypes).Should(HaveKey("application/vnd.foo"))
   395  				foo := Design.MediaTypes["application/vnd.foo"]
   396  				Ω(foo.Type.ToObject()).ShouldNot(BeNil())
   397  				o := foo.Type.ToObject()
   398  				Ω(o).Should(HaveKey("foo"))
   399  				Ω(o).Should(HaveKey("bar"))
   400  			})
   401  		})
   402  
   403  		Context("using variadic Traits", func() {
   404  			const traitName1 = "Authenticated"
   405  			const traitName2 = "AuthenticatedTwo"
   406  
   407  			BeforeEach(func() {
   408  				dsl = func() {
   409  					Trait(traitName1, func() {
   410  						Attributes(func() {
   411  							Attribute("foo")
   412  						})
   413  					})
   414  					Trait(traitName2, func() {
   415  						Attributes(func() {
   416  							Attribute("baz")
   417  						})
   418  					})
   419  				}
   420  			})
   421  
   422  			JustBeforeEach(func() {
   423  				API(name, dsl)
   424  				MediaType("application/vnd.foo", func() {
   425  					UseTrait(traitName1, traitName2)
   426  					Attributes(func() {
   427  						Attribute("bar")
   428  					})
   429  					View("default", func() {
   430  						Attribute("bar")
   431  						Attribute("foo")
   432  						Attribute("baz")
   433  					})
   434  				})
   435  				dslengine.Run()
   436  			})
   437  
   438  			It("sets the API traits", func() {
   439  				Ω(Design.Traits).Should(HaveLen(2))
   440  				Ω(Design.Traits).Should(HaveKey(traitName1))
   441  				Ω(Design.Traits).Should(HaveKey(traitName2))
   442  				Ω(Design.MediaTypes).Should(HaveKey("application/vnd.foo"))
   443  				foo := Design.MediaTypes["application/vnd.foo"]
   444  				Ω(foo.Type.ToObject()).ShouldNot(BeNil())
   445  				o := foo.Type.ToObject()
   446  				Ω(o).Should(HaveKey("foo"))
   447  				Ω(o).Should(HaveKey("bar"))
   448  				Ω(o).Should(HaveKey("baz"))
   449  			})
   450  		})
   451  	})
   452  
   453  })