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