-
Notifications
You must be signed in to change notification settings - Fork 0
/
error_test.go
291 lines (273 loc) · 8.54 KB
/
error_test.go
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
package gdk
import (
"encoding/json"
"fmt"
"reflect"
"testing"
)
func TestErrorCause(t *testing.T) {
t.Run("语法检测", func(t *testing.T) {
f := func() error {
return ErrorCause(fmt.Errorf("error"))
}
err := f()
if err == nil {
t.Errorf("got nil, want error")
}
if err.Error() != "error" {
t.Errorf("got %v, want error", err.Error())
}
err2 := ErrorCause(err)
if err2 == nil {
t.Errorf("got nil, want error")
}
if err2.Error() != "error" {
t.Errorf("got %v, want error", err2.Error())
}
// 检测 err 是不是 error类型
if _, ok := err.(error); !ok {
t.Errorf("got false, want true, err")
}
// err 同时实现了 Err 接口, 所以应该也是Err 类型
if _, ok := err.(Err); !ok {
t.Errorf("got false, want true, Err")
}
// 检测err对象是否实现了WithTags(string)Err方法
// 因为err实现了Err接口, 所以它应该实现了Err接口中的WithTags方法
if _, ok := err.(interface {
WithTag(string) Err
}); !ok {
t.Errorf("got false, want true, WithTag")
}
})
}
func foo(i int) error {
if i == 0 {
return ErrorCause(fmt.Errorf("testError"))
}
return foo(i - 1)
}
func TestIs(t *testing.T) {
var (
ERR_NUM_INVALID = 1000
ERR_TEST_CODE = 1001
)
err := ErrorCause(fmt.Errorf("error")).WithCode(ERR_NUM_INVALID)
if !err.Is(ERR_NUM_INVALID) {
t.Errorf("got %v, want error.code=1000", err.Detail())
}
if err.Is(ERR_TEST_CODE) {
t.Errorf("got %v, want error.code !=1001", err.Detail())
}
}
func TestWithTag(t *testing.T) {
t.Run("Tag", func(t *testing.T) {
err := ErrorCause(fmt.Errorf("error")).WithTag("gdk").Export()
if err.Tag != "gdk" {
t.Errorf("got %v, want tag=gdk", err.Tag)
}
})
t.Run("Global Tag", func(t *testing.T) {
SetGlobalTag("globalGDK")
defer UnsetGlobals()
err := ErrorCause(fmt.Errorf("error")).WithTag("gdk").Export()
if err.Tag != "gdk" && err.GlobalTag != "globalGDK" {
t.Errorf("got tag=%v, globalTag=%v, want tag=gdk, globalTag=globalGDK", err.Tag, err.GlobalTag)
}
err1 := ErrorCause(fmt.Errorf("error")).Export()
if err1.Tag != "" && err1.GlobalTag != "globalGDK" {
t.Errorf("got tag=%v, globalTag=%v, want tag=, globalTag=globalGDK", err.Tag, err.GlobalTag)
}
})
}
func TestWithFields(t *testing.T) {
globalFields := map[string]interface{}{
"service": "job",
}
fields := map[string]interface{}{
"version": "1.0.0",
"build": 20231101,
}
t.Run("Fields", func(t *testing.T) {
err := ErrorCause(fmt.Errorf("error")).WithFields(fields).Export()
if !reflect.DeepEqual(err.Fields, fields) {
t.Errorf("got %v, want %v", err.Fields, fields)
}
})
t.Run("GlobalFields", func(t *testing.T) {
SetGlobalFields(globalFields)
defer UnsetGlobals()
err := ErrorCause(fmt.Errorf("error")).WithFields(fields).Export()
if !reflect.DeepEqual(err.Fields, fields) {
t.Errorf("got %v, want %v", err.Fields, fields)
}
if !reflect.DeepEqual(err.GlobalFields, globalFields) {
t.Errorf("got %v, want %v", err.GlobalFields, globalFields)
}
})
}
func TestWithCode(t *testing.T) {
t.Run("code", func(t *testing.T) {
ERR_INVALID := 1000
err := ErrorCause(fmt.Errorf("error")).WithCode(ERR_INVALID)
if !err.Is(ERR_INVALID) {
t.Errorf("got %v, want code=1000", err.Detail())
}
})
}
func TestError(t *testing.T) {
t.Run("error", func(t *testing.T) {
err := ErrorCause(fmt.Errorf("error"))
if err.Error() != "error" {
t.Errorf("got %v, want error", err.Error())
}
})
t.Run("递归错误", func(t *testing.T) {
err := foo(3)
if err == nil {
t.Errorf("got nil, want !=nil")
}
if err.Error() != "testError" {
t.Errorf("got %v, want testError", err.Error())
}
})
}
func TestExport(t *testing.T) {
t.Run("Export", func(t *testing.T) {
code := 1000
tag := "machine"
fields := map[string]interface{}{
"inputs": []int{1, 1, 2},
"outs": nil,
"location": "db connection refused",
}
err := ErrorCause(fmt.Errorf("error")).WithTag(tag).WithFields(fields).WithCode(code)
e := err.Export()
if e.Code != code {
t.Errorf("got %v, want %v", e.Code, code)
}
if e.Tag != tag {
t.Errorf("got %v, want %v", e.Tag, tag)
}
if !reflect.DeepEqual(e.Fields, fields) {
t.Errorf("got %v, want %v", e.Fields, fields)
}
})
}
func TestDetail(t *testing.T) {
t.Run("Detail", func(t *testing.T) {
code := 1000
tag := "machine"
fields := map[string]interface{}{
"inputs": []interface{}{"a", "b"}, // 注意这里不能使用[]string{"a","b"} 否则下面json.Unmarshal反序列化之后时[]interface{} 此时因为类型不同,DeepEqual()=false
"outs": "1011",
"location": "db connection refused",
}
err := ErrorCause(fmt.Errorf("error")).WithTag(tag).WithFields(fields).WithCode(code)
d := err.Detail()
var e ErrDetail
ee := json.Unmarshal([]byte(d), &e)
//decoder := json.NewDecoder(bytes.NewReader([]byte(d)))
//// 未设置UseNumber, 长整型会丢失精度
//decoder.UseNumber()
//ee := decoder.Decode(&e)
if ee != nil {
t.Errorf("json.Unmarshal error %v", ee)
}
if c, ok := e.Code.(float64); !ok {
t.Log("type:", reflect.TypeOf(e.Code))
t.Errorf("got %v, want %v, ok %v", e.Code, code, ok)
} else if int(c) != code {
t.Errorf("got %v, want %v", e.Code, code)
}
if e.Tag != tag {
t.Errorf("got %v, want %v", e.Tag, tag)
}
if !reflect.DeepEqual(e.Fields, fields) {
t.Errorf("got %v, want %v", e.Fields, fields)
}
for k, v := range e.Fields {
if !reflect.DeepEqual(v, fields[k]) {
t.Errorf("key:%v, v:%v, want:%v, cmp:%v", k, v, fields[k], v == fields[k])
}
}
})
}
func TestErrorf(t *testing.T) {
t.Run("Errorf", func(t *testing.T) {
const ERR_INVALID = 1000
err := Errorf("error").WithCode(ERR_INVALID)
if !err.Is(ERR_INVALID) {
t.Errorf("got %v, want 1000", err.Detail())
}
if err.Error() != "error" {
t.Errorf("got %v, want error", err.Error())
}
})
}
func TestErrorT(t *testing.T) {
t.Run("ErrorT with code[int]", func(t *testing.T) {
const (
ERR_UNMARSHAL_INVALID = 1000
ERR_PARAMS_INVALID = 1001
)
errorTemplates := map[any]string{
ERR_UNMARSHAL_INVALID: "json.Unmarshal error %+v, inputs:%+v",
ERR_PARAMS_INVALID: "%v invalid",
}
SetGlobalErrorTemplates(errorTemplates)
err := ErrorT(ERR_PARAMS_INVALID, "stu.address")
if !err.Is(ERR_PARAMS_INVALID) {
t.Errorf("got %v, want %v", err.Detail(), ERR_PARAMS_INVALID)
}
if err.Error() != "stu.address invalid" {
t.Errorf("got %v, want %v", err.Error(), "stu.address invalid")
}
err2 := ErrorT(ERR_UNMARSHAL_INVALID, fmt.Errorf("cannot covert object into golang struct"), "{\"address\":\"china\",\"code\":1000\"}")
if !err2.Is(ERR_UNMARSHAL_INVALID) {
t.Errorf("got %v, want %v", err.Detail(), ERR_UNMARSHAL_INVALID)
}
want := "json.Unmarshal error cannot covert object into golang struct, inputs:{\"address\":\"china\",\"code\":1000\"}"
if err2.Error() != want {
t.Errorf("got %v, want %v", err2.Error(), want)
}
})
t.Run("ErrorT with code[string]", func(t *testing.T) {
const (
ERR_UNMARSHAL_INVALID = "ERR_UNMARSHAL_INVALID"
ERR_PARAMS_INVALID = "ERR_PARAMS_INVALID"
)
errorTemplates := map[any]string{
ERR_UNMARSHAL_INVALID: "json.Unmarshal error %+v, inputs:%+v",
ERR_PARAMS_INVALID: "%v invalid",
}
SetGlobalErrorTemplates(errorTemplates)
err := ErrorT(ERR_PARAMS_INVALID, "stu.address")
if !err.Is(ERR_PARAMS_INVALID) {
t.Errorf("got %v, want %v", err.Detail(), ERR_PARAMS_INVALID)
}
if err.Error() != "stu.address invalid" {
t.Errorf("got %v, want %v", err.Error(), "stu.address invalid")
}
err2 := ErrorT(ERR_UNMARSHAL_INVALID, fmt.Errorf("cannot covert object into golang struct"), "{\"address\":\"china\",\"code\":1000\"}")
if !err2.Is(ERR_UNMARSHAL_INVALID) {
t.Errorf("got %v, want %v", err.Detail(), ERR_UNMARSHAL_INVALID)
}
want := "json.Unmarshal error cannot covert object into golang struct, inputs:{\"address\":\"china\",\"code\":1000\"}"
if err2.Error() != want {
t.Errorf("got %v, want %v", err2.Error(), want)
}
})
}
func TestDetailText(t *testing.T) {
const ERR_PARSE_FAILED = "PARSE_FAILED"
_, e := json.Marshal(func() {})
SetGlobalTag("ip:192.168.1.12")
SetGlobalFields(map[string]interface{}{"build": 20231121})
defer UnsetGlobals()
err := ErrorCause(e).WithCode(ERR_PARSE_FAILED).WithTag("FunctionMarshal").WithFields(map[string]interface{}{"key": "value"})
want := `CallChains=TestDetailText, GlobalTag=ip:192.168.1.12, Tag=FunctionMarshal, GlobalFields={"build":20231121}, Fields={"key":"value"}, Code=PARSE_FAILED, Error=json: unsupported type: func() `
if err.DetailText() != want {
t.Errorf("got %v, want %v", err.DetailText(), want)
}
}