github.com/ccrossley/goa@v1.3.1/design/apidsl/action_test.go (about) 1 package apidsl_test 2 3 import ( 4 "strconv" 5 6 . "github.com/goadesign/goa/design" 7 . "github.com/goadesign/goa/design/apidsl" 8 "github.com/goadesign/goa/dslengine" 9 . "github.com/onsi/ginkgo" 10 . "github.com/onsi/gomega" 11 ) 12 13 var _ = Describe("Action", func() { 14 var name string 15 var dsl func() 16 var action *ActionDefinition 17 18 BeforeEach(func() { 19 dslengine.Reset() 20 name = "" 21 dsl = nil 22 }) 23 24 JustBeforeEach(func() { 25 Resource("res", func() { 26 Action(name, dsl) 27 }) 28 dslengine.Run() 29 if r, ok := Design.Resources["res"]; ok { 30 action = r.Actions[name] 31 } 32 }) 33 34 Context("with only a name and a route", func() { 35 BeforeEach(func() { 36 name = "foo" 37 }) 38 39 It("produces an invalid action", func() { 40 Ω(dslengine.Errors).Should(HaveOccurred()) 41 }) 42 }) 43 44 Context("with a name and DSL defining a route", func() { 45 var route = GET("/:id") 46 47 BeforeEach(func() { 48 name = "foo" 49 dsl = func() { Routing(route) } 50 }) 51 52 It("produces a valid action definition with the route and default status of 200 set", func() { 53 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 54 Ω(action).ShouldNot(BeNil()) 55 Ω(action.Name).Should(Equal(name)) 56 Ω(action.Validate()).ShouldNot(HaveOccurred()) 57 Ω(action.Routes).ShouldNot(BeNil()) 58 Ω(action.Routes).Should(HaveLen(1)) 59 Ω(action.Routes[0]).Should(Equal(route)) 60 }) 61 62 Context("with an empty params DSL", func() { 63 BeforeEach(func() { 64 olddsl := dsl 65 dsl = func() { olddsl(); Params(func() {}) } 66 name = "foo" 67 }) 68 69 It("produces a valid action", func() { 70 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 71 }) 72 }) 73 74 Context("with a metadata", func() { 75 BeforeEach(func() { 76 metadatadsl := func() { Metadata("swagger:extension:x-get", `{"foo":"bar"}`) } 77 route = GET("/:id", metadatadsl) 78 name = "foo" 79 }) 80 81 It("produces a valid action definition with the route with the metadata", func() { 82 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 83 Ω(action).ShouldNot(BeNil()) 84 Ω(action.Name).Should(Equal(name)) 85 Ω(action.Validate()).ShouldNot(HaveOccurred()) 86 Ω(action.Routes).ShouldNot(BeNil()) 87 Ω(action.Routes).Should(HaveLen(1)) 88 Ω(action.Routes[0]).Should(Equal(route)) 89 Ω(action.Routes[0].Metadata).ShouldNot(BeNil()) 90 Ω(action.Routes[0].Metadata).Should(Equal( 91 dslengine.MetadataDefinition{"swagger:extension:x-get": []string{`{"foo":"bar"}`}}, 92 )) 93 }) 94 }) 95 }) 96 97 Context("with a string payload", func() { 98 BeforeEach(func() { 99 name = "foo" 100 dsl = func() { 101 Routing(GET("/:id")) 102 Payload(String) 103 } 104 }) 105 106 It("produces a valid action with the given properties", func() { 107 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 108 Ω(action).ShouldNot(BeNil()) 109 Ω(action.Validate()).ShouldNot(HaveOccurred()) 110 Ω(action.Payload).ShouldNot(BeNil()) 111 Ω(action.Payload.Type).Should(Equal(String)) 112 }) 113 }) 114 115 Context("with a name and DSL defining a description, route, headers, payload and responses", func() { 116 const typeName = "typeName" 117 const description = "description" 118 const headerName = "Foo" 119 120 BeforeEach(func() { 121 Type(typeName, func() { 122 Attribute("name") 123 }) 124 name = "foo" 125 dsl = func() { 126 Description(description) 127 Routing(GET("/:id")) 128 Headers(func() { Header(headerName) }) 129 Payload(typeName) 130 Response(NoContent) 131 } 132 }) 133 134 It("produces a valid action with the given properties", func() { 135 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 136 Ω(action).ShouldNot(BeNil()) 137 Ω(action.Validate()).ShouldNot(HaveOccurred()) 138 Ω(action.Name).Should(Equal(name)) 139 Ω(action.Description).Should(Equal(description)) 140 Ω(action.Routes).Should(HaveLen(1)) 141 Ω(action.Responses).ShouldNot(BeNil()) 142 Ω(action.Responses).Should(HaveLen(1)) 143 Ω(action.Responses).Should(HaveKey("NoContent")) 144 Ω(action.Headers).ShouldNot(BeNil()) 145 Ω(action.Headers.Type).Should(BeAssignableToTypeOf(Object{})) 146 Ω(action.Headers.Type.(Object)).Should(HaveLen(1)) 147 Ω(action.Headers.Type.(Object)).Should(HaveKey(headerName)) 148 }) 149 }) 150 151 Context("with multiple headers sections", func() { 152 const typeName = "typeName" 153 const headerName = "Foo" 154 const headerName2 = "Foo2" 155 156 BeforeEach(func() { 157 Type(typeName, func() { 158 Attribute("name") 159 }) 160 name = "foo" 161 dsl = func() { 162 Routing(GET("/:id")) 163 Headers(func() { 164 Header(headerName) 165 Required(headerName) 166 }) 167 Headers(func() { 168 Header(headerName2) 169 Required(headerName2) 170 }) 171 } 172 }) 173 174 It("produces a valid action with all required headers accounted for", func() { 175 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 176 Ω(action).ShouldNot(BeNil()) 177 Ω(action.Validate()).ShouldNot(HaveOccurred()) 178 Ω(action.Name).Should(Equal(name)) 179 Ω(action.Headers).ShouldNot(BeNil()) 180 Ω(action.Headers.Type.(Object)).Should(HaveKey(headerName)) 181 Ω(action.Headers.Type).Should(BeAssignableToTypeOf(Object{})) 182 Ω(action.Headers.Type.(Object)).Should(HaveLen(2)) 183 Ω(action.Headers.Type.(Object)).Should(HaveKey(headerName)) 184 Ω(action.Headers.Type.(Object)).Should(HaveKey(headerName2)) 185 Ω(action.Headers.Validation).ShouldNot(BeNil()) 186 Ω(action.Headers.Validation.Required).Should(Equal([]string{headerName, headerName2})) 187 }) 188 }) 189 190 Context("using a response with a media type modifier", func() { 191 const mtID = "application/vnd.app.foo+json" 192 193 BeforeEach(func() { 194 MediaType(mtID, func() { 195 Attributes(func() { Attribute("foo") }) 196 View("default", func() { Attribute("foo") }) 197 }) 198 name = "foo" 199 dsl = func() { 200 Routing(GET("/:id")) 201 Response(OK, mtID) 202 } 203 }) 204 205 It("produces a response that keeps the modifier", func() { 206 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 207 Ω(action).ShouldNot(BeNil()) 208 Ω(action.Validate()).ShouldNot(HaveOccurred()) 209 Ω(action.Responses).ShouldNot(BeNil()) 210 Ω(action.Responses).Should(HaveLen(1)) 211 Ω(action.Responses).Should(HaveKey("OK")) 212 resp := action.Responses["OK"] 213 Ω(resp.MediaType).Should(Equal(mtID)) 214 }) 215 }) 216 217 Context("using a response template", func() { 218 const tmplName = "tmpl" 219 const respMediaType = "media" 220 const respStatus = 200 221 const respName = "respName" 222 223 BeforeEach(func() { 224 name = "foo" 225 API("test", func() { 226 ResponseTemplate(tmplName, func(status, name string) { 227 st, err := strconv.Atoi(status) 228 if err != nil { 229 dslengine.ReportError(err.Error()) 230 return 231 } 232 Status(st) 233 }) 234 }) 235 }) 236 237 Context("called correctly", func() { 238 BeforeEach(func() { 239 dsl = func() { 240 Routing(GET("/:id")) 241 Response(tmplName, strconv.Itoa(respStatus), respName, func() { 242 Media(respMediaType) 243 }) 244 } 245 }) 246 247 It("defines the response definition using the template", func() { 248 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 249 Ω(action).ShouldNot(BeNil()) 250 Ω(action.Responses).ShouldNot(BeNil()) 251 Ω(action.Responses).Should(HaveLen(1)) 252 Ω(action.Responses).Should(HaveKey(tmplName)) 253 resp := action.Responses[tmplName] 254 Ω(resp.Name).Should(Equal(tmplName)) 255 Ω(resp.Status).Should(Equal(respStatus)) 256 Ω(resp.MediaType).Should(Equal(respMediaType)) 257 }) 258 }) 259 260 Context("called incorrectly", func() { 261 BeforeEach(func() { 262 dsl = func() { 263 Routing(GET("/id")) 264 Response(tmplName, "not an integer", respName, func() { 265 Media(respMediaType) 266 }) 267 } 268 }) 269 270 It("fails", func() { 271 Ω(dslengine.Errors).Should(HaveOccurred()) 272 }) 273 }) 274 }) 275 }) 276 277 var _ = Describe("Payload", func() { 278 Context("with a payload definition", func() { 279 BeforeEach(func() { 280 dslengine.Reset() 281 282 Resource("foo", func() { 283 Action("bar", func() { 284 Routing(GET("")) 285 Payload(func() { 286 Member("name") 287 Required("name") 288 }) 289 }) 290 }) 291 }) 292 293 JustBeforeEach(func() { 294 dslengine.Run() 295 }) 296 297 It("generates the payload type", func() { 298 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 299 Ω(Design).ShouldNot(BeNil()) 300 Ω(Design.Resources).Should(HaveKey("foo")) 301 Ω(Design.Resources["foo"].Actions).Should(HaveKey("bar")) 302 Ω(Design.Resources["foo"].Actions["bar"].Payload).ShouldNot(BeNil()) 303 }) 304 }) 305 306 Context("with an array", func() { 307 BeforeEach(func() { 308 dslengine.Reset() 309 310 Resource("foo", func() { 311 Action("bar", func() { 312 Routing(GET("")) 313 Payload(ArrayOf(Integer)) 314 }) 315 }) 316 }) 317 318 JustBeforeEach(func() { 319 dslengine.Run() 320 }) 321 322 It("sets the payload type", func() { 323 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 324 Ω(Design).ShouldNot(BeNil()) 325 Ω(Design.Resources).Should(HaveKey("foo")) 326 Ω(Design.Resources["foo"].Actions).Should(HaveKey("bar")) 327 Ω(Design.Resources["foo"].Actions["bar"].Payload).ShouldNot(BeNil()) 328 Ω(Design.Resources["foo"].Actions["bar"].Payload.Type).ShouldNot(BeNil()) 329 Ω(Design.Resources["foo"].Actions["bar"].Payload.Type.IsArray()).Should(BeTrue()) 330 Ω(Design.Resources["foo"].Actions["bar"].Payload.Type.ToArray().ElemType).ShouldNot(BeNil()) 331 Ω(Design.Resources["foo"].Actions["bar"].Payload.Type.ToArray().ElemType.Type).Should(Equal(Integer)) 332 }) 333 }) 334 335 Context("with a hash", func() { 336 BeforeEach(func() { 337 dslengine.Reset() 338 339 Resource("foo", func() { 340 Action("bar", func() { 341 Routing(GET("")) 342 Payload(HashOf(String, Integer)) 343 }) 344 }) 345 }) 346 347 JustBeforeEach(func() { 348 dslengine.Run() 349 }) 350 351 It("sets the payload type", func() { 352 Ω(dslengine.Errors).ShouldNot(HaveOccurred()) 353 Ω(Design).ShouldNot(BeNil()) 354 Ω(Design.Resources).Should(HaveKey("foo")) 355 Ω(Design.Resources["foo"].Actions).Should(HaveKey("bar")) 356 Ω(Design.Resources["foo"].Actions["bar"].Payload).ShouldNot(BeNil()) 357 Ω(Design.Resources["foo"].Actions["bar"].Payload.Type).ShouldNot(BeNil()) 358 Ω(Design.Resources["foo"].Actions["bar"].Payload.Type.IsHash()).Should(BeTrue()) 359 Ω(Design.Resources["foo"].Actions["bar"].Payload.Type.ToHash().ElemType).ShouldNot(BeNil()) 360 Ω(Design.Resources["foo"].Actions["bar"].Payload.Type.ToHash().KeyType).ShouldNot(BeNil()) 361 Ω(Design.Resources["foo"].Actions["bar"].Payload.Type.ToHash().ElemType.Type).Should(Equal(Integer)) 362 Ω(Design.Resources["foo"].Actions["bar"].Payload.Type.ToHash().KeyType.Type).Should(Equal(String)) 363 }) 364 }) 365 366 })