1 package matchers_test
2
3 import (
4 "errors"
5
6 . "github.com/onsi/ginkgo/v2"
7 . "github.com/onsi/gomega"
8 . "github.com/onsi/gomega/matchers"
9 )
10
11 var _ = Describe("WithTransformMatcher", func() {
12
13 var plus1 = func(i int) int { return i + 1 }
14
15 Context("Panic if transform function invalid", func() {
16 panicsWithTransformer := func(transform interface{}) {
17 Expect(func() { WithTransform(transform, nil) }).WithOffset(1).To(Panic())
18 }
19 It("nil", func() {
20 panicsWithTransformer(nil)
21 })
22 Context("Invalid number of args, but correct return value count", func() {
23 It("zero", func() {
24 panicsWithTransformer(func() int { return 5 })
25 })
26 It("two", func() {
27 panicsWithTransformer(func(i, j int) int { return 5 })
28 })
29 })
30 Context("Invalid number of return values, but correct number of arguments", func() {
31 It("zero", func() {
32 panicsWithTransformer(func(i int) {})
33 })
34 It("two", func() {
35 panicsWithTransformer(func(i int) (int, int) { return 5, 6 })
36 })
37 })
38 Context("Invalid number of return values, but correct number of arguments", func() {
39 It("Two return values, but second return value not an error", func() {
40 panicsWithTransformer(func(interface{}) (int, int) { return 5, 6 })
41 })
42 })
43 })
44
45 When("the actual value is incompatible", func() {
46 It("fails to pass int to func(string)", func() {
47 actual, transform := int(0), func(string) int { return 0 }
48 success, err := WithTransform(transform, Equal(0)).Match(actual)
49 Expect(success).To(BeFalse())
50 Expect(err).To(HaveOccurred())
51 Expect(err.Error()).To(ContainSubstring("function expects 'string'"))
52 Expect(err.Error()).To(ContainSubstring("have 'int'"))
53 })
54
55 It("fails to pass string to func(interface)", func() {
56 actual, transform := "bang", func(error) int { return 0 }
57 success, err := WithTransform(transform, Equal(0)).Match(actual)
58 Expect(success).To(BeFalse())
59 Expect(err).To(HaveOccurred())
60 Expect(err.Error()).To(ContainSubstring("function expects 'error'"))
61 Expect(err.Error()).To(ContainSubstring("have 'string'"))
62 })
63
64 It("fails to pass nil interface to func(int)", func() {
65 actual, transform := error(nil), func(int) int { return 0 }
66 success, err := WithTransform(transform, Equal(0)).Match(actual)
67 Expect(success).To(BeFalse())
68 Expect(err).To(HaveOccurred())
69 Expect(err.Error()).To(ContainSubstring("function expects 'int'"))
70 Expect(err.Error()).To(ContainSubstring("have '<nil>'"))
71 })
72
73 It("fails to pass nil interface to func(pointer)", func() {
74 actual, transform := error(nil), func(*string) int { return 0 }
75 success, err := WithTransform(transform, Equal(0)).Match(actual)
76 Expect(success).To(BeFalse())
77 Expect(err).To(HaveOccurred())
78 Expect(err.Error()).To(ContainSubstring("function expects '*string'"))
79 Expect(err.Error()).To(ContainSubstring("have '<nil>'"))
80 })
81 })
82
83 It("works with positive cases", func() {
84 Expect(1).To(WithTransform(plus1, Equal(2)))
85 Expect(1).To(WithTransform(plus1, WithTransform(plus1, Equal(3))))
86 Expect(1).To(WithTransform(plus1, And(Equal(2), BeNumerically(">", 1))))
87
88
89 type S struct {
90 A int
91 B string
92 }
93 transformer := func(s S) string { return s.B }
94 Expect(S{1, "hi"}).To(WithTransform(transformer, Equal("hi")))
95
96
97 errString := func(e error) string {
98 if e == nil {
99 return "safe"
100 }
101 return e.Error()
102 }
103 Expect(nil).To(WithTransform(errString, Equal("safe")), "handles nil actual values")
104 Expect(errors.New("abc")).To(WithTransform(errString, Equal("abc")))
105 })
106
107 It("works with negative cases", func() {
108 Expect(1).ToNot(WithTransform(plus1, Equal(3)))
109 Expect(1).ToNot(WithTransform(plus1, WithTransform(plus1, Equal(2))))
110 })
111
112 Context("failure messages", func() {
113 When("match fails", func() {
114 It("gives a descriptive message", func() {
115 m := WithTransform(plus1, Equal(3))
116 Expect(m.Match(1)).To(BeFalse())
117 Expect(m.FailureMessage(1)).To(Equal("Expected\n <int>: 2\nto equal\n <int>: 3"))
118 })
119 })
120
121 When("match succeeds, but expected it to fail", func() {
122 It("gives a descriptive message", func() {
123 m := Not(WithTransform(plus1, Equal(3)))
124 Expect(m.Match(2)).To(BeFalse())
125 Expect(m.FailureMessage(2)).To(Equal("Expected\n <int>: 3\nnot to equal\n <int>: 3"))
126 })
127 })
128
129 When("transform fails", func() {
130 It("reports the transformation error", func() {
131 actual, trafo := "foo", func(string) (string, error) { return "", errors.New("that does not transform") }
132 success, err := WithTransform(trafo, Equal(actual)).Match(actual)
133 Expect(success).To(BeFalse())
134 Expect(err).To(HaveOccurred())
135 Expect(err.Error()).To(MatchRegexp(": that does not transform$"))
136 })
137 })
138
139 Context("actual value is incompatible with transform function's argument type", func() {
140 It("gracefully fails if transform cannot be performed", func() {
141 m := WithTransform(plus1, Equal(3))
142 result, err := m.Match("hi")
143 Expect(result).To(BeFalse())
144 Expect(err).To(MatchError("Transform function expects 'int' but we have 'string'"))
145 })
146 })
147 })
148
149 Context("MatchMayChangeInTheFuture()", func() {
150 It("Propagates value from wrapped matcher on the transformed value", func() {
151 m := WithTransform(plus1, Or())
152 Expect(m.Match(1)).To(BeFalse())
153 Expect(m.(*WithTransformMatcher).MatchMayChangeInTheFuture(1)).To(BeFalse())
154 })
155 It("Defaults to true", func() {
156 m := WithTransform(plus1, Equal(2))
157 Expect(m.Match(1)).To(BeTrue())
158 Expect(m.(*WithTransformMatcher).MatchMayChangeInTheFuture(1)).To(BeTrue())
159 })
160 })
161 })
162
View as plain text