-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjoin_test.go
More file actions
346 lines (292 loc) · 8.54 KB
/
Copy pathjoin_test.go
File metadata and controls
346 lines (292 loc) · 8.54 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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
package xerror
import (
"encoding/json"
"errors"
"fmt"
"testing"
"github.com/gomooth/xerror/xcode"
)
func TestJoinXErrors(t *testing.T) {
err1 := NewCode(100001, "错误A")
err2 := NewCode(100002, "错误B")
joined := JoinXErrors(err1, err2)
if joined == nil {
t.Fatal("JoinXErrors should return non-nil XError")
}
// 返回类型应为 XError
if joined.ErrorCode() != 100001 {
t.Fatalf("joined ErrorCode should be 100001, got %d", joined.ErrorCode())
}
if !IsErrorCode(joined, 100001) {
t.Fatal("joined error should contain code 100001")
}
if !IsErrorCode(joined, 100002) {
t.Fatal("joined error should contain code 100002")
}
}
func TestJoinXErrors_NilFiltering(t *testing.T) {
err1 := NewCode(100001, "错误A")
joined := JoinXErrors(err1, nil, nil)
if joined == nil {
t.Fatal("JoinXErrors should filter nil and return non-nil")
}
if !IsErrorCode(joined, 100001) {
t.Fatal("joined error should contain code 100001")
}
}
func TestJoinXErrors_AllNil(t *testing.T) {
joined := JoinXErrors(nil, nil)
if joined != nil {
t.Fatal("JoinXErrors with all nil should return nil")
}
}
func TestJoinXErrors_ErrorsIs(t *testing.T) {
err1 := NewCode(100001, "错误A")
err2 := NewCode(100002, "错误B")
joined := JoinXErrors(err1, err2)
if !errors.Is(joined, err1) {
t.Fatal("errors.Is should find err1 in joined")
}
if !errors.Is(joined, err2) {
t.Fatal("errors.Is should find err2 in joined")
}
}
func TestJoinXErrors_WithFields(t *testing.T) {
err1 := NewCode(100001, "错误A")
err2 := NewCode(100002, "错误B")
joined := JoinXErrors(err1, err2)
withFields := joined.WithFields(F("requestId", "abc123"))
fields := withFields.GetFields()
if len(fields) != 1 {
t.Fatalf("expected 1 field, got %d", len(fields))
}
if fields[0].Key != "requestId" {
t.Fatalf("expected key 'requestId', got %q", fields[0].Key)
}
// 原始 joined 不受影响
if len(joined.GetFields()) != 0 {
t.Fatal("original joined should have no fields")
}
}
func TestJoinXErrors_ToMessage(t *testing.T) {
err1 := NewCode(100001, "错误A")
joined := JoinXErrors(err1, nil)
msg := joined.ToMessage(nil)
if msg != "错误A" {
t.Fatalf("expected '错误A', got %q", msg)
}
}
func TestJoinErrors(t *testing.T) {
err1 := NewCode(100001, "XError")
err2 := errors.New("plain error")
joined := JoinErrors(err1, err2)
if joined == nil {
t.Fatal("JoinErrors should return non-nil")
}
if !IsErrorCode(joined, 100001) {
t.Fatal("JoinErrors should find code 100001")
}
}
func TestJoinErrors_AllNil(t *testing.T) {
joined := JoinErrors(nil, nil)
if joined != nil {
t.Fatal("JoinErrors with all nil should return nil")
}
}
func TestJoinErrors_OnlyPlainErrors(t *testing.T) {
plain1 := errors.New("plain1")
plain2 := errors.New("plain2")
joined := JoinErrors(plain1, plain2)
if joined == nil {
t.Fatal("JoinErrors should return non-nil for plain errors")
}
if joined.Error() == "" {
t.Fatal("joined error should have message")
}
}
func TestJoinedError_Format_MarshalError(t *testing.T) {
err := NewCode(100001, "错误A").WithFields(F("bad", unmarhalable{}))
joined := JoinXErrors(err)
// %+v 应不 panic 且包含栈信息
trace := fmt.Sprintf("%+v", joined)
if len(trace) == 0 {
t.Fatal("stack trace should not be empty")
}
}
func TestJoinedError_MarshalJSON(t *testing.T) {
err1 := NewCode(100001, "错误A")
err2 := NewCode(100002, "错误B")
joined := JoinXErrors(err1, err2).WithFields(F("key1", "val1"))
bs, err := json.Marshal(joined)
if err != nil {
t.Fatalf("MarshalJSON failed: %v", err)
}
var result map[string]any
if err := json.Unmarshal(bs, &result); err != nil {
t.Fatalf("unmarshal failed: %v", err)
}
if int(result["code"].(float64)) != 100001 {
t.Fatalf("expected code 100001, got %v", result["code"])
}
if result["message"] != "错误A" {
t.Fatalf("expected message '错误A', got %v", result["message"])
}
errsArr, ok := result["errors"].([]any)
if !ok || len(errsArr) != 2 {
t.Fatalf("expected 2 errors, got %v", result["errors"])
}
if _, ok := result["fields"]; !ok {
t.Fatal("fields should be present in JSON")
}
// 无 fields 的 joined error
joined2 := JoinXErrors(err1)
bs2, _ := json.Marshal(joined2)
var result2 map[string]any
json.Unmarshal(bs2, &result2)
if _, ok := result2["fields"]; ok {
t.Fatal("fields should not be present when empty")
}
}
func TestJoinedError_ErrorsMethod(t *testing.T) {
err1 := NewCode(100001, "错误A")
err2 := NewCode(100002, "错误B")
joined := JoinXErrors(err1, err2)
je, ok := joined.(*xJoinedError)
if !ok {
t.Fatal("expected *xJoinedError type")
}
errs := je.Errors()
if len(errs) != 2 {
t.Fatalf("expected 2 errors, got %d", len(errs))
}
}
func TestJoinedError_Unwrap(t *testing.T) {
err1 := NewCode(100001, "错误A")
err2 := NewCode(100002, "错误B")
joined := JoinXErrors(err1, err2)
// Unwrap 返回第一个子错误(单链遍历)
unwrapped := joined.Unwrap()
if unwrapped == nil {
t.Fatal("Unwrap should return first error")
}
}
type unmarhalable struct{}
func (unmarhalable) MarshalJSON() ([]byte, error) {
return nil, fmt.Errorf("cannot marshal")
}
func TestWithHttpStatus(t *testing.T) {
err := NewCode(100001, "测试错误")
if err.HttpStatus() != 500 {
t.Fatalf("expected default httpStatus 500, got %d", err.HttpStatus())
}
modified := err.WithHttpStatus(400)
if modified.HttpStatus() != 400 {
t.Fatalf("expected httpStatus 400, got %d", modified.HttpStatus())
}
if modified.ErrorCode() != 100001 {
t.Fatalf("code should be preserved, got %d", modified.ErrorCode())
}
if modified.Message() != "测试错误" {
t.Fatalf("message should be preserved, got %q", modified.Message())
}
// 原始不受影响
if err.HttpStatus() != 500 {
t.Fatal("original error should not be modified")
}
// JoinedError
err1 := NewCode(100001, "A")
err2 := NewCode(100002, "B")
joined := JoinXErrors(err1, err2)
modifiedJoined := joined.WithHttpStatus(403)
if modifiedJoined.HttpStatus() != 403 {
t.Fatalf("expected joined httpStatus 403, got %d", modifiedJoined.HttpStatus())
}
}
func TestCollectCodes(t *testing.T) {
// nil
if codes := CollectCodes(nil); codes != nil {
t.Fatalf("expected nil, got %v", codes)
}
// 单个 XError
err := NewCode(100001, "A")
codes := CollectCodes(err)
if len(codes) != 1 || codes[0] != 100001 {
t.Fatalf("expected [100001], got %v", codes)
}
// Wrap 链
inner := NewCode(100001, "A")
outer := Wrap(inner, "B")
codes = CollectCodes(outer)
if len(codes) != 1 || codes[0] != 100001 {
t.Fatalf("expected [100001], got %v", codes)
}
// 多层 Wrap 不同 code
e1 := NewCode(100001, "A")
e2 := WrapWithCode(e1, 100002)
codes = CollectCodes(e2)
if len(codes) != 2 {
t.Fatalf("expected 2 codes, got %v", codes)
}
// JoinedError
j1 := NewCode(100001, "A")
j2 := NewCode(100002, "B")
joined := JoinXErrors(j1, j2)
codes = CollectCodes(joined)
if len(codes) != 2 {
t.Fatalf("expected 2 codes, got %v", codes)
}
// 去重
j3 := NewCode(100001, "C")
joined2 := JoinXErrors(j1, j3)
codes = CollectCodes(joined2)
if len(codes) != 1 || codes[0] != 100001 {
t.Fatalf("expected deduplicated [100001], got %v", codes)
}
// CodeNone 不收集
plainErr := New("no code")
codes = CollectCodes(plainErr)
if len(codes) != 0 {
t.Fatalf("CodeNone should not be collected, got %v", codes)
}
// plain error
codes = CollectCodes(errors.New("plain"))
if len(codes) != 0 {
t.Fatalf("plain error should have no codes, got %v", codes)
}
}
func TestCollectCodes_XCode(t *testing.T) {
err := NewXCode(xcode.InternalServerError)
codes := CollectCodes(err)
if len(codes) != 1 || codes[0] != 500 {
t.Fatalf("expected [500], got %v", codes)
}
}
func TestJoinedError_ErrorsAs_TraversesChildren(t *testing.T) {
// 验证 errors.As 能遍历 xJoinedError 的所有子错误
err1 := errors.New("plain error")
err2 := NewCode(100002, "B")
joined := JoinErrors(err1, err2)
// 应能找到第二个子错误中的 XError
var xe XError
if !errors.As(joined, &xe) {
t.Fatal("errors.As should find XError in joined children")
}
if xe.ErrorCode() != 100002 {
t.Fatalf("expected code 100002, got %d", xe.ErrorCode())
}
}
func TestJoinedError_ErrorsAs_DeepNested(t *testing.T) {
// 多层嵌套的 As 遍历
err1 := errors.New("plain")
err2 := &testCustomErr{msg: "custom"}
joined := JoinErrors(err1, err2)
var ce *testCustomErr
if !errors.As(joined, &ce) {
t.Fatal("errors.As should find customErr in non-first child of joined error")
}
if ce.msg != "custom" {
t.Fatalf("expected 'custom', got %q", ce.msg)
}
}
type testCustomErr struct{ msg string }
func (e *testCustomErr) Error() string { return e.msg }