1package assert
2
3import (
4	"fmt"
5	"os"
6	"testing"
7
8	gocmp "github.com/google/go-cmp/cmp"
9	"gotest.tools/assert/cmp"
10)
11
12type fakeTestingT struct {
13	failNowed bool
14	failed    bool
15	msgs      []string
16}
17
18func (f *fakeTestingT) FailNow() {
19	f.failNowed = true
20}
21
22func (f *fakeTestingT) Fail() {
23	f.failed = true
24}
25
26func (f *fakeTestingT) Log(args ...interface{}) {
27	f.msgs = append(f.msgs, args[0].(string))
28}
29
30func (f *fakeTestingT) Helper() {}
31
32func TestAssertWithBoolFailure(t *testing.T) {
33	fakeT := &fakeTestingT{}
34
35	Assert(fakeT, 1 == 6)
36	expectFailNowed(t, fakeT, "assertion failed: expression is false: 1 == 6")
37}
38
39func TestAssertWithBoolFailureNotEqual(t *testing.T) {
40	fakeT := &fakeTestingT{}
41
42	var err error
43	Assert(fakeT, err != nil)
44	expectFailNowed(t, fakeT, "assertion failed: err is nil")
45}
46
47func TestAssertWithBoolFailureNotTrue(t *testing.T) {
48	fakeT := &fakeTestingT{}
49
50	badNews := true
51	Assert(fakeT, !badNews)
52	expectFailNowed(t, fakeT, "assertion failed: badNews is true")
53}
54
55func TestAssertWithBoolFailureAndExtraMessage(t *testing.T) {
56	fakeT := &fakeTestingT{}
57
58	Assert(fakeT, 1 > 5, "sometimes things fail")
59	expectFailNowed(t, fakeT,
60		"assertion failed: expression is false: 1 > 5: sometimes things fail")
61}
62
63func TestAssertWithBoolSuccess(t *testing.T) {
64	fakeT := &fakeTestingT{}
65
66	Assert(fakeT, 1 < 5)
67	expectSuccess(t, fakeT)
68}
69
70func TestAssertWithBoolMultiLineFailure(t *testing.T) {
71	fakeT := &fakeTestingT{}
72
73	Assert(fakeT, func() bool {
74		for range []int{1, 2, 3, 4} {
75		}
76		return false
77	}())
78	expectFailNowed(t, fakeT, `assertion failed: expression is false: func() bool {
79	for range []int{1, 2, 3, 4} {
80	}
81	return false
82}()`)
83}
84
85type exampleComparison struct {
86	success bool
87	message string
88}
89
90func (c exampleComparison) Compare() (bool, string) {
91	return c.success, c.message
92}
93
94func TestAssertWithComparisonSuccess(t *testing.T) {
95	fakeT := &fakeTestingT{}
96
97	cmp := exampleComparison{success: true}
98	Assert(fakeT, cmp.Compare)
99	expectSuccess(t, fakeT)
100}
101
102func TestAssertWithComparisonFailure(t *testing.T) {
103	fakeT := &fakeTestingT{}
104
105	cmp := exampleComparison{message: "oops, not good"}
106	Assert(fakeT, cmp.Compare)
107	expectFailNowed(t, fakeT, "assertion failed: oops, not good")
108}
109
110func TestAssertWithComparisonAndExtraMessage(t *testing.T) {
111	fakeT := &fakeTestingT{}
112
113	cmp := exampleComparison{message: "oops, not good"}
114	Assert(fakeT, cmp.Compare, "extra stuff %v", true)
115	expectFailNowed(t, fakeT, "assertion failed: oops, not good: extra stuff true")
116}
117
118type customError struct{}
119
120func (e *customError) Error() string {
121	return "custom error"
122}
123
124func TestNilErrorSuccess(t *testing.T) {
125	fakeT := &fakeTestingT{}
126
127	var err error
128	NilError(fakeT, err)
129	expectSuccess(t, fakeT)
130
131	NilError(fakeT, nil)
132	expectSuccess(t, fakeT)
133
134	var customErr *customError
135	NilError(fakeT, customErr)
136}
137
138func TestNilErrorFailure(t *testing.T) {
139	fakeT := &fakeTestingT{}
140
141	NilError(fakeT, fmt.Errorf("this is the error"))
142	expectFailNowed(t, fakeT, "assertion failed: error is not nil: this is the error")
143}
144
145func TestCheckFailure(t *testing.T) {
146	fakeT := &fakeTestingT{}
147
148	if Check(fakeT, 1 == 2) {
149		t.Error("expected check to return false on failure")
150	}
151	expectFailed(t, fakeT, "assertion failed: expression is false: 1 == 2")
152}
153
154func TestCheckSuccess(t *testing.T) {
155	fakeT := &fakeTestingT{}
156
157	if !Check(fakeT, 1 == 1) {
158		t.Error("expected check to return true on success")
159	}
160	expectSuccess(t, fakeT)
161}
162
163func TestCheckEqualFailure(t *testing.T) {
164	fakeT := &fakeTestingT{}
165
166	actual, expected := 5, 9
167	Check(fakeT, cmp.Equal(actual, expected))
168	expectFailed(t, fakeT, "assertion failed: 5 (actual int) != 9 (expected int)")
169}
170
171func TestEqualSuccess(t *testing.T) {
172	fakeT := &fakeTestingT{}
173
174	Equal(fakeT, 1, 1)
175	expectSuccess(t, fakeT)
176
177	Equal(fakeT, "abcd", "abcd")
178	expectSuccess(t, fakeT)
179}
180
181func TestEqualFailure(t *testing.T) {
182	fakeT := &fakeTestingT{}
183
184	actual, expected := 1, 3
185	Equal(fakeT, actual, expected)
186	expectFailNowed(t, fakeT, "assertion failed: 1 (actual int) != 3 (expected int)")
187}
188
189func TestEqualFailureTypes(t *testing.T) {
190	fakeT := &fakeTestingT{}
191
192	Equal(fakeT, 3, uint(3))
193	expectFailNowed(t, fakeT, `assertion failed: 3 (int) != 3 (uint)`)
194}
195
196func TestEqualFailureWithSelectorArgument(t *testing.T) {
197	fakeT := &fakeTestingT{}
198
199	type tc struct {
200		expected string
201	}
202	var testcase = tc{expected: "foo"}
203
204	Equal(fakeT, "ok", testcase.expected)
205	expectFailNowed(t, fakeT,
206		"assertion failed: ok (string) != foo (testcase.expected string)")
207}
208
209func TestEqualFailureWithIndexExpr(t *testing.T) {
210	fakeT := &fakeTestingT{}
211
212	expected := map[string]string{"foo": "bar"}
213	Equal(fakeT, "ok", expected["foo"])
214	expectFailNowed(t, fakeT,
215		`assertion failed: ok (string) != bar (expected["foo"] string)`)
216}
217
218func TestEqualFailureWithCallExprArgument(t *testing.T) {
219	fakeT := &fakeTestingT{}
220	ce := customError{}
221	Equal(fakeT, "", ce.Error())
222	expectFailNowed(t, fakeT,
223		"assertion failed:  (string) != custom error (string)")
224}
225
226func TestAssertFailureWithOfflineComparison(t *testing.T) {
227	fakeT := &fakeTestingT{}
228	a := 1
229	b := 2
230	// store comparison in a variable, so ast lookup can't find it
231	comparison := cmp.Equal(a, b)
232	Assert(fakeT, comparison)
233	// expected value wont have variable names
234	expectFailNowed(t, fakeT, "assertion failed: 1 (int) != 2 (int)")
235}
236
237type testingT interface {
238	Errorf(msg string, args ...interface{})
239	Fatalf(msg string, args ...interface{})
240}
241
242func expectFailNowed(t testingT, fakeT *fakeTestingT, expected string) {
243	if ht, ok := t.(helperT); ok {
244		ht.Helper()
245	}
246	if fakeT.failed {
247		t.Errorf("should not have failed, got messages %s", fakeT.msgs)
248	}
249	if !fakeT.failNowed {
250		t.Fatalf("should have failNowed with message %s", expected)
251	}
252	if fakeT.msgs[0] != expected {
253		t.Fatalf("should have failure message %q, got %q", expected, fakeT.msgs[0])
254	}
255}
256
257// nolint: unparam
258func expectFailed(t testingT, fakeT *fakeTestingT, expected string) {
259	if ht, ok := t.(helperT); ok {
260		ht.Helper()
261	}
262	if fakeT.failNowed {
263		t.Errorf("should not have failNowed, got messages %s", fakeT.msgs)
264	}
265	if !fakeT.failed {
266		t.Fatalf("should have failed with message %s", expected)
267	}
268	if fakeT.msgs[0] != expected {
269		t.Fatalf("should have failure message %q, got %q", expected, fakeT.msgs[0])
270	}
271}
272
273func expectSuccess(t testingT, fakeT *fakeTestingT) {
274	if ht, ok := t.(helperT); ok {
275		ht.Helper()
276	}
277	if fakeT.failNowed {
278		t.Errorf("should not have failNowed, got messages %s", fakeT.msgs)
279	}
280	if fakeT.failed {
281		t.Errorf("should not have failed, got messages %s", fakeT.msgs)
282	}
283}
284
285type stub struct {
286	a string
287	b int
288}
289
290func TestDeepEqualSuccess(t *testing.T) {
291	actual := stub{"ok", 1}
292	expected := stub{"ok", 1}
293
294	fakeT := &fakeTestingT{}
295	DeepEqual(fakeT, actual, expected, gocmp.AllowUnexported(stub{}))
296	expectSuccess(t, fakeT)
297}
298
299func TestDeepEqualFailure(t *testing.T) {
300	actual := stub{"ok", 1}
301	expected := stub{"ok", 2}
302
303	fakeT := &fakeTestingT{}
304	DeepEqual(fakeT, actual, expected, gocmp.AllowUnexported(stub{}))
305	expectFailNowed(t, fakeT, "assertion failed: "+`
306--- actual
307+++ expected
308{assert.stub}.b:
309	-: 1
310	+: 2
311`)
312}
313
314func TestErrorFailure(t *testing.T) {
315	t.Run("nil error", func(t *testing.T) {
316		fakeT := &fakeTestingT{}
317
318		var err error
319		Error(fakeT, err, "this error")
320		expectFailNowed(t, fakeT, "assertion failed: expected an error, got nil")
321	})
322	t.Run("different error", func(t *testing.T) {
323		fakeT := &fakeTestingT{}
324
325		err := fmt.Errorf("the actual error")
326		Error(fakeT, err, "this error")
327		expected := `assertion failed: expected error "this error", got "the actual error"`
328		expectFailNowed(t, fakeT, expected)
329	})
330}
331
332func TestErrorContainsFailure(t *testing.T) {
333	t.Run("nil error", func(t *testing.T) {
334		fakeT := &fakeTestingT{}
335
336		var err error
337		ErrorContains(fakeT, err, "this error")
338		expectFailNowed(t, fakeT, "assertion failed: expected an error, got nil")
339	})
340	t.Run("different error", func(t *testing.T) {
341		fakeT := &fakeTestingT{}
342
343		err := fmt.Errorf("the actual error")
344		ErrorContains(fakeT, err, "this error")
345		expected := `assertion failed: expected error to contain "this error", got "the actual error"`
346		expectFailNowed(t, fakeT, expected)
347	})
348}
349
350func TestErrorTypeFailure(t *testing.T) {
351	t.Run("nil error", func(t *testing.T) {
352		fakeT := &fakeTestingT{}
353
354		var err error
355		ErrorType(fakeT, err, os.IsNotExist)
356		expectFailNowed(t, fakeT, "assertion failed: error is nil, not os.IsNotExist")
357	})
358	t.Run("different error", func(t *testing.T) {
359		fakeT := &fakeTestingT{}
360
361		err := fmt.Errorf("the actual error")
362		ErrorType(fakeT, err, os.IsNotExist)
363		expected := `assertion failed: error is the actual error (*errors.errorString), not os.IsNotExist`
364		expectFailNowed(t, fakeT, expected)
365	})
366}
367