forked from rzajac/zltest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
entry.go
276 lines (252 loc) · 7.15 KB
/
entry.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
package zltest
import (
"fmt"
"math"
"strconv"
"time"
"github.com/rs/zerolog"
)
// KeyStatus represents a status of searching and deserialization
// of a key in log entry.
type KeyStatus string
const (
// KeyFound is used when Key found successfully.
KeyFound KeyStatus = "KeyFound"
// KeyBadType is used when Key found but it's not of expected type.
KeyBadType KeyStatus = "KeyBadType"
// KeyMissing is used when Key is not in the log entry.
KeyMissing KeyStatus = "KeyMissing"
// KeyBadFormat is used when Key found but its format is wrong.
KeyBadFormat KeyStatus = "KeyBadFormat"
)
// Entry represents zerolog log entry.
type Entry struct {
raw string // Entry as it was written to the writer.
m map[string]interface{} // JSON decoded log entry.
t T // Test manager.
}
// String implements fmt.Stringer interface and returns log entry
// as it was written to the writer.
func (ent *Entry) String() string {
return ent.raw
}
// Str returns log entry key as a string.
func (ent *Entry) Str(key string) (string, KeyStatus) {
if itf, ok := ent.m[key]; ok {
if got, ok := itf.(string); ok {
return got, KeyFound
}
return "", KeyBadType
}
return "", KeyMissing
}
// Float64 returns log entry key as a float64.
func (ent *Entry) Float64(key string) (float64, KeyStatus) {
if itf, ok := ent.m[key]; ok {
if got, ok := itf.(float64); ok {
return got, KeyFound
}
return 0, KeyBadType
}
return 0, KeyMissing
}
// Bool returns log entry key as a boolean.
func (ent *Entry) Bool(key string) (bool, KeyStatus) {
if itf, ok := ent.m[key]; ok {
if got, ok := itf.(bool); ok {
return got, KeyFound
}
return false, KeyBadType
}
return false, KeyMissing
}
// Time returns log entry key as a time.Time. It uses zerolog.TimeFieldFormat
// to parse the time string representation.
func (ent *Entry) Time(key string) (time.Time, KeyStatus) {
if itf, ok := ent.m[key]; ok {
if got, ok := itf.(string); ok {
tim, err := time.Parse(zerolog.TimeFieldFormat, got)
if err != nil {
return time.Time{}, KeyBadFormat
}
return tim, KeyFound
}
return time.Time{}, KeyBadType
}
return time.Time{}, KeyMissing
}
// ExpStr tests log entry has key, its value is a string and it's equal to exp.
func (ent *Entry) ExpStr(key string, exp string) {
ent.t.Helper()
if err := ent.expStr(key, exp); err != "" {
ent.t.Error(err)
}
}
func (ent *Entry) expStr(key string, exp string) string {
ent.t.Helper()
got, status := ent.Str(key)
if status == KeyFound {
if got != exp {
return fmt.Sprintf(
"expected entry key '%s' to have value '%s' but got '%s'",
key,
exp,
got,
)
}
return ""
}
return formatError(status, key, "string")
}
// ExpTime tests log entry has key, its value is a string representing time in
// zerolog.TimeFieldFormat and it's equal to exp.
func (ent *Entry) ExpTime(key string, exp time.Time) {
ent.t.Helper()
if err := ent.expTime(key, exp); err != "" {
ent.t.Error(err)
}
}
func (ent *Entry) expTime(key string, exp time.Time) string {
ent.t.Helper()
got, status := ent.Time(key)
if status == KeyFound {
if !exp.Equal(got) {
return fmt.Sprintf("expected entry '%s' to be '%s' but is '%s'",
key,
exp.Format(zerolog.TimeFieldFormat),
got.Format(zerolog.TimeFieldFormat),
)
}
return ""
}
return formatError(status, key, "string")
}
// ExpDur tests log entry has key and its value is equal to exp time.Duration.
// The duration vale in the entry is multiplied by zerolog.DurationFieldUnit
// before the comparison.
func (ent *Entry) ExpDur(key string, exp time.Duration) {
ent.t.Helper()
if err := ent.expDur(key, exp); err != "" {
ent.t.Error(err)
}
}
func (ent *Entry) expDur(key string, exp time.Duration) string {
ent.t.Helper()
got, status := ent.Float64(key)
if status == KeyFound {
gotD := time.Duration(int(got)) * zerolog.DurationFieldUnit
if gotD != exp {
return fmt.Sprintf(
"expected entry key '%s' to have value '%d' (%s) but got '%d' (%s)",
key,
exp/zerolog.DurationFieldUnit,
exp.String(),
gotD/zerolog.DurationFieldUnit,
gotD.String(),
)
}
return ""
}
return formatError(status, key, "number")
}
// ExpBool tests log entry has a key, its value is boolean and equal to exp.
func (ent *Entry) ExpBool(key string, exp bool) {
ent.t.Helper()
if err := ent.expBool(key, exp); err != "" {
ent.t.Error(err)
}
}
func (ent *Entry) expBool(key string, exp bool) string {
ent.t.Helper()
got, status := ent.Bool(key)
if status == KeyFound {
if got != exp {
return fmt.Sprintf(
"expected entry key '%s' to have value '%v' but got '%v'",
key,
exp,
got,
)
}
return ""
}
return formatError(status, key, "bool")
}
// ExpLoggedWithin tests log entry was logged at exp time. The actual time
// may be within +/- diff.
func (ent *Entry) ExpLoggedWithin(exp time.Time, diff time.Duration) {
ent.ExpTimeWithin(zerolog.TimestampFieldName, exp, diff)
}
// ExpTimeWithin tests log entry has key, its value is a string representing
// time in zerolog.TimeFieldFormat and it's equal to exp time. The actual time
// may be within +/- diff.
func (ent *Entry) ExpTimeWithin(key string, exp time.Time, diff time.Duration) {
ent.t.Helper()
got, status := ent.Time(key)
if status == KeyFound {
gotD := math.Abs(float64(exp.Sub(got)))
if gotD > float64(diff) {
ent.t.Errorf("expected entry '%s' to be within '%s' but difference is '%s'",
key,
diff.String(),
time.Duration(gotD).String(),
)
}
return
}
ent.t.Error(formatError(status, key, "string"))
}
// ExpMsg tests log entry message field (zerolog.MessageFieldName) is
// equal to exp.
func (ent *Entry) ExpMsg(exp string) {
ent.ExpStr(zerolog.MessageFieldName, exp)
}
func (ent *Entry) expMsg(exp string) string {
return ent.expStr(zerolog.MessageFieldName, exp)
}
// ExpLevel tests log entry level field (zerolog.LevelFieldName) is
// equal to exp.
func (ent *Entry) ExpLevel(exp zerolog.Level) {
ent.ExpStr(zerolog.LevelFieldName, exp.String())
}
func (ent *Entry) expLevel(exp zerolog.Level) string {
return ent.expStr(zerolog.LevelFieldName, exp.String())
}
// ExpNum tests log entry has key and its numerical value is equal to exp.
func (ent *Entry) ExpNum(key string, exp float64) {
ent.t.Helper()
if err := ent.expNum(key, exp); err != "" {
ent.t.Error(err)
}
}
func (ent *Entry) expNum(key string, exp float64) string {
ent.t.Helper()
got, status := ent.Float64(key)
if status == KeyFound {
if got != exp {
expS := strconv.FormatFloat(exp, 'f', -1, 64)
gotS := strconv.FormatFloat(got, 'f', -1, 64)
return fmt.Sprintf(
"expected entry key '%s' to have value '%s' but got '%s'",
key,
expS,
gotS,
)
}
return ""
}
return formatError(status, key, "number")
}
// formatError formats error message based on status of log entry key search.
func formatError(status KeyStatus, key, typ string) string {
switch status {
case KeyMissing:
return fmt.Sprintf("expected entry to have key '%s'", key)
case KeyBadType:
return fmt.Sprintf("expected entry key '%s' to be '%s'", key, typ)
case KeyBadFormat:
return fmt.Sprintf("key '%s' in a wrong format", key)
default:
return fmt.Sprintf("invalid KeyStatus '%s'", status)
}
}