Index: common/errors/tags_test.go |
diff --git a/common/errors/tags_test.go b/common/errors/tags_test.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1ae01b6ae0bec43f53c6accdc7e2dadf5660c58c |
--- /dev/null |
+++ b/common/errors/tags_test.go |
@@ -0,0 +1,149 @@ |
+// Copyright 2017 The LUCI Authors. All rights reserved. |
+// Use of this source code is governed under the Apache License, Version 2.0 |
+// that can be found in the LICENSE file. |
+ |
+package errors |
+ |
+import ( |
+ stdErr "errors" |
+ "testing" |
+ |
+ . "github.com/smartystreets/goconvey/convey" |
+) |
+ |
+type customInt int |
+ |
+func TestTags(t *testing.T) { |
+ t.Parallel() |
+ |
+ aBoolTag := NewBoolTag("errors.testing tag") |
+ aValueTag := NewTagger("errors.testing value tag") |
+ aStringTag := NewTagger("errors.testing string tag", func(v interface{}) { |
+ _ = v.(string) |
+ }) |
+ aCustomIntTag := NewTagger("errors.testing string tag", func(v interface{}) { |
+ _ = v.(customInt) |
+ }) |
+ |
+ Convey("Tags", t, func() { |
+ Convey(`have unique tagKey values`, func() { |
+ tagSet := map[tagKey]struct{}{} |
+ tagSet[aBoolTag.(tagKey)] = struct{}{} |
+ tagSet[aValueTag.(tagKey)] = struct{}{} |
+ tagSet[aStringTag.(tagKey)] = struct{}{} |
+ tagSet[aCustomIntTag.(tagKey)] = struct{}{} |
+ So(tagSet, ShouldHaveLength, 4) |
+ }) |
+ |
+ Convey(`can be applied to errors`, func() { |
+ Convey(`at creation time`, func() { |
+ err := New("I am an error", aBoolTag, aValueTag.With(100)) |
+ So(aBoolTag.In(err), ShouldBeTrue) |
+ d, ok := aValueTag.ValueIn(err) |
+ So(ok, ShouldBeTrue) |
+ So(d, ShouldEqual, 100) |
+ |
+ d, _ = aBoolTag.ValueIn(err) |
+ So(d, ShouldBeNil) |
+ |
+ So(aStringTag.In(err), ShouldBeFalse) |
+ So(aCustomIntTag.In(err), ShouldBeFalse) |
+ }) |
+ |
+ Convey(`added to existing errors`, func() { |
+ err := New("I am an error") |
+ err2 := aCustomIntTag.With(customInt(236)).Apply(err) |
+ err2 = aBoolTag.Apply(err2) |
+ |
+ d, ok := aCustomIntTag.ValueIn(err2) |
+ So(ok, ShouldBeTrue) |
+ So(d, ShouldEqual, customInt(236)) |
+ So(aBoolTag.In(err2), ShouldBeTrue) |
+ |
+ So(aCustomIntTag.In(err), ShouldBeFalse) |
+ So(aBoolTag.In(err), ShouldBeFalse) |
+ }) |
+ |
+ Convey(`added to stdlib errors`, func() { |
+ err := stdErr.New("I am an error") |
+ err2 := aStringTag.With("hi").Apply(err) |
+ |
+ d, ok := aStringTag.ValueIn(err2) |
+ So(ok, ShouldBeTrue) |
+ So(d, ShouldEqual, "hi") |
+ |
+ So(aStringTag.In(err), ShouldBeFalse) |
+ }) |
+ |
+ Convey(`multiple applications has the last one win`, func() { |
+ err := New("I am an error") |
+ err = aStringTag.With("hi").Apply(err) |
+ err = aStringTag.With("there").Apply(err) |
+ err = aStringTag.With("winner").Apply(err) |
+ |
+ d, ok := aStringTag.ValueIn(err) |
+ So(ok, ShouldBeTrue) |
+ So(d, ShouldEqual, "winner") |
+ |
+ Convey(`muliterrors are first to last`, func() { |
+ err = NewMultiError( |
+ New("a", aStringTag.With("hi"), aBoolTag), |
+ New("b", aValueTag.With("there"), aStringTag.With("no")), |
+ New("c", aValueTag.With("nope"), aStringTag.With("nopers")), |
+ ) |
+ |
+ So(aBoolTag.In(err), ShouldBeTrue) |
+ |
+ d, ok := aStringTag.ValueIn(err) |
+ So(ok, ShouldBeTrue) |
+ So(d, ShouldEqual, "hi") |
+ |
+ d, ok = aValueTag.ValueIn(err) |
+ So(ok, ShouldBeTrue) |
+ So(d, ShouldEqual, "there") |
+ |
+ Convey(`and all the correct values show up with GetTags`, func() { |
+ tags := GetTags(err) |
+ So(tags, ShouldContainKey, aValueTag) |
+ So(tags, ShouldContainKey, aStringTag) |
+ So(tags, ShouldContainKey, aBoolTag) |
+ So(tags, ShouldNotContainKey, aCustomIntTag) |
+ |
+ So(tags[aValueTag], ShouldEqual, "there") |
+ So(tags[aStringTag], ShouldEqual, "hi") |
+ So(tags[aBoolTag], ShouldBeNil) |
+ }) |
+ }) |
+ }) |
+ }) |
+ |
+ Convey(`only allow simple values`, func() { |
+ Convey(`numbers, strings OK`, func() { |
+ aValueTag.With("hi") |
+ aValueTag.With(100) |
+ aValueTag.With(false) |
+ aValueTag.With(34.100) |
+ aValueTag.With(12 + 100i) |
+ }) |
+ |
+ Convey(`no structs, etc.`, func() { |
+ So(func() { aValueTag.With(struct{}{}) }, ShouldPanic) |
+ So(func() { aValueTag.With([]int{}) }, ShouldPanic) |
+ So(func() { aValueTag.With(func() {}) }, ShouldPanic) |
+ So(func() { aValueTag.With(make(chan int)) }, ShouldPanic) |
+ So(func() { aValueTag.With([10]byte{}) }, ShouldPanic) |
+ So(func() { aValueTag.With(map[int]int{}) }, ShouldPanic) |
+ So(func() { aValueTag.With(&struct{}{}) }, ShouldPanic) |
+ }) |
+ |
+ Convey(`and custom validators can add additional constriants`, func() { |
+ aStringTag.With("sup") |
+ So(func() { aStringTag.With(100) }, ShouldPanic) |
+ |
+ aCustomIntTag.With(customInt(100)) |
+ So(func() { aCustomIntTag.With(100) }, ShouldPanic) |
+ So(func() { aCustomIntTag.With("hi") }, ShouldPanic) |
+ }) |
+ }) |
+ }) |
+} |