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