-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtesting.go
More file actions
243 lines (227 loc) · 6.57 KB
/
testing.go
File metadata and controls
243 lines (227 loc) · 6.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
package result
import (
"fmt"
"strings"
"testing"
)
// AssertOk fails the test if Result is Err and returns the unwrapped value.
// This is the most common testing helper - use it when you expect success.
//
// Example:
//
// user := result.AssertOk(t, repo.FindByID(ctx, id))
// assert.Equal(t, "test@example.com", user.Email)
func AssertOk[T any](t testing.TB, r Result[T]) T {
t.Helper()
if r.IsErr() {
t.Fatalf("Expected Ok, got Err: %v", r.UnwrapErr())
}
return r.Unwrap()
}
// AssertErr fails the test if Result is Ok and returns the unwrapped error.
// Use this when you expect an operation to fail.
//
// Example:
//
// err := result.AssertErr(t, repo.FindByID(ctx, "invalid-id"))
// assert.Contains(t, err.Error(), "not found")
func AssertErr[T any](t testing.TB, r Result[T]) error {
t.Helper()
if r.IsOk() {
t.Fatalf("Expected Err, got Ok: %v", r.Unwrap())
}
return r.UnwrapErr()
}
// AssertErrKind fails the test if Result is not Err with the expected Kind.
// Use this to verify specific error categories.
//
// Example:
//
// result.AssertErrKind(t, repo.FindByID(ctx, "invalid-id"), result.KindNotFound)
func AssertErrKind[T any](t testing.TB, r Result[T], expected Kind) {
t.Helper()
if r.IsOk() {
t.Fatalf("Expected Err with kind %v, got Ok: %v", expected, r.Unwrap())
}
actual := KindOf(r.UnwrapErr())
if actual != expected {
t.Fatalf("Expected error kind %v, got %v: %v", expected, actual, r.UnwrapErr())
}
}
// AssertErrContains fails the test if the error message doesn't contain the substring.
// Use this for checking specific error messages.
//
// Example:
//
// result.AssertErrContains(t, domain.NewEmail(""), "cannot be empty")
func AssertErrContains[T any](t testing.TB, r Result[T], substr string) {
t.Helper()
err := AssertErr(t, r)
if !strings.Contains(err.Error(), substr) {
t.Fatalf("Expected error to contain %q, got: %v", substr, err)
}
}
// AssertErrOp fails the test if the error doesn't have the expected operation.
// Use this to verify error context propagation.
//
// Example:
//
// result.AssertErrOp(t, handler.Handle(ctx, cmd), "commands.create_user")
func AssertErrOp[T any](t testing.TB, r Result[T], expectedOp string) {
t.Helper()
err := AssertErr(t, r)
actualOp := OpOf(err)
if !strings.Contains(actualOp, expectedOp) {
t.Fatalf("Expected error op to contain %q, got: %q", expectedOp, actualOp)
}
}
// OkOrFatal is like AssertOk but uses t.Fatal instead of t.Fatalf.
// Use this when you want a simpler error message.
//
// Example:
//
// user := result.OkOrFatal(t, repo.FindByID(ctx, id))
func OkOrFatal[T any](t testing.TB, r Result[T]) T {
t.Helper()
if r.IsErr() {
t.Fatal(r.UnwrapErr())
}
return r.Unwrap()
}
// RequireOk is an alias for AssertOk (for compatibility with testify naming).
func RequireOk[T any](t testing.TB, r Result[T]) T {
return AssertOk(t, r)
}
// RequireErr is an alias for AssertErr (for compatibility with testify naming).
func RequireErr[T any](t testing.TB, r Result[T]) error {
return AssertErr(t, r)
}
// AssertOkWith checks that Result is Ok and the value satisfies a predicate.
//
// Example:
//
// result.AssertOkWith(t, repo.FindByID(ctx, id), func(u *User) bool {
// return u.IsActive
// }, "expected user to be active")
func AssertOkWith[T any](t testing.TB, r Result[T], predicate func(T) bool, msg string) T {
t.Helper()
value := AssertOk(t, r)
if !predicate(value) {
t.Fatalf("Predicate failed: %s (value: %v)", msg, value)
}
return value
}
// AssertErrWith checks that Result is Err and the error satisfies a predicate.
//
// Example:
//
// result.AssertErrWith(t, domain.NewEmail(""), func(err error) bool {
// return result.KindOf(err) == result.KindValidation
// }, "expected validation error")
func AssertErrWith[T any](t testing.TB, r Result[T], predicate func(error) bool, msg string) error {
t.Helper()
err := AssertErr(t, r)
if !predicate(err) {
t.Fatalf("Predicate failed: %s (error: %v)", msg, err)
}
return err
}
// ExpectOk is like AssertOk but doesn't fail the test - just logs an error.
// Use this for non-critical assertions.
//
// Example:
//
// if user, ok := result.ExpectOk(t, repo.FindByID(ctx, id)); ok {
// // Continue with test
// }
func ExpectOk[T any](t testing.TB, r Result[T]) (T, bool) {
t.Helper()
if r.IsErr() {
t.Errorf("Expected Ok, got Err: %v", r.UnwrapErr())
var zero T
return zero, false
}
return r.Unwrap(), true
}
// ExpectErr is like AssertErr but doesn't fail the test - just logs an error.
//
// Example:
//
// if err, ok := result.ExpectErr(t, repo.FindByID(ctx, "invalid")); ok {
// // Continue with test
// }
func ExpectErr[T any](t testing.TB, r Result[T]) (error, bool) {
t.Helper()
if r.IsOk() {
t.Errorf("Expected Err, got Ok: %v", r.Unwrap())
return nil, false
}
return r.UnwrapErr(), true
}
// AssertPanics verifies that a function panics when called.
// Use this to test Must() and similar panic-inducing functions.
//
// Example:
//
// result.AssertPanics(t, func() {
// result.Must(result.Err[int](errors.New("fail")), "should panic")
// })
func AssertPanics(t testing.TB, fn func()) {
t.Helper()
defer func() {
if r := recover(); r == nil {
t.Fatal("Expected function to panic, but it didn't")
}
}()
fn()
}
// AssertNotPanics verifies that a function does not panic.
//
// Example:
//
// result.AssertNotPanics(t, func() {
// result.Must(result.Ok(42), "should not panic")
// })
func AssertNotPanics(t testing.TB, fn func()) {
t.Helper()
defer func() {
if r := recover(); r != nil {
t.Fatalf("Expected function not to panic, but it panicked: %v", r)
}
}()
fn()
}
// MockResult creates a mock Result for testing.
// Use this to simulate different Result scenarios in tests.
//
// Example:
//
// mockRepo := &MockRepo{
// FindByIDFunc: func(id string) result.Result[*User] {
// return result.MockResult[*User](result.KindNotFound, "user not found")
// },
// }
func MockResult[T any](kind Kind, message string) Result[T] {
var err error
switch kind {
case KindValidation:
err = Validation("mock.operation", message, nil)
case KindNotFound:
err = NotFound("mock.operation", message)
case KindConflict:
err = Conflict("mock.operation", message)
case KindUnauthorized:
err = Unauthorized("mock.operation", message)
case KindForbidden:
err = Forbidden("mock.operation", message)
case KindDomain:
err = Domain("mock.operation", message)
case KindInfrastructure:
err = Infrastructure("mock.operation", fmt.Errorf("%s", message))
case KindInternal:
err = Internal("mock.operation", fmt.Errorf("%s", message))
default:
err = Internal("mock.operation", fmt.Errorf("%s", message))
}
return Err[T](err)
}