1 package exec
2
3 import (
4 "context"
5 "database/sql"
6 "encoding/json"
7 "fmt"
8 "strings"
9 "testing"
10 "time"
11
12 "github.com/DATA-DOG/go-sqlmock"
13 "github.com/stretchr/testify/suite"
14 )
15
16 var (
17 testAddr1 = "111 Test Addr"
18 testAddr2 = "211 Test Addr"
19 testName1 = "Test1"
20 testName2 = "Test2"
21 testPhone1 = "111-111-1111"
22 testPhone2 = "222-222-2222"
23 testAge1 int64 = 10
24 testAge2 int64 = 20
25 testByteSliceContent = "byte slice result"
26 otherAddr1 = "111 Test Addr Other"
27 otherAddr2 = "211 Test Addr Other"
28 otherName1 = "Test1 Other"
29 otherName2 = "Test2 Other"
30 )
31
32 type queryExecutorSuite struct {
33 suite.Suite
34 }
35
36 func (qes *queryExecutorSuite) TestWithError() {
37 type StructWithTags struct {
38 Address string `db:"address"`
39 Name string `db:"name"`
40 }
41
42 ctx := context.Background()
43 db, _, err := sqlmock.New()
44 qes.NoError(err)
45 expectedErr := fmt.Errorf("crud exec error")
46 e := newQueryExecutor(db, expectedErr, `SELECT * FROM "items"`)
47 var items []StructWithTags
48 qes.EqualError(e.ScanStructs(&items), expectedErr.Error())
49 qes.EqualError(e.ScanStructsContext(ctx, &items), expectedErr.Error())
50 found, err := e.ScanStruct(&StructWithTags{})
51 qes.EqualError(err, expectedErr.Error())
52 qes.False(found)
53 found, err = e.ScanStructContext(ctx, &StructWithTags{})
54 qes.EqualError(err, expectedErr.Error())
55 qes.False(found)
56 var vals []string
57 qes.EqualError(e.ScanVals(&vals), expectedErr.Error())
58 qes.EqualError(e.ScanValsContext(ctx, &vals), expectedErr.Error())
59 var val string
60 found, err = e.ScanVal(&val)
61 qes.EqualError(err, expectedErr.Error())
62 qes.False(found)
63 found, err = e.ScanValContext(ctx, &val)
64 qes.EqualError(err, expectedErr.Error())
65 qes.False(found)
66 }
67
68 func (qes *queryExecutorSuite) TestToSQL() {
69 db, _, err := sqlmock.New()
70 qes.NoError(err)
71
72 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
73 query, args, err := e.ToSQL()
74 qes.NoError(err)
75 qes.Equal(`SELECT * FROM "items"`, query)
76 qes.Empty(args)
77 }
78
79 func (qes *queryExecutorSuite) TestScanStructs_withTaggedFields() {
80 type StructWithTags struct {
81 Address string `db:"address"`
82 Name string `db:"name"`
83 }
84
85 db, mock, err := sqlmock.New()
86 qes.NoError(err)
87
88 mock.ExpectQuery(`SELECT \* FROM "items"`).
89 WithArgs().
90 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
91 AddRow(testAddr1, testName1).
92 AddRow(testAddr2, testName2),
93 )
94
95 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
96
97 var items []StructWithTags
98 qes.NoError(e.ScanStructs(&items))
99 qes.Equal([]StructWithTags{
100 {Address: testAddr1, Name: testName1},
101 {Address: testAddr2, Name: testName2},
102 }, items)
103 }
104
105 func (qes *queryExecutorSuite) TestScanStructs_withUntaggedFields() {
106 type StructWithNoTags struct {
107 Address string
108 Name string
109 }
110 db, mock, err := sqlmock.New()
111 qes.NoError(err)
112
113 mock.ExpectQuery(`SELECT \* FROM "items"`).
114 WithArgs().
115 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
116 AddRow(testAddr1, testName1).
117 AddRow(testAddr2, testName2))
118
119 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
120
121 var items []StructWithNoTags
122 qes.NoError(e.ScanStructs(&items))
123 qes.Equal([]StructWithNoTags{
124 {Address: testAddr1, Name: testName1},
125 {Address: testAddr2, Name: testName2},
126 }, items)
127 }
128
129 func (qes *queryExecutorSuite) TestScanStructs_withPointerFields() {
130 type StructWithPointerFields struct {
131 Str *string
132 Time *time.Time
133 Bool *bool
134 Int *int64
135 Float *float64
136 }
137 db, mock, err := sqlmock.New()
138 qes.NoError(err)
139 now := time.Now()
140 str1, str2 := "str1", "str2"
141 t := true
142 var i1, i2 int64 = 1, 2
143 f1, f2 := 1.1, 2.1
144 mock.ExpectQuery(`SELECT \* FROM "items"`).
145 WithArgs().
146 WillReturnRows(sqlmock.NewRows([]string{"str", "time", "bool", "int", "float"}).
147 AddRow(str1, now, true, i1, f1).
148 AddRow(str2, now, true, i2, f2).
149 AddRow(nil, nil, nil, nil, nil),
150 )
151
152 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
153
154 var items []StructWithPointerFields
155 qes.NoError(e.ScanStructs(&items))
156 qes.Equal([]StructWithPointerFields{
157 {Str: &str1, Time: &now, Bool: &t, Int: &i1, Float: &f1},
158 {Str: &str2, Time: &now, Bool: &t, Int: &i2, Float: &f2},
159 {},
160 }, items)
161 }
162
163 func (qes *queryExecutorSuite) TestScanStructs_withPrivateFields() {
164 type StructWithPrivateTags struct {
165 private string
166 Address string `db:"address"`
167 Name string `db:"name"`
168 }
169
170 db, mock, err := sqlmock.New()
171 qes.NoError(err)
172
173 mock.ExpectQuery(`SELECT \* FROM "items"`).
174 WithArgs().
175 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
176 AddRow(testAddr1, testName1).
177 AddRow(testAddr2, testName2),
178 )
179
180 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
181
182 var items []StructWithPrivateTags
183 qes.NoError(e.ScanStructs(&items))
184 qes.Equal([]StructWithPrivateTags{
185 {Address: testAddr1, Name: testName1},
186 {Address: testAddr2, Name: testName2},
187 }, items)
188 }
189
190 func (qes *queryExecutorSuite) TestScanStructs_pointers() {
191 type StructWithTags struct {
192 Address string `db:"address"`
193 Name string `db:"name"`
194 }
195
196 db, mock, err := sqlmock.New()
197 qes.NoError(err)
198
199 mock.ExpectQuery(`SELECT \* FROM "items"`).
200 WithArgs().
201 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
202 AddRow(testAddr1, testName1).
203 AddRow(testAddr2, testName2),
204 )
205
206 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
207
208 var items []*StructWithTags
209 qes.NoError(e.ScanStructs(&items))
210 qes.Equal([]*StructWithTags{
211 {Address: testAddr1, Name: testName1},
212 {Address: testAddr2, Name: testName2},
213 }, items)
214 }
215
216 func (qes *queryExecutorSuite) TestScanStructs_withIgnoredEmbeddedStruct() {
217 type StructWithTags struct {
218 Address string `db:"address"`
219 Name string `db:"name"`
220 }
221
222 type ComposedIgnoredStruct struct {
223 StructWithTags `db:"-"`
224 PhoneNumber string `db:"phone_number"`
225 Age int64 `db:"age"`
226 }
227
228 db, mock, err := sqlmock.New()
229 qes.NoError(err)
230
231 mock.ExpectQuery(`SELECT \* FROM "items"`).
232 WithArgs().
233 WillReturnRows(sqlmock.NewRows([]string{"phone_number", "age"}).
234 AddRow(testPhone1, testAge1).AddRow(testPhone2, testAge2),
235 )
236
237 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
238
239 var composed []ComposedIgnoredStruct
240 qes.NoError(e.ScanStructs(&composed))
241 qes.Equal([]ComposedIgnoredStruct{
242 {StructWithTags: StructWithTags{}, PhoneNumber: testPhone1, Age: testAge1},
243 {StructWithTags: StructWithTags{}, PhoneNumber: testPhone2, Age: testAge2},
244 }, composed)
245 }
246
247 func (qes *queryExecutorSuite) TestScanStructs_withEmbeddedStruct() {
248 type StructWithTags struct {
249 Address string `db:"address"`
250 Name string `db:"name"`
251 }
252 type ComposedStruct struct {
253 StructWithTags
254 PhoneNumber string `db:"phone_number"`
255 Age int64 `db:"age"`
256 }
257 db, mock, err := sqlmock.New()
258 qes.NoError(err)
259
260 mock.ExpectQuery(`SELECT \* FROM "items"`).
261 WithArgs().
262 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "phone_number", "age"}).
263 AddRow(testAddr1, testName1, testPhone1, testAge1).
264 AddRow(testAddr2, testName2, testPhone2, testAge2),
265 )
266
267 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
268
269 var composed []ComposedStruct
270 qes.NoError(e.ScanStructs(&composed))
271 qes.Equal([]ComposedStruct{
272 {StructWithTags: StructWithTags{Address: testAddr1, Name: testName1}, PhoneNumber: testPhone1, Age: testAge1},
273 {StructWithTags: StructWithTags{Address: testAddr2, Name: testName2}, PhoneNumber: testPhone2, Age: testAge2},
274 }, composed)
275 }
276
277 func (qes *queryExecutorSuite) TestScanStructs_pointersWithEmbeddedStruct() {
278 type StructWithTags struct {
279 Address string `db:"address"`
280 Name string `db:"name"`
281 }
282 type ComposedStruct struct {
283 StructWithTags
284 PhoneNumber string `db:"phone_number"`
285 Age int64 `db:"age"`
286 }
287
288 db, mock, err := sqlmock.New()
289 qes.NoError(err)
290
291 mock.ExpectQuery(`SELECT \* FROM "items"`).
292 WithArgs().
293 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "phone_number", "age"}).
294 AddRow(testAddr1, testName1, testPhone1, testAge1).
295 AddRow(testAddr2, testName2, testPhone2, testAge2),
296 )
297
298 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
299
300 var composed []*ComposedStruct
301 qes.NoError(e.ScanStructs(&composed))
302 qes.Equal([]*ComposedStruct{
303 {StructWithTags: StructWithTags{Address: testAddr1, Name: testName1}, PhoneNumber: testPhone1, Age: testAge1},
304 {StructWithTags: StructWithTags{Address: testAddr2, Name: testName2}, PhoneNumber: testPhone2, Age: testAge2},
305 }, composed)
306 }
307
308 func (qes *queryExecutorSuite) TestScanStructs_pointersWithEmbeddedStructDuplicateFields() {
309 type StructWithTags struct {
310 Address string `db:"address"`
311 Name string `db:"name"`
312 }
313
314 type ComposedStructWithDuplicateFields struct {
315 StructWithTags
316 Address string `db:"other_address"`
317 Name string `db:"other_name"`
318 }
319
320 db, mock, err := sqlmock.New()
321 qes.NoError(err)
322
323 mock.ExpectQuery(`SELECT \* FROM "items"`).
324 WithArgs().
325 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "other_address", "other_name"}).
326 AddRow(testAddr1, testName1, otherAddr1, otherName1).
327 AddRow(testAddr2, testName2, otherAddr2, otherName2),
328 )
329
330 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
331
332 var composed []*ComposedStructWithDuplicateFields
333 qes.NoError(e.ScanStructs(&composed))
334 qes.Equal([]*ComposedStructWithDuplicateFields{
335 {
336 StructWithTags: StructWithTags{Address: testAddr1, Name: testName1},
337 Address: otherAddr1,
338 Name: otherName1,
339 },
340 {
341 StructWithTags: StructWithTags{Address: testAddr2, Name: testName2},
342 Address: otherAddr2,
343 Name: otherName2,
344 },
345 }, composed)
346 }
347
348 func (qes *queryExecutorSuite) TestScanStructs_pointersWithEmbeddedPointerDuplicateFields() {
349 type StructWithTags struct {
350 Address string `db:"address"`
351 Name string `db:"name"`
352 }
353
354 type ComposedWithWithPointerWithDuplicateFields struct {
355 *StructWithTags
356 Address string `db:"other_address"`
357 Name string `db:"other_name"`
358 }
359
360 db, mock, err := sqlmock.New()
361 qes.NoError(err)
362
363 mock.ExpectQuery(`SELECT \* FROM "items"`).
364 WithArgs().
365 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "other_address", "other_name"}).
366 AddRow(testAddr1, testName1, otherAddr1, otherName1).
367 AddRow(testAddr2, testName2, otherAddr2, otherName2),
368 )
369
370 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
371
372 var composed []*ComposedWithWithPointerWithDuplicateFields
373 qes.NoError(e.ScanStructs(&composed))
374 qes.Equal([]*ComposedWithWithPointerWithDuplicateFields{
375 {
376 StructWithTags: &StructWithTags{Address: testAddr1, Name: testName1},
377 Address: otherAddr1,
378 Name: otherName1,
379 },
380 {
381 StructWithTags: &StructWithTags{Address: testAddr2, Name: testName2},
382 Address: otherAddr2,
383 Name: otherName2,
384 },
385 }, composed)
386 }
387
388 func (qes *queryExecutorSuite) TestScanStructs_withIgnoredEmbeddedPointerStruct() {
389 type StructWithTags struct {
390 Address string `db:"address"`
391 Name string `db:"name"`
392 }
393
394 type ComposedIgnoredPointerStruct struct {
395 *StructWithTags `db:"-"`
396 PhoneNumber string `db:"phone_number"`
397 Age int64 `db:"age"`
398 }
399
400 db, mock, err := sqlmock.New()
401 qes.NoError(err)
402
403 mock.ExpectQuery(`SELECT \* FROM "items"`).
404 WithArgs().
405 WillReturnRows(sqlmock.NewRows([]string{"phone_number", "age"}).
406 AddRow(testPhone1, testAge1).
407 AddRow(testPhone2, testAge2),
408 )
409
410 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
411
412 var composed []ComposedIgnoredPointerStruct
413 qes.NoError(e.ScanStructs(&composed))
414 qes.Equal([]ComposedIgnoredPointerStruct{
415 {PhoneNumber: testPhone1, Age: testAge1},
416 {PhoneNumber: testPhone2, Age: testAge2},
417 }, composed)
418 }
419
420 func (qes *queryExecutorSuite) TestScanStructs_withEmbeddedStructPointer() {
421 type StructWithTags struct {
422 Address string `db:"address"`
423 Name string `db:"name"`
424 }
425
426 type ComposedWithPointerStruct struct {
427 *StructWithTags
428 PhoneNumber string `db:"phone_number"`
429 Age int64 `db:"age"`
430 }
431
432 db, mock, err := sqlmock.New()
433 qes.NoError(err)
434
435 mock.ExpectQuery(`SELECT \* FROM "items"`).
436 WithArgs().
437 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "phone_number", "age"}).
438 AddRow(testAddr1, testName1, testPhone1, testAge1).
439 AddRow(testAddr2, testName2, testPhone2, testAge2),
440 )
441
442 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
443
444 var composed []ComposedWithPointerStruct
445 qes.NoError(e.ScanStructs(&composed))
446 qes.Equal([]ComposedWithPointerStruct{
447 {StructWithTags: &StructWithTags{Address: testAddr1, Name: testName1}, PhoneNumber: testPhone1, Age: testAge1},
448 {StructWithTags: &StructWithTags{Address: testAddr2, Name: testName2}, PhoneNumber: testPhone2, Age: testAge2},
449 }, composed)
450 }
451
452 func (qes *queryExecutorSuite) TestScanStructs_pointersWithEmbeddedStructPointer() {
453 type StructWithTags struct {
454 Address string `db:"address"`
455 Name string `db:"name"`
456 }
457
458 type ComposedWithPointerStruct struct {
459 *StructWithTags
460 PhoneNumber string `db:"phone_number"`
461 Age int64 `db:"age"`
462 }
463 db, mock, err := sqlmock.New()
464 qes.NoError(err)
465
466 mock.ExpectQuery(`SELECT \* FROM "items"`).
467 WithArgs().
468 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "phone_number", "age"}).
469 AddRow(testAddr1, testName1, testPhone1, testAge1).
470 AddRow(testAddr2, testName2, testPhone2, testAge2),
471 )
472
473 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
474
475 var composed []*ComposedWithPointerStruct
476 qes.NoError(e.ScanStructs(&composed))
477 qes.Equal([]*ComposedWithPointerStruct{
478 {StructWithTags: &StructWithTags{Address: testAddr1, Name: testName1}, PhoneNumber: testPhone1, Age: testAge1},
479 {StructWithTags: &StructWithTags{Address: testAddr2, Name: testName2}, PhoneNumber: testPhone2, Age: testAge2},
480 }, composed)
481 }
482
483 func (qes *queryExecutorSuite) TestScanStructs_badValue() {
484 type StructWithTags struct {
485 Address string `db:"address"`
486 Name string `db:"name"`
487 }
488
489 tests := []struct {
490 name string
491 items interface{}
492 }{
493 {
494 name: "non-pointer items",
495 items: []StructWithTags{},
496 },
497 {
498 name: "non-slice items",
499 items: &StructWithTags{},
500 },
501 }
502 for i := range tests {
503 test := tests[i]
504 qes.Run(test.name, func() {
505 db, mock, err := sqlmock.New()
506 qes.NoError(err)
507 mock.ExpectQuery(`SELECT \* FROM "items"`).
508 WithArgs().
509 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
510 AddRow(testAddr1, testName1).AddRow(testAddr2, testName2),
511 )
512 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
513 qes.Equal(errUnsupportedScanStructsType, e.ScanStructs(test.items))
514 })
515 }
516 }
517
518 func (qes *queryExecutorSuite) TestScanStructs_queryError() {
519 type StructWithTags struct {
520 Address string `db:"address"`
521 Name string `db:"name"`
522 }
523
524 db, mock, err := sqlmock.New()
525 qes.NoError(err)
526
527 mock.ExpectQuery(`SELECT \* FROM "items"`).
528 WillReturnError(fmt.Errorf("queryExecutor error"))
529
530 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
531
532 var items []StructWithTags
533 qes.EqualError(e.ScanStructs(&items), "queryExecutor error")
534 }
535
536 func (qes *queryExecutorSuite) TestScanStructsContext_withTaggedFields() {
537 type StructWithTags struct {
538 Address string `db:"address"`
539 Name string `db:"name"`
540 }
541
542 ctx := context.Background()
543 db, mock, err := sqlmock.New()
544 qes.NoError(err)
545
546 mock.ExpectQuery(`SELECT \* FROM "items"`).
547 WithArgs().
548 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
549 AddRow(testAddr1, testName1).
550 AddRow(testAddr2, testName2),
551 )
552
553 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
554
555 var items []StructWithTags
556 qes.NoError(e.ScanStructsContext(ctx, &items))
557 qes.Equal([]StructWithTags{
558 {Address: testAddr1, Name: testName1},
559 {Address: testAddr2, Name: testName2},
560 }, items)
561 }
562
563 func (qes *queryExecutorSuite) TestScanStructsContext_withUntaggedFields() {
564 type StructWithNoTags struct {
565 Address string
566 Name string
567 }
568
569 ctx := context.Background()
570 db, mock, err := sqlmock.New()
571 qes.NoError(err)
572
573 mock.ExpectQuery(`SELECT \* FROM "items"`).
574 WithArgs().
575 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
576 AddRow(testAddr1, testName1).
577 AddRow(testAddr2, testName2),
578 )
579
580 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
581
582 var items []StructWithNoTags
583 qes.NoError(e.ScanStructsContext(ctx, &items))
584 qes.Equal([]StructWithNoTags{
585 {Address: testAddr1, Name: testName1},
586 {Address: testAddr2, Name: testName2},
587 }, items)
588 }
589
590 func (qes *queryExecutorSuite) TestScanStructsContext_withPointerFields() {
591 type StructWithPointerFields struct {
592 Address *string
593 Name *string
594 }
595 ctx := context.Background()
596 db, mock, err := sqlmock.New()
597 qes.NoError(err)
598
599 mock.ExpectQuery(`SELECT \* FROM "items"`).
600 WithArgs().
601 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
602 AddRow(testAddr1, testName1).
603 AddRow(testAddr2, testName2),
604 )
605
606 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
607
608 var items []StructWithPointerFields
609 qes.NoError(e.ScanStructsContext(ctx, &items))
610 qes.Equal([]StructWithPointerFields{
611 {Address: &testAddr1, Name: &testName1},
612 {Address: &testAddr2, Name: &testName2},
613 }, items)
614 }
615
616 func (qes *queryExecutorSuite) TestScanStructsContext_pointers() {
617 type StructWithTags struct {
618 Address string `db:"address"`
619 Name string `db:"name"`
620 }
621
622 ctx := context.Background()
623 db, mock, err := sqlmock.New()
624 qes.NoError(err)
625
626 mock.ExpectQuery(`SELECT \* FROM "items"`).
627 WithArgs().
628 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
629 AddRow(testAddr1, testName1).
630 AddRow(testAddr2, testName2),
631 )
632
633 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
634
635 var items []*StructWithTags
636 qes.NoError(e.ScanStructsContext(ctx, &items))
637 qes.Equal([]*StructWithTags{
638 {Address: testAddr1, Name: testName1},
639 {Address: testAddr2, Name: testName2},
640 }, items)
641 }
642
643 func (qes *queryExecutorSuite) TestScanStructsContext_withEmbeddedStruct() {
644 type StructWithTags struct {
645 Address string `db:"address"`
646 Name string `db:"name"`
647 }
648 type ComposedStruct struct {
649 StructWithTags
650 PhoneNumber string `db:"phone_number"`
651 Age int64 `db:"age"`
652 }
653 ctx := context.Background()
654 db, mock, err := sqlmock.New()
655 qes.NoError(err)
656
657 mock.ExpectQuery(`SELECT \* FROM "items"`).
658 WithArgs().
659 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "phone_number", "age"}).
660 AddRow(testAddr1, testName1, testPhone1, testAge1).
661 AddRow(testAddr2, testName2, testPhone2, testAge2),
662 )
663
664 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
665
666 var composed []ComposedStruct
667 qes.NoError(e.ScanStructsContext(ctx, &composed))
668 qes.Equal([]ComposedStruct{
669 {StructWithTags: StructWithTags{Address: testAddr1, Name: testName1}, PhoneNumber: testPhone1, Age: testAge1},
670 {StructWithTags: StructWithTags{Address: testAddr2, Name: testName2}, PhoneNumber: testPhone2, Age: testAge2},
671 }, composed)
672 }
673
674 func (qes *queryExecutorSuite) TestScanStructsContext_withIgnoredEmbeddedStruct() {
675 type StructWithTags struct {
676 Address string `db:"address"`
677 Name string `db:"name"`
678 }
679
680 type ComposedIgnoredStruct struct {
681 StructWithTags `db:"-"`
682 PhoneNumber string `db:"phone_number"`
683 Age int64 `db:"age"`
684 }
685
686 ctx := context.Background()
687 db, mock, err := sqlmock.New()
688 qes.NoError(err)
689
690 mock.ExpectQuery(`SELECT \* FROM "items"`).
691 WithArgs().
692 WillReturnRows(sqlmock.NewRows([]string{"phone_number", "age"}).
693 AddRow(testPhone1, testAge1).
694 AddRow(testPhone2, testAge2),
695 )
696
697 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
698
699 var composed []ComposedIgnoredStruct
700 qes.NoError(e.ScanStructsContext(ctx, &composed))
701 qes.Equal([]ComposedIgnoredStruct{
702 {StructWithTags: StructWithTags{}, PhoneNumber: testPhone1, Age: testAge1},
703 {StructWithTags: StructWithTags{}, PhoneNumber: testPhone2, Age: testAge2},
704 }, composed)
705 }
706
707 func (qes *queryExecutorSuite) TestScanStructsContext_pointersWithEmbeddedStruct() {
708 type StructWithTags struct {
709 Address string `db:"address"`
710 Name string `db:"name"`
711 }
712 type ComposedStruct struct {
713 StructWithTags
714 PhoneNumber string `db:"phone_number"`
715 Age int64 `db:"age"`
716 }
717 ctx := context.Background()
718 db, mock, err := sqlmock.New()
719 qes.NoError(err)
720
721 mock.ExpectQuery(`SELECT \* FROM "items"`).
722 WithArgs().
723 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "phone_number", "age"}).
724 AddRow(testAddr1, testName1, testPhone1, testAge1).
725 AddRow(testAddr2, testName2, testPhone2, testAge2),
726 )
727
728 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
729
730 var composed []*ComposedStruct
731 qes.NoError(e.ScanStructsContext(ctx, &composed))
732 qes.Equal([]*ComposedStruct{
733 {StructWithTags: StructWithTags{Address: testAddr1, Name: testName1}, PhoneNumber: testPhone1, Age: testAge1},
734 {StructWithTags: StructWithTags{Address: testAddr2, Name: testName2}, PhoneNumber: testPhone2, Age: testAge2},
735 }, composed)
736 }
737
738 func (qes *queryExecutorSuite) TestScanStructsContext_withEmbeddedStructPointer() {
739 type StructWithTags struct {
740 Address string `db:"address"`
741 Name string `db:"name"`
742 }
743
744 type ComposedWithPointerStruct struct {
745 *StructWithTags
746 PhoneNumber string `db:"phone_number"`
747 Age int64 `db:"age"`
748 }
749
750 ctx := context.Background()
751 db, mock, err := sqlmock.New()
752 qes.NoError(err)
753
754 mock.ExpectQuery(`SELECT \* FROM "items"`).
755 WithArgs().
756 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "phone_number", "age"}).
757 AddRow(testAddr1, testName1, testPhone1, testAge1).
758 AddRow(testAddr2, testName2, testPhone2, testAge2),
759 )
760
761 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
762
763 var composed []ComposedWithPointerStruct
764 qes.NoError(e.ScanStructsContext(ctx, &composed))
765 qes.Equal([]ComposedWithPointerStruct{
766 {StructWithTags: &StructWithTags{Address: testAddr1, Name: testName1}, PhoneNumber: testPhone1, Age: testAge1},
767 {StructWithTags: &StructWithTags{Address: testAddr2, Name: testName2}, PhoneNumber: testPhone2, Age: testAge2},
768 }, composed)
769 }
770
771 func (qes *queryExecutorSuite) TestScanStructsContext_pointersWithEmbeddedStructPointer() {
772 type StructWithTags struct {
773 Address string `db:"address"`
774 Name string `db:"name"`
775 }
776
777 type ComposedWithPointerStruct struct {
778 *StructWithTags
779 PhoneNumber string `db:"phone_number"`
780 Age int64 `db:"age"`
781 }
782
783 ctx := context.Background()
784 db, mock, err := sqlmock.New()
785 qes.NoError(err)
786
787 mock.ExpectQuery(`SELECT \* FROM "items"`).
788 WithArgs().
789 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "phone_number", "age"}).
790 AddRow(testAddr1, testName1, testPhone1, testAge1).
791 AddRow(testAddr2, testName2, testPhone2, testAge2),
792 )
793
794 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
795
796 var composed []*ComposedWithPointerStruct
797 qes.NoError(e.ScanStructsContext(ctx, &composed))
798 qes.Equal([]*ComposedWithPointerStruct{
799 {StructWithTags: &StructWithTags{Address: testAddr1, Name: testName1}, PhoneNumber: testPhone1, Age: testAge1},
800 {StructWithTags: &StructWithTags{Address: testAddr2, Name: testName2}, PhoneNumber: testPhone2, Age: testAge2},
801 }, composed)
802 }
803
804 func (qes *queryExecutorSuite) TestScanStructsContext_badValue() {
805 type StructWithTags struct {
806 Address string `db:"address"`
807 Name string `db:"name"`
808 }
809
810 tests := []struct {
811 name string
812 items interface{}
813 }{
814 {
815 name: "non-pointer items",
816 items: []StructWithTags{},
817 },
818 {
819 name: "non-slice items",
820 items: &StructWithTags{},
821 },
822 }
823 for i := range tests {
824 test := tests[i]
825 qes.Run(test.name, func() {
826 db, mock, err := sqlmock.New()
827 qes.NoError(err)
828 mock.ExpectQuery(`SELECT \* FROM "items"`).
829 WithArgs().
830 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
831 AddRow(testAddr1, testName1).AddRow(testAddr2, testName2),
832 )
833 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
834 qes.Equal(errUnsupportedScanStructsType, e.ScanStructsContext(context.Background(), test.items))
835 })
836 }
837 }
838
839 func (qes *queryExecutorSuite) TestScanStructsContext_queryError() {
840 type StructWithTags struct {
841 Address string `db:"address"`
842 Name string `db:"name"`
843 }
844
845 ctx := context.Background()
846 db, mock, err := sqlmock.New()
847 qes.NoError(err)
848
849 mock.ExpectQuery(`SELECT \* FROM "items"`).
850 WillReturnError(fmt.Errorf("queryExecutor error"))
851
852 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
853
854 var items []StructWithTags
855 qes.EqualError(e.ScanStructsContext(ctx, &items), "queryExecutor error")
856 }
857
858 func (qes *queryExecutorSuite) TestScanStruct() {
859 type StructWithNoTags struct {
860 Address string
861 Name string
862 }
863
864 type StructWithTags struct {
865 Address string `db:"address"`
866 Name string `db:"name"`
867 }
868
869 type ComposedStruct struct {
870 StructWithTags
871 PhoneNumber string `db:"phone_number"`
872 Age int64 `db:"age"`
873 }
874 type ComposedWithPointerStruct struct {
875 *StructWithTags
876 PhoneNumber string `db:"phone_number"`
877 Age int64 `db:"age"`
878 }
879
880 db, mock, err := sqlmock.New()
881 qes.NoError(err)
882
883 mock.ExpectQuery(`SELECT \* FROM "items"`).
884 WillReturnError(fmt.Errorf("queryExecutor error"))
885
886 mock.ExpectQuery(`SELECT \* FROM "items"`).
887 WithArgs().
888 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
889 AddRow(nil, nil),
890 )
891
892 mock.ExpectQuery(`SELECT \* FROM "items"`).
893 WithArgs().
894 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}))
895
896 mock.ExpectQuery(`SELECT \* FROM "items"`).
897 WithArgs().
898 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
899 AddRow(testAddr1, testName1),
900 )
901
902 mock.ExpectQuery(`SELECT \* FROM "items"`).
903 WithArgs().
904 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "phone_number", "age"}).
905 AddRow(testAddr1, testName1, testPhone1, testAge1),
906 )
907
908 mock.ExpectQuery(`SELECT \* FROM "items"`).
909 WithArgs().
910 WillReturnRows(sqlmock.NewRows([]string{"address", "name", "phone_number", "age"}).
911 AddRow(testAddr1, testName1, testPhone1, testAge1),
912 )
913
914 mock.ExpectQuery(`SELECT \* FROM "items"`).
915 WithArgs().
916 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).AddRow(testAddr1, testName1))
917
918 e := newQueryExecutor(db, nil, `SELECT * FROM "items"`)
919
920 var slicePtr []StructWithTags
921 var item StructWithTags
922 found, err := e.ScanStruct(item)
923 qes.Equal(errUnsupportedScanStructType, err)
924 qes.False(found)
925 found, err = e.ScanStruct(&slicePtr)
926 qes.Equal(errUnsupportedScanStructType, err)
927 qes.False(found)
928 found, err = e.ScanStruct(&item)
929 qes.EqualError(err, "queryExecutor error")
930 qes.False(found)
931
932 found, err = e.ScanStruct(&item)
933 qes.Error(err)
934 qes.False(found)
935
936 found, err = e.ScanStruct(&item)
937 qes.NoError(err)
938 qes.False(found)
939
940 found, err = e.ScanStruct(&item)
941 qes.NoError(err)
942 qes.True(found)
943 qes.Equal(StructWithTags{
944 Address: testAddr1,
945 Name: testName1,
946 }, item)
947
948 var composed ComposedStruct
949 found, err = e.ScanStruct(&composed)
950 qes.NoError(err)
951 qes.True(found)
952 qes.Equal(ComposedStruct{
953 StructWithTags: StructWithTags{Address: testAddr1, Name: testName1},
954 PhoneNumber: testPhone1,
955 Age: testAge1,
956 }, composed)
957
958 var embeddedPtr ComposedWithPointerStruct
959 found, err = e.ScanStruct(&embeddedPtr)
960 qes.NoError(err)
961 qes.True(found)
962 qes.Equal(ComposedWithPointerStruct{
963 StructWithTags: &StructWithTags{
964 Address: testAddr1,
965 Name: testName1,
966 },
967 PhoneNumber: testPhone1,
968 Age: testAge1,
969 }, embeddedPtr)
970
971 var noTag StructWithNoTags
972 found, err = e.ScanStruct(&noTag)
973 qes.NoError(err)
974 qes.True(found)
975 qes.Equal(StructWithNoTags{
976 Address: testAddr1,
977 Name: testName1,
978 }, noTag)
979 }
980
981 func (qes *queryExecutorSuite) TestScanStruct_taggedStructs() {
982 type StructWithNoTags struct {
983 Address string
984 Name string
985 }
986
987 type StructWithTags struct {
988 Address string `db:"address"`
989 Name string `db:"name"`
990 }
991
992 type ComposedStruct struct {
993 StructWithTags
994 PhoneNumber string `db:"phone_number"`
995 Age int64 `db:"age"`
996 }
997 type ComposedWithPointerStruct struct {
998 *StructWithTags
999 PhoneNumber string `db:"phone_number"`
1000 Age int64 `db:"age"`
1001 }
1002
1003 type StructWithTaggedStructs struct {
1004 NoTags StructWithNoTags `db:"notags"`
1005 Tags StructWithTags `db:"tags"`
1006 Composed ComposedStruct `db:"composedstruct"`
1007 ComposedPointer ComposedWithPointerStruct `db:"composedptrstruct"`
1008 }
1009
1010 db, mock, err := sqlmock.New()
1011 qes.NoError(err)
1012
1013 cols := []string{
1014 "notags.address", "notags.name",
1015 "tags.address", "tags.name",
1016 "composedstruct.address", "composedstruct.name", "composedstruct.phone_number", "composedstruct.age",
1017 "composedptrstruct.address", "composedptrstruct.name", "composedptrstruct.phone_number", "composedptrstruct.age",
1018 }
1019
1020 q := `SELECT` + strings.Join(cols, ", ") + ` FROM "items"`
1021
1022 mock.ExpectQuery(q).
1023 WithArgs().
1024 WillReturnRows(sqlmock.NewRows(cols).AddRow(
1025 testAddr1, testName1,
1026 testAddr2, testName2,
1027 testAddr1, testName1, testPhone1, testAge1,
1028 testAddr2, testName2, testPhone2, testAge2,
1029 ))
1030
1031 e := newQueryExecutor(db, nil, q)
1032
1033 var item StructWithTaggedStructs
1034 found, err := e.ScanStruct(&item)
1035 qes.NoError(err)
1036 qes.True(found)
1037 qes.Equal(StructWithTaggedStructs{
1038 NoTags: StructWithNoTags{Address: testAddr1, Name: testName1},
1039 Tags: StructWithTags{Address: testAddr2, Name: testName2},
1040 Composed: ComposedStruct{
1041 StructWithTags: StructWithTags{Address: testAddr1, Name: testName1},
1042 PhoneNumber: testPhone1,
1043 Age: testAge1,
1044 },
1045 ComposedPointer: ComposedWithPointerStruct{
1046 StructWithTags: &StructWithTags{Address: testAddr2, Name: testName2},
1047 PhoneNumber: testPhone2,
1048 Age: testAge2,
1049 },
1050 }, item)
1051 }
1052
1053 func (qes *queryExecutorSuite) TestScanVals() {
1054 db, mock, err := sqlmock.New()
1055 qes.NoError(err)
1056
1057 var id1, id2 int64 = 1, 2
1058
1059 mock.ExpectQuery(`SELECT "id" FROM "items"`).
1060 WillReturnError(fmt.Errorf("queryExecutor error"))
1061
1062 mock.ExpectQuery(`SELECT "id" FROM "items"`).
1063 WithArgs().
1064 WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(id1).RowError(0, fmt.Errorf("row error")))
1065
1066 mock.ExpectQuery(`SELECT "id" FROM "items"`).
1067 WithArgs().
1068 WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(id1).AddRow("a"))
1069
1070 mock.ExpectQuery(`SELECT "id" FROM "items"`).
1071 WithArgs().
1072 WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(id1).AddRow(id2))
1073
1074 mock.ExpectQuery(`SELECT "id" FROM "items"`).
1075 WithArgs().
1076 WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(id1).AddRow(id2))
1077
1078 e := newQueryExecutor(db, nil, `SELECT "id" FROM "items"`)
1079
1080 var ids []int64
1081 qes.EqualError(e.ScanVals(&ids), "queryExecutor error")
1082 qes.EqualError(e.ScanVals(&ids), "row error")
1083 qes.Error(e.ScanVals(&ids))
1084
1085 ids = ids[0:0]
1086 qes.NoError(e.ScanVals(&ids))
1087 qes.Equal(ids, []int64{id1, id2})
1088
1089 var pointers []*int64
1090 qes.NoError(e.ScanVals(&pointers))
1091 qes.Len(pointers, 2)
1092 qes.Equal(&id1, pointers[0])
1093 qes.Equal(&id2, pointers[1])
1094 }
1095
1096 func (qes *queryExecutorSuite) TestScanValsError() {
1097 var id int64
1098
1099 tests := []struct {
1100 name string
1101 items interface{}
1102 }{
1103 {
1104 name: "non-pointer items",
1105 items: []int64{},
1106 },
1107 {
1108 name: "non-slice items",
1109 items: &id,
1110 },
1111 }
1112 for i := range tests {
1113 test := tests[i]
1114 qes.Run(test.name, func() {
1115 db, mock, err := sqlmock.New()
1116 qes.NoError(err)
1117 mock.ExpectQuery(`SELECT "id" FROM "items"`).
1118 WithArgs().
1119 WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1).AddRow(2))
1120
1121 e := newQueryExecutor(db, nil, `SELECT "id" FROM "items"`)
1122 qes.Equal(errUnsupportedScanValsType, e.ScanVals(test.items))
1123 })
1124 }
1125 }
1126
1127 func (qes *queryExecutorSuite) TestScanVal() {
1128 db, mock, err := sqlmock.New()
1129 qes.NoError(err)
1130
1131 id1 := int64(1)
1132 mock.ExpectQuery(`SELECT "id" FROM "items"`).
1133 WillReturnError(fmt.Errorf("queryExecutor error"))
1134
1135 mock.ExpectQuery(`SELECT "id" FROM "items"`).
1136 WithArgs().
1137 WillReturnRows(sqlmock.NewRows([]string{"id"}).RowError(0, fmt.Errorf("row error")).AddRow(id1))
1138
1139 mock.ExpectQuery(`SELECT "id" FROM "items"`).
1140 WithArgs().
1141 WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("c"))
1142
1143 mock.ExpectQuery(`SELECT "id" FROM "items"`).
1144 WithArgs().
1145 WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(id1))
1146
1147 e := newQueryExecutor(db, nil, `SELECT "id" FROM "items"`)
1148
1149 var id int64
1150 var ids []int64
1151 found, err := e.ScanVal(id)
1152 qes.Equal(errScanValPointer, err)
1153 qes.False(found)
1154 found, err = e.ScanVal(&ids)
1155 qes.Equal(errScanValNonSlice, err)
1156 qes.False(found)
1157 found, err = e.ScanVal(&id)
1158 qes.EqualError(err, "queryExecutor error")
1159 qes.False(found)
1160
1161 found, err = e.ScanVal(&id)
1162 qes.EqualError(err, "row error")
1163 qes.False(found)
1164
1165 found, err = e.ScanVal(&id)
1166 qes.Error(err)
1167 qes.False(found)
1168
1169 var ptrID *int64
1170 found, err = e.ScanVal(&ptrID)
1171 qes.NoError(err)
1172 qes.True(found)
1173 qes.Equal(&id1, ptrID)
1174 }
1175
1176 func (qes *queryExecutorSuite) TestScanVal_withByteSlice() {
1177 db, mock, err := sqlmock.New()
1178 qes.NoError(err)
1179
1180 mock.ExpectQuery(`SELECT "name" FROM "items"`).
1181 WithArgs().
1182 WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow(testByteSliceContent))
1183
1184 e := newQueryExecutor(db, nil, `SELECT "name" FROM "items"`)
1185
1186 var bytes []byte
1187 found, err := e.ScanVal(bytes)
1188 qes.Equal(errScanValPointer, err)
1189 qes.False(found)
1190
1191 found, err = e.ScanVal(&bytes)
1192 qes.NoError(err)
1193 qes.True(found)
1194 qes.Equal([]byte(testByteSliceContent), bytes)
1195 }
1196
1197 func (qes *queryExecutorSuite) TestScanVal_withRawBytes() {
1198 db, mock, err := sqlmock.New()
1199 qes.NoError(err)
1200
1201 mock.ExpectQuery(`SELECT "name" FROM "items"`).
1202 WithArgs().
1203 WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow(testByteSliceContent))
1204
1205 e := newQueryExecutor(db, nil, `SELECT "name" FROM "items"`)
1206
1207 var bytes sql.RawBytes
1208 found, err := e.ScanVal(bytes)
1209 qes.Equal(errScanValPointer, err)
1210 qes.False(found)
1211
1212 found, err = e.ScanVal(&bytes)
1213 qes.NoError(err)
1214 qes.True(found)
1215 qes.Equal(sql.RawBytes(testByteSliceContent), bytes)
1216 }
1217
1218 type JSONBoolArray []bool
1219
1220 func (b *JSONBoolArray) Scan(src interface{}) error {
1221 return json.Unmarshal(src.([]byte), b)
1222 }
1223
1224 func (qes *queryExecutorSuite) TestScanVal_withValuerSlice() {
1225 db, mock, err := sqlmock.New()
1226 qes.NoError(err)
1227
1228 mock.ExpectQuery(`SELECT "bools" FROM "items"`).
1229 WithArgs().
1230 WillReturnRows(sqlmock.NewRows([]string{"bools"}).FromCSVString(`"[true, false, true]"`))
1231
1232 e := newQueryExecutor(db, nil, `SELECT "bools" FROM "items"`)
1233
1234 var bools JSONBoolArray
1235 found, err := e.ScanVal(bools)
1236 qes.Equal(errScanValPointer, err)
1237 qes.False(found)
1238
1239 found, err = e.ScanVal(&bools)
1240 qes.NoError(err)
1241 qes.True(found)
1242 qes.Equal(JSONBoolArray{true, false, true}, bools)
1243 }
1244
1245 func TestQueryExecutorSuite(t *testing.T) {
1246 suite.Run(t, new(queryExecutorSuite))
1247 }
1248
View as plain text