-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathresult_test.go
391 lines (344 loc) · 9.27 KB
/
result_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
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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
package sol
import (
"fmt"
"reflect"
"testing"
"time"
)
// mock values
var (
mockInt = int64(1)
mockStr = "a"
mockBool = true
mockFloat = 1.1
mockTime = time.Date(2015, 3, 1, 0, 0, 0, 0, time.UTC)
)
// mock is a mock Scanner used only for testing. It returns an example
// value for every supported type
type mock struct {
columns []string
counter, total int
}
var _ Scanner = &mock{}
func (mock mock) Close() error { return nil }
func (mock mock) Columns() ([]string, error) {
return mock.columns, nil
}
func (mock mock) Err() error { return nil }
func (mock *mock) Next() bool {
if mock.counter < mock.total {
mock.counter += 1
return true
}
return false
}
func (mock mock) Scan(dests ...interface{}) error {
if len(dests) != len(mock.columns) {
return fmt.Errorf(
"Unequal number of scanner destinations (%d) for columns (%d)",
len(dests), len(mock.columns),
)
}
for i, dest := range dests {
v := reflect.ValueOf(dest)
if v.Kind() != reflect.Ptr {
return fmt.Errorf(
"Scan error on column %d: destination (%T) is not a pointer",
i, v.Kind(),
)
}
v = reflect.Indirect(v)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v.SetInt(int64(mock.counter)) // Test increments
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
v.SetUint(uint64(mockInt))
case reflect.String:
v.SetString(mockStr)
case reflect.Bool:
v.SetBool(mockBool)
case reflect.Float32, reflect.Float64:
v.SetFloat(mockFloat)
case reflect.Interface, reflect.Ptr:
// Used by Values
switch mock.columns[i] {
case "int":
v.Set(reflect.ValueOf(int64(mock.counter))) // Test increments
case "str":
v.Set(reflect.ValueOf(mockStr))
case "bool":
v.Set(reflect.ValueOf(mockBool))
case "float":
v.Set(reflect.ValueOf(mockFloat))
case "time":
v.Set(reflect.ValueOf(mockTime))
}
case reflect.Struct:
switch dest.(type) {
case time.Time, *time.Time:
v.Set(reflect.ValueOf(mockTime))
}
}
}
return nil
}
func mockResult(total int, columns ...string) Result {
return Result{Scanner: &mock{total: total, columns: columns}}
}
func TestMock(t *testing.T) {
example := mockResult(1, "int", "str", "bool", "float", "time")
example.Next()
var num int64
var str string
var boolean bool
var float float64
var timestamp time.Time
example.Scanner.Scan(&num, &str, &boolean, &float, ×tamp)
if num != mockInt {
t.Errorf("Unequal mock int: have %d, want %d", num, mockInt)
}
if str != mockStr {
t.Errorf("Unequal mock str: have %s, want %s", str, mockStr)
}
if boolean != mockBool {
t.Errorf("Unequal mock bool: have %t, want %t", boolean, mockBool)
}
if float != mockFloat {
t.Errorf("Unequal mock float: have %f, want %f", float, mockFloat)
}
if !timestamp.Equal(mockTime) {
t.Errorf("Unequal mock time: have %v, want %v", timestamp, mockTime)
}
}
func TestResult_One(t *testing.T) {
var zero, one, two Result // Example results
var values Values
zero = mockResult(0, "int")
if err := zero.One(values); err == nil {
t.Errorf("Zero results should error with Result.One")
}
one = mockResult(1, "int")
if err := one.One(values); err == nil {
t.Errorf("Results.One should error when given an uninitialized map")
}
values = Values{}
one = mockResult(1, "int", "str") // Reset
if err := one.One(values); err != nil {
t.Errorf(
"Result.One should not error when given a Values type: %s",
err,
)
}
expected := Values{"int": mockInt, "str": mockStr}
if !reflect.DeepEqual(expected, values) {
t.Errorf("Unequal Values: %+v != %+v", expected, values)
}
values = Values{}
one = mockResult(1, "int", "str") // Reset
if err := one.One(&values); err == nil {
t.Error("Result.One should error when given a *Values type")
}
one = mockResult(1, "int") // Reset
ID := struct {
ID int64
}{}
if err := one.One(ID); err == nil {
t.Errorf("Result.One should error when given a struct type")
}
one = mockResult(1, "int") // Reset
if err := one.One(&ID); err != nil {
t.Errorf("Result.One should not error when given a *struct type")
}
if ID.ID != mockInt {
t.Errorf("Unequal int: have %d, want %d", ID.ID, mockInt)
}
// Match misaligned fields
two = mockResult(2, "user_id", "is_admin", "str")
user := struct {
UserID int64
Email string
IsAdmin bool
}{}
if err := two.One(&user); err != nil {
t.Errorf("Result.One should not error when given a struct dest")
}
if user.UserID != mockInt {
t.Errorf("Unequal int: have %d, want %d", user.UserID, mockInt)
}
if !user.IsAdmin {
t.Errorf("Unequal bool: have %t, want %t", user.IsAdmin, mockBool)
}
// Single addr dest
var id int64
one = mockResult(1, "int") // Reset
if err := one.One(&id); err != nil {
t.Errorf(
"Single column results should not error when given a single dest",
)
}
if id != mockInt {
t.Errorf("Unequal int from single addr: have %d, want %d", id, mockInt)
}
two = mockResult(2, "int", "str")
if err := two.One(&id); err == nil {
t.Errorf("Result with multiple columns should error when given a single dest")
}
}
func TestResult_oneStruct(t *testing.T) {
one := mockResult(1, "name", "created")
type Timestamp struct {
Created time.Time
}
type thing struct {
Name string
Timestamp
}
var have thing
if err := one.One(&have); err != nil {
t.Errorf("Result.oneStruct should not error when scanning: %s", err)
}
want := thing{
Name: mockStr,
Timestamp: Timestamp{Created: mockTime},
}
if !reflect.DeepEqual(want, have) {
t.Errorf(
"unexpected Result.oneStruct values: want %+v, have %+v",
want, have,
)
}
}
func TestResult_All(t *testing.T) {
var zero, one, two Result // Example results
// Scan into values
var values []Values
zero = mockResult(0, "int")
if err := zero.All(&values); err != nil {
t.Errorf("Zero results should not error with Result.All: %s", err)
}
two = mockResult(2, "int", "str")
if err := two.All(values); err == nil {
t.Errorf("Result.All should error when given a non-pointer")
}
two = mockResult(2, "int", "str") // Reset
if err := two.All(&values); err != nil {
t.Errorf(
"Result.All should not error when scanned into []Values: %s",
err,
)
}
wantValues := []Values{
{"int": int64(1), "str": mockStr},
{"int": int64(2), "str": mockStr},
}
if !reflect.DeepEqual(values, wantValues) {
t.Errorf("Unequal Values slice: want %v, have %v", wantValues, values)
}
// Scan into structs
two = mockResult(2, "user_id", "is_admin", "str")
type user struct {
UserID int64
Email string
IsAdmin bool
}
var users []user
if err := two.All(users); err == nil {
t.Errorf("Result.All should error when scanned into a non-pointer")
}
if err := two.All(&users); err != nil {
t.Errorf(
"Result.All should not error when scanned into a slice of struct type: %s",
err,
)
}
wantUsers := []user{
{UserID: 1, IsAdmin: true},
{UserID: 2, IsAdmin: true},
}
if !reflect.DeepEqual(users, wantUsers) {
t.Errorf("Unequal struct slices: want %v, have %v", wantUsers, users)
}
// Struct slices can also be pre-populated
users = []user{{UserID: 3, Email: "[email protected]"}}
two = mockResult(2, "user_id", "is_admin") // Reset
if err := two.All(&users); err != nil {
t.Errorf(
"Result.All should not error when scanned into a pre-populated slice of structs: %s",
err,
)
}
wantUsers = []user{
{UserID: 1, Email: "[email protected]", IsAdmin: true},
{UserID: 2, IsAdmin: true},
}
if !reflect.DeepEqual(users, wantUsers) {
t.Errorf("Unequal struct slices: want %v, have %v", wantUsers, users)
}
// Scan into a deeply nested struct
one = mockResult(1, "id", "CreatedAt", "Value")
var nested []embedded
if err := one.All(&nested); err != nil {
t.Errorf(
"Result.All should not error when scanned a nested struct: %s",
err,
)
}
wantNested := embedded{}
wantNested.Serial.ID = mockInt
wantNested.Timestamp.CreatedAt = mockTime
wantNested.Metadata = metadata{}
wantNested.Deep.Level2.Level3.Value = mockBool
if !reflect.DeepEqual(nested, []embedded{wantNested}) {
t.Errorf(
"Unequal nested struct: want %v, have %v",
[]embedded{wantNested},
nested,
)
}
}
func TestResult_allNative(t *testing.T) {
single := mockResult(2, "int")
var have []int
if err := single.All(&have); err != nil {
t.Errorf("Result.allNative should not error when scanning: %s", err)
}
want := []int{1, 2}
if !reflect.DeepEqual(want, have) {
t.Errorf("Unequal []int: want %v, have %v", want, have)
}
// Reset and try with multiple results
multi := mockResult(2, "int", "str")
var wrong []int
if err := multi.All(&wrong); err == nil {
t.Errorf("Result.All should error with an unmatched type: %s", err)
}
}
// Benchmark the scanning of multiple results into a slice of structs
func BenchmarkResults_allStruct(b *testing.B) {
var results Result
type user struct {
UserID int64
Email string
IsAdmin bool
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var users []user
results = mockResult(10, "user_id", "is_admin", "str")
results.All(&users)
}
}
func BenchmarkResults_allMap(b *testing.B) {
var results Result
type user struct {
UserID int64
Email string
IsAdmin bool
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var values []Values
results = mockResult(10, "int", "time", "str")
results.All(&values)
}
}