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