github.com/shogo82148/goa-v1@v1.6.2/design/apidsl/action_test.go (about) 1 package apidsl_test 2 3 import ( 4 "strconv" 5 6 . "github.com/onsi/ginkgo" 7 . "github.com/onsi/gomega" 8 "github.com/shogo82148/goa-v1/design" 9 "github.com/shogo82148/goa-v1/design/apidsl" 10 "github.com/shogo82148/goa-v1/dslengine" 11 ) 12 13 var _ = Describe("Action", func() { 14 var name string 15 var dsl func() 16 var action *design.ActionDefinition 17 18 BeforeEach(func() { 19 dslengine.Reset() 20 name = "" 21 dsl = nil 22 }) 23 24 JustBeforeEach(func() { 25 apidsl.Resource("res", func() { 26 apidsl.Action(name, dsl) 27 }) 28 dslengine.Run() 29 if r, ok := design.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 = apidsl.GET("/:id") 46 47 BeforeEach(func() { 48 name = "foo" 49 dsl = func() { apidsl.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(); apidsl.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() { apidsl.Metadata("swagger:extension:x-get", `{"foo":"bar"}`) } 77 route = apidsl.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 apidsl.Routing(apidsl.GET("/:id")) 102 apidsl.Payload(design.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(design.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 apidsl.Type(typeName, func() { 122 apidsl.Attribute("name") 123 }) 124 name = "foo" 125 dsl = func() { 126 apidsl.Description(description) 127 apidsl.Routing(apidsl.GET("/:id")) 128 apidsl.Headers(func() { apidsl.Header(headerName) }) 129 apidsl.Payload(typeName) 130 apidsl.Response(design.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(design.Object{})) 146 Ω(action.Headers.Type.(design.Object)).Should(HaveLen(1)) 147 Ω(action.Headers.Type.(design.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 apidsl.Type(typeName, func() { 158 apidsl.Attribute("name") 159 }) 160 name = "foo" 161 dsl = func() { 162 apidsl.Routing(apidsl.GET("/:id")) 163 apidsl.Headers(func() { 164 apidsl.Header(headerName) 165 apidsl.Required(headerName) 166 }) 167 apidsl.Headers(func() { 168 apidsl.Header(headerName2) 169 apidsl.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.(design.Object)).Should(HaveKey(headerName)) 181 Ω(action.Headers.Type).Should(BeAssignableToTypeOf(design.Object{})) 182 Ω(action.Headers.Type.(design.Object)).Should(HaveLen(2)) 183 Ω(action.Headers.Type.(design.Object)).Should(HaveKey(headerName)) 184 Ω(action.Headers.Type.(design.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 apidsl.MediaType(mtID, func() { 195 apidsl.Attributes(func() { apidsl.Attribute("foo") }) 196 apidsl.View("default", func() { apidsl.Attribute("foo") }) 197 }) 198 name = "foo" 199 dsl = func() { 200 apidsl.Routing(apidsl.GET("/:id")) 201 apidsl.Response(design.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 apidsl.API("test", func() { 226 apidsl.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 apidsl.Status(st) 233 }) 234 }) 235 }) 236 237 Context("called correctly", func() { 238 BeforeEach(func() { 239 dsl = func() { 240 apidsl.Routing(apidsl.GET("/:id")) 241 apidsl.Response(tmplName, strconv.Itoa(respStatus), respName, func() { 242 apidsl.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 apidsl.Routing(apidsl.GET("/id")) 264 apidsl.Response(tmplName, "not an integer", respName, func() { 265 apidsl.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 apidsl.Resource("foo", func() { 283 apidsl.Action("bar", func() { 284 apidsl.Routing(apidsl.GET("")) 285 apidsl.Payload(func() { 286 apidsl.Member("name") 287 apidsl.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.Design).ShouldNot(BeNil()) 300 Ω(design.Design.Resources).Should(HaveKey("foo")) 301 Ω(design.Design.Resources["foo"].Actions).Should(HaveKey("bar")) 302 Ω(design.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 apidsl.Resource("foo", func() { 311 apidsl.Action("bar", func() { 312 apidsl.Routing(apidsl.GET("")) 313 apidsl.Payload(apidsl.ArrayOf(design.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.Design).ShouldNot(BeNil()) 325 Ω(design.Design.Resources).Should(HaveKey("foo")) 326 Ω(design.Design.Resources["foo"].Actions).Should(HaveKey("bar")) 327 Ω(design.Design.Resources["foo"].Actions["bar"].Payload).ShouldNot(BeNil()) 328 Ω(design.Design.Resources["foo"].Actions["bar"].Payload.Type).ShouldNot(BeNil()) 329 Ω(design.Design.Resources["foo"].Actions["bar"].Payload.Type.IsArray()).Should(BeTrue()) 330 Ω(design.Design.Resources["foo"].Actions["bar"].Payload.Type.ToArray().ElemType).ShouldNot(BeNil()) 331 Ω(design.Design.Resources["foo"].Actions["bar"].Payload.Type.ToArray().ElemType.Type).Should(Equal(design.Integer)) 332 }) 333 }) 334 335 Context("with a hash", func() { 336 BeforeEach(func() { 337 dslengine.Reset() 338 339 apidsl.Resource("foo", func() { 340 apidsl.Action("bar", func() { 341 apidsl.Routing(apidsl.GET("")) 342 apidsl.Payload(apidsl.HashOf(design.String, design.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.Design).ShouldNot(BeNil()) 354 Ω(design.Design.Resources).Should(HaveKey("foo")) 355 Ω(design.Design.Resources["foo"].Actions).Should(HaveKey("bar")) 356 Ω(design.Design.Resources["foo"].Actions["bar"].Payload).ShouldNot(BeNil()) 357 Ω(design.Design.Resources["foo"].Actions["bar"].Payload.Type).ShouldNot(BeNil()) 358 Ω(design.Design.Resources["foo"].Actions["bar"].Payload.Type.IsHash()).Should(BeTrue()) 359 Ω(design.Design.Resources["foo"].Actions["bar"].Payload.Type.ToHash().ElemType).ShouldNot(BeNil()) 360 Ω(design.Design.Resources["foo"].Actions["bar"].Payload.Type.ToHash().KeyType).ShouldNot(BeNil()) 361 Ω(design.Design.Resources["foo"].Actions["bar"].Payload.Type.ToHash().ElemType.Type).Should(Equal(design.Integer)) 362 Ω(design.Design.Resources["foo"].Actions["bar"].Payload.Type.ToHash().KeyType.Type).Should(Equal(design.String)) 363 }) 364 }) 365 366 })