github.com/brycereitano/goa@v0.0.0-20170315073847-8ffa6c85e265/error_test.go (about)

     1  package goa
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  
     8  	. "github.com/onsi/ginkgo"
     9  	. "github.com/onsi/gomega"
    10  )
    11  
    12  var _ = Describe("Error", func() {
    13  	const (
    14  		id     = "foo"
    15  		code   = "invalid"
    16  		status = 400
    17  		detail = "error"
    18  	)
    19  	var meta = map[string]interface{}{"what": 42}
    20  
    21  	var gerr *ErrorResponse
    22  
    23  	BeforeEach(func() {
    24  		gerr = &ErrorResponse{ID: id, Code: code, Status: status, Detail: detail, Meta: meta}
    25  	})
    26  
    27  	It("serializes to JSON", func() {
    28  		b, err := json.Marshal(gerr)
    29  		Ω(err).ShouldNot(HaveOccurred())
    30  		Ω(string(b)).Should(Equal(`{"id":"foo","code":"invalid","status":400,"detail":"error","meta":{"what":42}}`))
    31  	})
    32  })
    33  
    34  var _ = Describe("InvalidParamTypeError", func() {
    35  	var valErr error
    36  	name := "param"
    37  	val := 42
    38  	expected := "43"
    39  
    40  	JustBeforeEach(func() {
    41  		valErr = InvalidParamTypeError(name, val, expected)
    42  	})
    43  
    44  	It("creates a http error", func() {
    45  		Ω(valErr).ShouldNot(BeNil())
    46  		Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
    47  		err := valErr.(*ErrorResponse)
    48  		Ω(err.Detail).Should(ContainSubstring(name))
    49  		Ω(err.Detail).Should(ContainSubstring("%d", val))
    50  		Ω(err.Detail).Should(ContainSubstring(expected))
    51  	})
    52  })
    53  
    54  var _ = Describe("MissingParaerror", func() {
    55  	var valErr error
    56  	name := "param"
    57  
    58  	JustBeforeEach(func() {
    59  		valErr = MissingParamError(name)
    60  	})
    61  
    62  	It("creates a http error", func() {
    63  		Ω(valErr).ShouldNot(BeNil())
    64  		Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
    65  		err := valErr.(*ErrorResponse)
    66  		Ω(err.Detail).Should(ContainSubstring(name))
    67  	})
    68  })
    69  
    70  var _ = Describe("InvalidAttributeTypeError", func() {
    71  	var valErr error
    72  	ctx := "ctx"
    73  	val := 42
    74  	expected := "43"
    75  
    76  	JustBeforeEach(func() {
    77  		valErr = InvalidAttributeTypeError(ctx, val, expected)
    78  	})
    79  
    80  	It("creates a http error", func() {
    81  		Ω(valErr).ShouldNot(BeNil())
    82  		Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
    83  		err := valErr.(*ErrorResponse)
    84  		Ω(err.Detail).Should(ContainSubstring(ctx))
    85  		Ω(err.Detail).Should(ContainSubstring("%d", val))
    86  		Ω(err.Detail).Should(ContainSubstring(expected))
    87  	})
    88  })
    89  
    90  var _ = Describe("MissingAttributeError", func() {
    91  	var valErr error
    92  	ctx := "ctx"
    93  	name := "param"
    94  
    95  	JustBeforeEach(func() {
    96  		valErr = MissingAttributeError(ctx, name)
    97  	})
    98  
    99  	It("creates a http error", func() {
   100  		Ω(valErr).ShouldNot(BeNil())
   101  		Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
   102  		err := valErr.(*ErrorResponse)
   103  		Ω(err.Detail).Should(ContainSubstring(ctx))
   104  		Ω(err.Detail).Should(ContainSubstring(name))
   105  	})
   106  })
   107  
   108  var _ = Describe("MissingHeaderError", func() {
   109  	var valErr error
   110  	name := "param"
   111  
   112  	JustBeforeEach(func() {
   113  		valErr = MissingHeaderError(name)
   114  	})
   115  
   116  	It("creates a http error", func() {
   117  		Ω(valErr).ShouldNot(BeNil())
   118  		Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
   119  		err := valErr.(*ErrorResponse)
   120  		Ω(err.Detail).Should(ContainSubstring(name))
   121  	})
   122  })
   123  
   124  var _ = Describe("InvalidEnumValueError", func() {
   125  	var valErr error
   126  	ctx := "ctx"
   127  	val := 42
   128  	allowed := []interface{}{"43", "44"}
   129  
   130  	JustBeforeEach(func() {
   131  		valErr = InvalidEnumValueError(ctx, val, allowed)
   132  	})
   133  
   134  	It("creates a http error", func() {
   135  		Ω(valErr).ShouldNot(BeNil())
   136  		Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
   137  		err := valErr.(*ErrorResponse)
   138  		Ω(err.Detail).Should(ContainSubstring(ctx))
   139  		Ω(err.Detail).Should(ContainSubstring("%d", val))
   140  		Ω(err.Detail).Should(ContainSubstring(`"43", "44"`))
   141  	})
   142  })
   143  
   144  var _ = Describe("InvalidFormaerror", func() {
   145  	var valErr error
   146  	ctx := "ctx"
   147  	target := "target"
   148  	format := FormatDateTime
   149  	formatError := errors.New("boo")
   150  
   151  	JustBeforeEach(func() {
   152  		valErr = InvalidFormatError(ctx, target, format, formatError)
   153  	})
   154  
   155  	It("creates a http error", func() {
   156  		Ω(valErr).ShouldNot(BeNil())
   157  		Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
   158  		err := valErr.(*ErrorResponse)
   159  		Ω(err.Detail).Should(ContainSubstring(ctx))
   160  		Ω(err.Detail).Should(ContainSubstring(target))
   161  		Ω(err.Detail).Should(ContainSubstring("date-time"))
   162  		Ω(err.Detail).Should(ContainSubstring(formatError.Error()))
   163  	})
   164  })
   165  
   166  var _ = Describe("InvalidPatternError", func() {
   167  	var valErr error
   168  	ctx := "ctx"
   169  	target := "target"
   170  	pattern := "pattern"
   171  
   172  	JustBeforeEach(func() {
   173  		valErr = InvalidPatternError(ctx, target, pattern)
   174  	})
   175  
   176  	It("creates a http error", func() {
   177  		Ω(valErr).ShouldNot(BeNil())
   178  		Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
   179  		err := valErr.(*ErrorResponse)
   180  		Ω(err.Detail).Should(ContainSubstring(ctx))
   181  		Ω(err.Detail).Should(ContainSubstring(target))
   182  		Ω(err.Detail).Should(ContainSubstring(pattern))
   183  	})
   184  })
   185  
   186  var _ = Describe("InvalidRangeError", func() {
   187  	var valErr error
   188  	var value interface{}
   189  
   190  	ctx := "ctx"
   191  	target := "target"
   192  	min := true
   193  
   194  	JustBeforeEach(func() {
   195  		valErr = InvalidRangeError(ctx, target, value, min)
   196  	})
   197  
   198  	Context("with an int value", func() {
   199  		BeforeEach(func() {
   200  			value = 42
   201  		})
   202  
   203  		It("creates a http error", func() {
   204  			Ω(valErr).ShouldNot(BeNil())
   205  			Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
   206  			err := valErr.(*ErrorResponse)
   207  			Ω(err.Detail).Should(ContainSubstring(ctx))
   208  			Ω(err.Detail).Should(ContainSubstring("greater than or equal to"))
   209  			Ω(err.Detail).Should(ContainSubstring(fmt.Sprintf("%#v", value)))
   210  			Ω(err.Detail).Should(ContainSubstring(target))
   211  		})
   212  	})
   213  
   214  	Context("with a float64 value", func() {
   215  		BeforeEach(func() {
   216  			value = 42.42
   217  		})
   218  
   219  		It("creates a http error with no value truncation", func() {
   220  			Ω(valErr).ShouldNot(BeNil())
   221  			Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
   222  			err := valErr.(*ErrorResponse)
   223  			Ω(err.Detail).Should(ContainSubstring(ctx))
   224  			Ω(err.Detail).Should(ContainSubstring("greater than or equal to"))
   225  			Ω(err.Detail).Should(ContainSubstring(fmt.Sprintf("%#v", value)))
   226  			Ω(err.Detail).Should(ContainSubstring(target))
   227  		})
   228  	})
   229  })
   230  
   231  var _ = Describe("InvalidLengthError", func() {
   232  	const ctx = "ctx"
   233  	const value = 42
   234  	const min = true
   235  
   236  	var target interface{}
   237  	var ln int
   238  
   239  	var valErr error
   240  
   241  	JustBeforeEach(func() {
   242  		valErr = InvalidLengthError(ctx, target, ln, value, min)
   243  	})
   244  
   245  	Context("on strings", func() {
   246  		BeforeEach(func() {
   247  			target = "target"
   248  			ln = len("target")
   249  		})
   250  
   251  		It("creates a http error", func() {
   252  			Ω(valErr).ShouldNot(BeNil())
   253  			Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
   254  			err := valErr.(*ErrorResponse)
   255  			Ω(err.Detail).Should(ContainSubstring(ctx))
   256  			Ω(err.Detail).Should(ContainSubstring("greater than or equal to"))
   257  			Ω(err.Detail).Should(ContainSubstring(fmt.Sprintf("%#v", value)))
   258  			Ω(err.Detail).Should(ContainSubstring(target.(string)))
   259  		})
   260  	})
   261  
   262  	Context("on slices", func() {
   263  		BeforeEach(func() {
   264  			target = []string{"target1", "target2"}
   265  			ln = 2
   266  		})
   267  
   268  		It("creates a http error", func() {
   269  			Ω(valErr).ShouldNot(BeNil())
   270  			Ω(valErr).Should(BeAssignableToTypeOf(&ErrorResponse{}))
   271  			err := valErr.(*ErrorResponse)
   272  			Ω(err.Detail).Should(ContainSubstring(ctx))
   273  			Ω(err.Detail).Should(ContainSubstring("greater than or equal to"))
   274  			Ω(err.Detail).Should(ContainSubstring(fmt.Sprintf("%#v", value)))
   275  			Ω(err.Detail).Should(ContainSubstring(fmt.Sprintf("%#v", target)))
   276  		})
   277  	})
   278  })
   279  
   280  var _ = Describe("Merge", func() {
   281  	var err, err2 error
   282  	var mErr *ErrorResponse
   283  
   284  	BeforeEach(func() {
   285  		err = nil
   286  		err2 = nil
   287  		mErr = nil
   288  	})
   289  
   290  	JustBeforeEach(func() {
   291  		e := MergeErrors(err, err2)
   292  		if e != nil {
   293  			mErr = e.(*ErrorResponse)
   294  		}
   295  	})
   296  
   297  	Context("with two nil errors", func() {
   298  		It("returns a nil error", func() {
   299  			Ω(mErr).Should(BeNil())
   300  		})
   301  	})
   302  
   303  	Context("with a nil argument", func() {
   304  		const code = "foo"
   305  
   306  		BeforeEach(func() {
   307  			err = &ErrorResponse{Code: code}
   308  		})
   309  
   310  		It("returns the target", func() {
   311  			Ω(mErr).Should(Equal(err))
   312  		})
   313  	})
   314  
   315  	Context("with a nil target", func() {
   316  		Context("with the second argument a Error", func() {
   317  			const detail = "foo"
   318  
   319  			BeforeEach(func() {
   320  				err2 = &ErrorResponse{Detail: detail}
   321  			})
   322  
   323  			It("returns it", func() {
   324  				Ω(mErr).Should(Equal(err2))
   325  			})
   326  		})
   327  
   328  		Context("with the second argument not a Error", func() {
   329  			const detail = "foo"
   330  			BeforeEach(func() {
   331  				err2 = errors.New(detail)
   332  			})
   333  
   334  			It("wraps it into a Error", func() {
   335  				Ω(mErr).ShouldNot(BeNil())
   336  				Ω(mErr.Detail).Should(Equal(detail))
   337  			})
   338  		})
   339  
   340  	})
   341  
   342  	Context("with a non-nil target", func() {
   343  		const detail = "foo"
   344  		var status = 42
   345  		var code = "common"
   346  
   347  		BeforeEach(func() {
   348  			err = &ErrorResponse{Detail: detail, Status: status, Code: code}
   349  		})
   350  
   351  		Context("with another Error", func() {
   352  			const detail2 = "foo2"
   353  			var status2 = status
   354  			var code2 = code
   355  			var mErr2 *ErrorResponse
   356  
   357  			BeforeEach(func() {
   358  				mErr2 = &ErrorResponse{Detail: detail2, Status: status2, Code: code2}
   359  				err2 = mErr2
   360  			})
   361  
   362  			It("concatenates both error details", func() {
   363  				Ω(mErr.Detail).Should(Equal(detail + "; " + mErr2.Detail))
   364  			})
   365  
   366  			It("uses the common status", func() {
   367  				Ω(mErr.Status).Should(Equal(status))
   368  			})
   369  
   370  			It("uses the common code", func() {
   371  				Ω(mErr.Code).Should(Equal(code))
   372  			})
   373  
   374  			Context("with different code", func() {
   375  				BeforeEach(func() {
   376  					mErr2.Code = code + code
   377  				})
   378  
   379  				It("produces a bad_request error", func() {
   380  					Ω(mErr.Code).Should(Equal("bad_request"))
   381  					Ω(mErr.Status).Should(Equal(400))
   382  					Ω(mErr.Detail).Should(Equal(detail + "; " + mErr2.Detail))
   383  				})
   384  			})
   385  
   386  			Context("with different status", func() {
   387  				BeforeEach(func() {
   388  					mErr2.Status = status + status
   389  				})
   390  
   391  				It("produces a bad_request error", func() {
   392  					Ω(mErr.Code).Should(Equal("bad_request"))
   393  					Ω(mErr.Status).Should(Equal(400))
   394  					Ω(mErr.Detail).Should(Equal(detail + "; " + mErr2.Detail))
   395  				})
   396  			})
   397  
   398  			Context("with nil target metadata", func() {
   399  				BeforeEach(func() {
   400  					err.(*ErrorResponse).Meta = nil
   401  				})
   402  
   403  				Context("with nil/empty other metadata", func() {
   404  					BeforeEach(func() {
   405  						mErr2.Meta = nil
   406  					})
   407  
   408  					It("keeps nil target metadata if no other metadata", func() {
   409  						Ω(mErr.Meta).Should(BeNil())
   410  					})
   411  				})
   412  
   413  				Context("with other metadata", func() {
   414  					metaValues2 := map[string]interface{}{"foo": 1, "bar": 2}
   415  
   416  					BeforeEach(func() {
   417  						err.(*ErrorResponse).Meta = nil
   418  						mErr2.Meta = metaValues2
   419  					})
   420  
   421  					It("merges the metadata", func() {
   422  						Ω(mErr.Meta).Should(HaveLen(len(metaValues2)))
   423  						for k, v := range metaValues2 {
   424  							Ω(mErr.Meta[k]).Should(Equal(v))
   425  						}
   426  					})
   427  				})
   428  			})
   429  
   430  			Context("with target metadata", func() {
   431  				metaValues := map[string]interface{}{"baz": 3, "qux": 4}
   432  
   433  				BeforeEach(func() {
   434  					mv := make(map[string]interface{}, len(metaValues))
   435  					for k, v := range metaValues {
   436  						mv[k] = v
   437  					}
   438  					err.(*ErrorResponse).Meta = mv
   439  				})
   440  
   441  				Context("with nil/empty other metadata", func() {
   442  					BeforeEach(func() {
   443  						mErr2.Meta = nil
   444  					})
   445  
   446  					It("keeps target metadata if no other metadata", func() {
   447  						Ω(mErr.Meta).Should(HaveLen(len(metaValues)))
   448  						for k, v := range metaValues {
   449  							Ω(mErr.Meta[k]).Should(Equal(v))
   450  						}
   451  					})
   452  				})
   453  
   454  				Context("with other metadata", func() {
   455  					metaValues2 := map[string]interface{}{"foo": 1, "bar": 2}
   456  
   457  					BeforeEach(func() {
   458  						mErr2.Meta = metaValues2
   459  					})
   460  
   461  					It("merges the metadata", func() {
   462  						Ω(mErr.Meta).Should(HaveLen(len(metaValues) + len(metaValues2)))
   463  						for k, v := range metaValues {
   464  							Ω(mErr.Meta[k]).Should(Equal(v))
   465  						}
   466  						for k, v := range metaValues2 {
   467  							Ω(mErr.Meta[k]).Should(Equal(v))
   468  						}
   469  					})
   470  				})
   471  			})
   472  
   473  			Context("with metadata with a common key", func() {
   474  				const commonKey = "foo"
   475  
   476  				var metaValues = map[string]interface{}{commonKey: "bar", "foo2": 44}
   477  				var metaValues2 = map[string]interface{}{commonKey: 43, "baz": 42}
   478  
   479  				BeforeEach(func() {
   480  					mv := make(map[string]interface{}, len(metaValues))
   481  					for k, v := range metaValues {
   482  						mv[k] = v
   483  					}
   484  					err.(*ErrorResponse).Meta = mv
   485  					mErr2.Meta = metaValues2
   486  				})
   487  
   488  				It("merges the metadata", func() {
   489  					Ω(mErr.Meta).Should(HaveLen(len(metaValues) + len(metaValues2) - 1))
   490  					for k, v := range metaValues {
   491  						if k != commonKey {
   492  							Ω(mErr.Meta[k]).Should(Equal(v))
   493  						}
   494  					}
   495  					for k, v := range metaValues2 {
   496  						if k != commonKey {
   497  							Ω(mErr.Meta[k]).Should(Equal(v))
   498  						}
   499  					}
   500  					Ω(mErr.Meta[commonKey]).Should(Equal(metaValues2[commonKey]))
   501  				})
   502  			})
   503  		})
   504  	})
   505  
   506  })