1 package goqu_test
2
3 import (
4 "context"
5 "fmt"
6 "sync"
7 "testing"
8
9 "github.com/DATA-DOG/go-sqlmock"
10 "github.com/doug-martin/goqu/v9"
11 "github.com/doug-martin/goqu/v9/internal/errors"
12 "github.com/stretchr/testify/suite"
13 )
14
15 type testActionItem struct {
16 Address string `db:"address"`
17 Name string `db:"name"`
18 }
19
20 type dbTestMockLogger struct {
21 Messages []string
22 }
23
24 func (dtml *dbTestMockLogger) Printf(format string, v ...interface{}) {
25 dtml.Messages = append(dtml.Messages, fmt.Sprintf(format, v...))
26 }
27
28 func (dtml *dbTestMockLogger) Reset() {
29 dtml.Messages = dtml.Messages[0:0]
30 }
31
32 type databaseSuite struct {
33 suite.Suite
34 }
35
36 func (ds *databaseSuite) TestLogger() {
37 mDB, mock, err := sqlmock.New()
38 ds.NoError(err)
39 mock.ExpectQuery(`SELECT \* FROM "items"`).
40 WithArgs().
41 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
42 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
43
44 mock.ExpectExec(`SELECT \* FROM "items" WHERE "id" = ?`).
45 WithArgs(1).
46 WillReturnResult(sqlmock.NewResult(0, 0))
47
48 db := goqu.New("db-mock", mDB)
49 logger := new(dbTestMockLogger)
50 db.Logger(logger)
51 var items []testActionItem
52 ds.NoError(db.ScanStructs(&items, `SELECT * FROM "items"`))
53 _, err = db.Exec(`SELECT * FROM "items" WHERE "id" = ?`, 1)
54 ds.NoError(err)
55 db.Trace("TEST", "")
56 ds.Equal([]string{
57 "[goqu] QUERY [query:=`SELECT * FROM \"items\"`]",
58 "[goqu] EXEC [query:=`SELECT * FROM \"items\" WHERE \"id\" = ?` args:=[1]]",
59 "[goqu] TEST",
60 }, logger.Messages)
61 }
62
63 func (ds *databaseSuite) TestScanStructs() {
64 mDB, mock, err := sqlmock.New()
65 ds.NoError(err)
66 mock.ExpectQuery(`SELECT \* FROM "items"`).
67 WithArgs().
68 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
69 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
70 mock.ExpectQuery(`SELECT \* FROM "items"`).
71 WithArgs().
72 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
73 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
74 mock.ExpectQuery(`SELECT \* FROM "items"`).
75 WithArgs().
76 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
77 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
78 mock.ExpectQuery(`SELECT "test" FROM "items"`).
79 WithArgs().
80 WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
81
82 db := goqu.New("db-mock", mDB)
83 var items []testActionItem
84 ds.NoError(db.ScanStructs(&items, `SELECT * FROM "items"`))
85 ds.Len(items, 2)
86 ds.Equal("111 Test Addr", items[0].Address)
87 ds.Equal("Test1", items[0].Name)
88
89 ds.Equal("211 Test Addr", items[1].Address)
90 ds.Equal("Test2", items[1].Name)
91
92 items = items[0:0]
93 ds.EqualError(db.ScanStructs(items, `SELECT * FROM "items"`),
94 "goqu: type must be a pointer to a slice when scanning into structs")
95 ds.EqualError(db.ScanStructs(&testActionItem{}, `SELECT * FROM "items"`),
96 "goqu: type must be a pointer to a slice when scanning into structs")
97 ds.EqualError(db.ScanStructs(&items, `SELECT "test" FROM "items"`),
98 `goqu: unable to find corresponding field to column "test" returned by query`)
99 }
100
101 func (ds *databaseSuite) TestScanStruct() {
102 mDB, mock, err := sqlmock.New()
103 ds.NoError(err)
104 mock.ExpectQuery(`SELECT \* FROM "items" LIMIT 1`).
105 WithArgs().
106 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).FromCSVString("111 Test Addr,Test1"))
107
108 mock.ExpectQuery(`SELECT "test" FROM "items" LIMIT 1`).
109 WithArgs().
110 WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
111
112 db := goqu.New("mock", mDB)
113 var item testActionItem
114 found, err := db.ScanStruct(&item, `SELECT * FROM "items" LIMIT 1`)
115 ds.NoError(err)
116 ds.True(found)
117 ds.Equal("111 Test Addr", item.Address)
118 ds.Equal("Test1", item.Name)
119
120 _, err = db.ScanStruct(item, `SELECT * FROM "items" LIMIT 1`)
121 ds.EqualError(err, "goqu: type must be a pointer to a struct when scanning into a struct")
122 _, err = db.ScanStruct([]testActionItem{}, `SELECT * FROM "items" LIMIT 1`)
123 ds.EqualError(err, "goqu: type must be a pointer to a struct when scanning into a struct")
124 _, err = db.ScanStruct(&item, `SELECT "test" FROM "items" LIMIT 1`)
125 ds.EqualError(err, `goqu: unable to find corresponding field to column "test" returned by query`)
126 }
127
128 func (ds *databaseSuite) TestScanVals() {
129 mDB, mock, err := sqlmock.New()
130 ds.NoError(err)
131 mock.ExpectQuery(`SELECT "id" FROM "items"`).
132 WithArgs().
133 WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
134 mock.ExpectQuery(`SELECT "id" FROM "items"`).
135 WithArgs().
136 WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
137 mock.ExpectQuery(`SELECT "id" FROM "items"`).
138 WithArgs().
139 WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
140
141 db := goqu.New("mock", mDB)
142 var ids []uint32
143 ds.NoError(db.ScanVals(&ids, `SELECT "id" FROM "items"`))
144 ds.Len(ids, 5)
145
146 ds.EqualError(db.ScanVals([]uint32{}, `SELECT "id" FROM "items"`),
147 "goqu: type must be a pointer to a slice when scanning into vals")
148 ds.EqualError(db.ScanVals(testActionItem{}, `SELECT "id" FROM "items"`),
149 "goqu: type must be a pointer to a slice when scanning into vals")
150 }
151
152 func (ds *databaseSuite) TestScanVal() {
153 mDB, mock, err := sqlmock.New()
154 ds.NoError(err)
155 mock.ExpectQuery(`SELECT "id" FROM "items"`).
156 WithArgs().
157 WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("10"))
158
159 db := goqu.New("mock", mDB)
160 var id int64
161 found, err := db.ScanVal(&id, `SELECT "id" FROM "items"`)
162 ds.NoError(err)
163 ds.Equal(int64(10), id)
164 ds.True(found)
165
166 found, err = db.ScanVal([]int64{}, `SELECT "id" FROM "items"`)
167 ds.False(found)
168 ds.EqualError(err, "goqu: type must be a pointer when scanning into val")
169 found, err = db.ScanVal(10, `SELECT "id" FROM "items"`)
170 ds.False(found)
171 ds.EqualError(err, "goqu: type must be a pointer when scanning into val")
172 }
173
174 func (ds *databaseSuite) TestExec() {
175 mDB, mock, err := sqlmock.New()
176 ds.NoError(err)
177 mock.ExpectExec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE \("name" IS NULL\)`).
178 WithArgs().
179 WillReturnResult(sqlmock.NewResult(0, 0))
180
181 mock.ExpectExec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE \("name" IS NULL\)`).
182 WithArgs().
183 WillReturnError(errors.New("mock error"))
184
185 db := goqu.New("mock", mDB)
186 _, err = db.Exec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE ("name" IS NULL)`)
187 ds.NoError(err)
188 _, err = db.Exec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE ("name" IS NULL)`)
189 ds.EqualError(err, "goqu: mock error")
190 }
191
192 func (ds *databaseSuite) TestQuery() {
193 mDB, mock, err := sqlmock.New()
194 ds.NoError(err)
195 mock.ExpectQuery(`SELECT \* FROM "items"`).
196 WithArgs().
197 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
198 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
199
200 mock.ExpectQuery(`SELECT \* FROM "items"`).
201 WithArgs().
202 WillReturnError(errors.New("mock error"))
203
204 db := goqu.New("mock", mDB)
205 _, err = db.Query(`SELECT * FROM "items"`)
206 ds.NoError(err, "goqu - mock error")
207
208 _, err = db.Query(`SELECT * FROM "items"`)
209 ds.EqualError(err, "goqu: mock error")
210 }
211
212 func (ds *databaseSuite) TestQueryRow() {
213 mDB, mock, err := sqlmock.New()
214 ds.NoError(err)
215 mock.ExpectQuery(`SELECT \* FROM "items"`).
216 WithArgs().
217 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
218 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
219
220 mock.ExpectQuery(`SELECT \* FROM "items"`).
221 WithArgs().
222 WillReturnError(errors.New("mock error"))
223
224 db := goqu.New("mock", mDB)
225 rows := db.QueryRow(`SELECT * FROM "items"`)
226 var address string
227 var name string
228 ds.NoError(rows.Scan(&address, &name))
229
230 rows = db.QueryRow(`SELECT * FROM "items"`)
231 ds.EqualError(rows.Scan(&address, &name), "goqu: mock error")
232 }
233
234 func (ds *databaseSuite) TestPrepare() {
235 mDB, mock, err := sqlmock.New()
236 ds.NoError(err)
237 mock.ExpectPrepare("SELECT \\* FROM test WHERE id = \\?")
238 db := goqu.New("mock", mDB)
239 stmt, err := db.Prepare("SELECT * FROM test WHERE id = ?")
240 ds.NoError(err)
241 ds.NotNil(stmt)
242 }
243
244 func (ds *databaseSuite) TestBegin() {
245 mDB, mock, err := sqlmock.New()
246 ds.NoError(err)
247 mock.ExpectBegin()
248 mock.ExpectBegin().WillReturnError(errors.New("transaction error"))
249 db := goqu.New("mock", mDB)
250 tx, err := db.Begin()
251 ds.NoError(err)
252 ds.Equal("mock", tx.Dialect())
253
254 _, err = db.Begin()
255 ds.EqualError(err, "goqu: transaction error")
256 }
257
258 func (ds *databaseSuite) TestBeginTx() {
259 ctx := context.Background()
260 mDB, mock, err := sqlmock.New()
261 ds.NoError(err)
262 mock.ExpectBegin()
263 mock.ExpectBegin().WillReturnError(errors.New("transaction error"))
264 db := goqu.New("mock", mDB)
265 tx, err := db.BeginTx(ctx, nil)
266 ds.NoError(err)
267 ds.Equal("mock", tx.Dialect())
268
269 _, err = db.BeginTx(ctx, nil)
270 ds.EqualError(err, "goqu: transaction error")
271 }
272
273 func (ds *databaseSuite) TestWithTx() {
274 mDB, mock, err := sqlmock.New()
275 ds.NoError(err)
276
277 db := goqu.New("mock", mDB)
278
279 cases := []struct {
280 expectf func(sqlmock.Sqlmock)
281 f func(*goqu.TxDatabase) error
282 wantErr bool
283 errStr string
284 }{
285 {
286 expectf: func(mock sqlmock.Sqlmock) {
287 mock.ExpectBegin()
288 mock.ExpectCommit()
289 },
290 f: func(_ *goqu.TxDatabase) error { return nil },
291 wantErr: false,
292 },
293 {
294 expectf: func(mock sqlmock.Sqlmock) {
295 mock.ExpectBegin().WillReturnError(errors.New("transaction begin error"))
296 },
297 f: func(_ *goqu.TxDatabase) error { return nil },
298 wantErr: true,
299 errStr: "goqu: transaction begin error",
300 },
301 {
302 expectf: func(mock sqlmock.Sqlmock) {
303 mock.ExpectBegin()
304 mock.ExpectRollback()
305 },
306 f: func(_ *goqu.TxDatabase) error { return errors.New("transaction error") },
307 wantErr: true,
308 errStr: "goqu: transaction error",
309 },
310 {
311 expectf: func(mock sqlmock.Sqlmock) {
312 mock.ExpectBegin()
313 mock.ExpectRollback().WillReturnError(errors.New("transaction rollback error"))
314 },
315 f: func(_ *goqu.TxDatabase) error { return errors.New("something wrong") },
316 wantErr: true,
317 errStr: "goqu: transaction rollback error",
318 },
319 {
320 expectf: func(mock sqlmock.Sqlmock) {
321 mock.ExpectBegin()
322 mock.ExpectCommit().WillReturnError(errors.New("commit error"))
323 },
324 f: func(_ *goqu.TxDatabase) error { return nil },
325 wantErr: true,
326 errStr: "goqu: commit error",
327 },
328 }
329 for _, c := range cases {
330 c.expectf(mock)
331 err := db.WithTx(c.f)
332 if c.wantErr {
333 ds.EqualError(err, c.errStr)
334 } else {
335 ds.NoError(err)
336 }
337 }
338 }
339
340 func (ds *databaseSuite) TestRollbackOnPanic() {
341 mDB, mock, err := sqlmock.New()
342
343 defer func() {
344 p := recover()
345 if p == nil {
346 ds.Fail("there should be a panic")
347 }
348 ds.Require().Equal("a problem has happened", p.(string))
349 ds.Require().NoError(mock.ExpectationsWereMet())
350 }()
351
352 ds.NoError(err)
353
354 mock.ExpectBegin()
355 mock.ExpectRollback()
356
357 db := goqu.New("mock", mDB)
358 _ = db.WithTx(func(_ *goqu.TxDatabase) error {
359 panic("a problem has happened")
360 })
361 }
362
363 func (ds *databaseSuite) TestDataRace() {
364 mDB, mock, err := sqlmock.New()
365 ds.NoError(err)
366 db := goqu.New("mock", mDB)
367
368 const concurrency = 10
369
370 for i := 0; i < concurrency; i++ {
371 mock.ExpectQuery(`SELECT "address", "name" FROM "items"`).
372 WithArgs().
373 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
374 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
375 }
376
377 wg := sync.WaitGroup{}
378 for i := 0; i < concurrency; i++ {
379 wg.Add(1)
380 go func() {
381 defer wg.Done()
382
383 sql := db.From("items").Limit(1)
384 var item testActionItem
385 found, err := sql.ScanStruct(&item)
386 ds.NoError(err)
387 ds.True(found)
388 ds.Equal(item.Address, "111 Test Addr")
389 ds.Equal(item.Name, "Test1")
390 }()
391 }
392
393 wg.Wait()
394 }
395
396 func TestDatabaseSuite(t *testing.T) {
397 suite.Run(t, new(databaseSuite))
398 }
399
400 type txdatabaseSuite struct {
401 suite.Suite
402 }
403
404 func (tds *txdatabaseSuite) TestLogger() {
405 mDB, mock, err := sqlmock.New()
406 tds.NoError(err)
407 mock.ExpectBegin()
408 mock.ExpectQuery(`SELECT \* FROM "items"`).
409 WithArgs().
410 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
411 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
412
413 mock.ExpectExec(`SELECT \* FROM "items" WHERE "id" = ?`).
414 WithArgs(1).
415 WillReturnResult(sqlmock.NewResult(0, 0))
416 mock.ExpectCommit()
417
418 tx, err := goqu.New("db-mock", mDB).Begin()
419 tds.NoError(err)
420 logger := new(dbTestMockLogger)
421 tx.Logger(logger)
422 var items []testActionItem
423 tds.NoError(tx.ScanStructs(&items, `SELECT * FROM "items"`))
424 _, err = tx.Exec(`SELECT * FROM "items" WHERE "id" = ?`, 1)
425 tds.NoError(err)
426 tds.NoError(tx.Commit())
427 tds.Equal([]string{
428 "[goqu - transaction] QUERY [query:=`SELECT * FROM \"items\"`] ",
429 "[goqu - transaction] EXEC [query:=`SELECT * FROM \"items\" WHERE \"id\" = ?` args:=[1]] ",
430 "[goqu - transaction] COMMIT",
431 }, logger.Messages)
432 }
433
434 func (tds *txdatabaseSuite) TestLogger_FromDb() {
435 mDB, mock, err := sqlmock.New()
436 tds.NoError(err)
437 mock.ExpectBegin()
438 mock.ExpectQuery(`SELECT \* FROM "items"`).
439 WithArgs().
440 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
441 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
442
443 mock.ExpectExec(`SELECT \* FROM "items" WHERE "id" = ?`).
444 WithArgs(1).
445 WillReturnResult(sqlmock.NewResult(0, 0))
446 mock.ExpectCommit()
447
448 db := goqu.New("db-mock", mDB)
449 logger := new(dbTestMockLogger)
450 db.Logger(logger)
451 tx, err := db.Begin()
452 tds.NoError(err)
453
454 var items []testActionItem
455 tds.NoError(tx.ScanStructs(&items, `SELECT * FROM "items"`))
456 _, err = tx.Exec(`SELECT * FROM "items" WHERE "id" = ?`, 1)
457 tds.NoError(err)
458 tds.NoError(tx.Commit())
459 tds.Equal([]string{
460 "[goqu - transaction] QUERY [query:=`SELECT * FROM \"items\"`] ",
461 "[goqu - transaction] EXEC [query:=`SELECT * FROM \"items\" WHERE \"id\" = ?` args:=[1]] ",
462 "[goqu - transaction] COMMIT",
463 }, logger.Messages)
464 }
465
466 func (tds *txdatabaseSuite) TestCommit() {
467 mDB, mock, err := sqlmock.New()
468 tds.NoError(err)
469 mock.ExpectBegin()
470 mock.ExpectCommit()
471 db := goqu.New("mock", mDB)
472 tx, err := db.Begin()
473 tds.NoError(err)
474 tds.NoError(tx.Commit())
475 }
476
477 func (tds *txdatabaseSuite) TestRollback() {
478 mDB, mock, err := sqlmock.New()
479 tds.NoError(err)
480 mock.ExpectBegin()
481 mock.ExpectRollback()
482 db := goqu.New("mock", mDB)
483 tx, err := db.Begin()
484 tds.NoError(err)
485 tds.NoError(tx.Rollback())
486 }
487
488 func (tds *txdatabaseSuite) TestFrom() {
489 mDB, mock, err := sqlmock.New()
490 tds.NoError(err)
491 mock.ExpectBegin()
492 mock.ExpectCommit()
493 db := goqu.New("mock", mDB)
494 tx, err := db.Begin()
495 tds.NoError(err)
496 tds.NotNil(goqu.From("test"))
497 tds.NoError(tx.Commit())
498 }
499
500 func (tds *txdatabaseSuite) TestScanStructs() {
501 mDB, mock, err := sqlmock.New()
502 tds.NoError(err)
503 mock.ExpectBegin()
504 mock.ExpectQuery(`SELECT \* FROM "items"`).
505 WithArgs().
506 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
507 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
508 mock.ExpectQuery(`SELECT \* FROM "items"`).
509 WithArgs().
510 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
511 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
512 mock.ExpectQuery(`SELECT \* FROM "items"`).
513 WithArgs().
514 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
515 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
516 mock.ExpectQuery(`SELECT "test" FROM "items"`).
517 WithArgs().
518 WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
519 mock.ExpectCommit()
520 db := goqu.New("mock", mDB)
521 tx, err := db.Begin()
522 tds.NoError(err)
523 var items []testActionItem
524 tds.NoError(tx.ScanStructs(&items, `SELECT * FROM "items"`))
525 tds.Len(items, 2)
526 tds.Equal("111 Test Addr", items[0].Address)
527 tds.Equal("Test1", items[0].Name)
528
529 tds.Equal("211 Test Addr", items[1].Address)
530 tds.Equal("Test2", items[1].Name)
531
532 items = items[0:0]
533 tds.EqualError(tx.ScanStructs(items, `SELECT * FROM "items"`),
534 "goqu: type must be a pointer to a slice when scanning into structs")
535 tds.EqualError(tx.ScanStructs(&testActionItem{}, `SELECT * FROM "items"`),
536 "goqu: type must be a pointer to a slice when scanning into structs")
537 tds.EqualError(tx.ScanStructs(&items, `SELECT "test" FROM "items"`),
538 `goqu: unable to find corresponding field to column "test" returned by query`)
539 tds.NoError(tx.Commit())
540 }
541
542 func (tds *txdatabaseSuite) TestScanStruct() {
543 mDB, mock, err := sqlmock.New()
544 tds.NoError(err)
545 mock.ExpectBegin()
546 mock.ExpectQuery(`SELECT \* FROM "items" LIMIT 1`).
547 WithArgs().
548 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).FromCSVString("111 Test Addr,Test1"))
549
550 mock.ExpectQuery(`SELECT "test" FROM "items" LIMIT 1`).
551 WithArgs().
552 WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
553 mock.ExpectCommit()
554 db := goqu.New("mock", mDB)
555 tx, err := db.Begin()
556 tds.NoError(err)
557 var item testActionItem
558 found, err := tx.ScanStruct(&item, `SELECT * FROM "items" LIMIT 1`)
559 tds.NoError(err)
560 tds.True(found)
561 tds.Equal("111 Test Addr", item.Address)
562 tds.Equal("Test1", item.Name)
563
564 _, err = tx.ScanStruct(item, `SELECT * FROM "items" LIMIT 1`)
565 tds.EqualError(err, "goqu: type must be a pointer to a struct when scanning into a struct")
566 _, err = tx.ScanStruct([]testActionItem{}, `SELECT * FROM "items" LIMIT 1`)
567 tds.EqualError(err, "goqu: type must be a pointer to a struct when scanning into a struct")
568 _, err = tx.ScanStruct(&item, `SELECT "test" FROM "items" LIMIT 1`)
569 tds.EqualError(err, `goqu: unable to find corresponding field to column "test" returned by query`)
570 tds.NoError(tx.Commit())
571 }
572
573 func (tds *txdatabaseSuite) TestScanVals() {
574 mDB, mock, err := sqlmock.New()
575 tds.NoError(err)
576 mock.ExpectBegin()
577 mock.ExpectQuery(`SELECT "id" FROM "items"`).
578 WithArgs().
579 WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
580 mock.ExpectQuery(`SELECT "id" FROM "items"`).
581 WithArgs().
582 WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
583 mock.ExpectQuery(`SELECT "id" FROM "items"`).
584 WithArgs().
585 WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
586 mock.ExpectCommit()
587 db := goqu.New("mock", mDB)
588 tx, err := db.Begin()
589 tds.NoError(err)
590 var ids []uint32
591 tds.NoError(tx.ScanVals(&ids, `SELECT "id" FROM "items"`))
592 tds.Len(ids, 5)
593
594 tds.EqualError(tx.ScanVals([]uint32{}, `SELECT "id" FROM "items"`),
595 "goqu: type must be a pointer to a slice when scanning into vals")
596 tds.EqualError(tx.ScanVals(testActionItem{}, `SELECT "id" FROM "items"`),
597 "goqu: type must be a pointer to a slice when scanning into vals")
598 tds.NoError(tx.Commit())
599 }
600
601 func (tds *txdatabaseSuite) TestScanVal() {
602 mDB, mock, err := sqlmock.New()
603 tds.NoError(err)
604 mock.ExpectBegin()
605 mock.ExpectQuery(`SELECT "id" FROM "items"`).
606 WithArgs().
607 WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("10"))
608 mock.ExpectCommit()
609 db := goqu.New("mock", mDB)
610 tx, err := db.Begin()
611 tds.NoError(err)
612 var id int64
613 found, err := tx.ScanVal(&id, `SELECT "id" FROM "items"`)
614 tds.NoError(err)
615 tds.Equal(int64(10), id)
616 tds.True(found)
617
618 found, err = tx.ScanVal([]int64{}, `SELECT "id" FROM "items"`)
619 tds.False(found)
620 tds.EqualError(err, "goqu: type must be a pointer when scanning into val")
621 found, err = tx.ScanVal(10, `SELECT "id" FROM "items"`)
622 tds.False(found)
623 tds.EqualError(err, "goqu: type must be a pointer when scanning into val")
624 tds.NoError(tx.Commit())
625 }
626
627 func (tds *txdatabaseSuite) TestExec() {
628 mDB, mock, err := sqlmock.New()
629 tds.NoError(err)
630 mock.ExpectBegin()
631 mock.ExpectExec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE \("name" IS NULL\)`).
632 WithArgs().
633 WillReturnResult(sqlmock.NewResult(0, 0))
634
635 mock.ExpectExec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE \("name" IS NULL\)`).
636 WithArgs().
637 WillReturnError(errors.New("mock error"))
638 mock.ExpectCommit()
639 db := goqu.New("mock", mDB)
640 tx, err := db.Begin()
641 tds.NoError(err)
642 _, err = tx.Exec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE ("name" IS NULL)`)
643 tds.NoError(err)
644 _, err = tx.Exec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE ("name" IS NULL)`)
645 tds.EqualError(err, "goqu: mock error")
646 tds.NoError(tx.Commit())
647 }
648
649 func (tds *txdatabaseSuite) TestQuery() {
650 mDB, mock, err := sqlmock.New()
651 tds.NoError(err)
652 mock.ExpectBegin()
653 mock.ExpectQuery(`SELECT \* FROM "items"`).
654 WithArgs().
655 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
656 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
657
658 mock.ExpectQuery(`SELECT \* FROM "items"`).
659 WithArgs().
660 WillReturnError(errors.New("mock error"))
661 mock.ExpectCommit()
662 db := goqu.New("mock", mDB)
663 tx, err := db.Begin()
664 tds.NoError(err)
665 _, err = tx.Query(`SELECT * FROM "items"`)
666 tds.NoError(err, "goqu - mock error")
667
668 _, err = tx.Query(`SELECT * FROM "items"`)
669 tds.EqualError(err, "goqu: mock error")
670 tds.NoError(tx.Commit())
671 }
672
673 func (tds *txdatabaseSuite) TestQueryRow() {
674 mDB, mock, err := sqlmock.New()
675 tds.NoError(err)
676 mock.ExpectBegin()
677 mock.ExpectQuery(`SELECT \* FROM "items"`).
678 WithArgs().
679 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
680 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
681
682 mock.ExpectQuery(`SELECT \* FROM "items"`).
683 WithArgs().
684 WillReturnError(errors.New("mock error"))
685 mock.ExpectCommit()
686 db := goqu.New("mock", mDB)
687 tx, err := db.Begin()
688 tds.NoError(err)
689 rows := tx.QueryRow(`SELECT * FROM "items"`)
690 var address string
691 var name string
692 tds.NoError(rows.Scan(&address, &name))
693
694 rows = tx.QueryRow(`SELECT * FROM "items"`)
695 tds.EqualError(rows.Scan(&address, &name), "goqu: mock error")
696 tds.NoError(tx.Commit())
697 }
698
699 func (tds *txdatabaseSuite) TestWrap() {
700 mDB, mock, err := sqlmock.New()
701 tds.NoError(err)
702 mock.ExpectBegin()
703 mock.ExpectCommit()
704 mock.ExpectBegin()
705 mock.ExpectRollback()
706 db := goqu.New("mock", mDB)
707 tx, err := db.Begin()
708 tds.NoError(err)
709 tds.NoError(tx.Wrap(func() error {
710 return nil
711 }))
712 tx, err = db.Begin()
713 tds.NoError(err)
714 tds.EqualError(tx.Wrap(func() error {
715 return errors.New("tx error")
716 }), "goqu: tx error")
717 }
718
719 func (tds *txdatabaseSuite) TestDataRace() {
720 mDB, mock, err := sqlmock.New()
721 tds.NoError(err)
722 mock.ExpectBegin()
723 db := goqu.New("mock", mDB)
724 tx, err := db.Begin()
725 tds.NoError(err)
726
727 const concurrency = 10
728
729 for i := 0; i < concurrency; i++ {
730 mock.ExpectQuery(`SELECT "address", "name" FROM "items"`).
731 WithArgs().
732 WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
733 FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
734 }
735
736 wg := sync.WaitGroup{}
737 for i := 0; i < concurrency; i++ {
738 wg.Add(1)
739 go func() {
740 defer wg.Done()
741
742 sql := tx.From("items").Limit(1)
743 var item testActionItem
744 found, err := sql.ScanStruct(&item)
745 tds.NoError(err)
746 tds.True(found)
747 tds.Equal(item.Address, "111 Test Addr")
748 tds.Equal(item.Name, "Test1")
749 }()
750 }
751
752 wg.Wait()
753 mock.ExpectCommit()
754 tds.NoError(tx.Commit())
755 }
756
757 func TestTxDatabaseSuite(t *testing.T) {
758 suite.Run(t, new(txdatabaseSuite))
759 }
760
View as plain text