760 lines
22 KiB
Go
760 lines
22 KiB
Go
package db_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
|
|
dbv2 "git.fsdpf.net/go/db/v2"
|
|
"git.fsdpf.net/go/db/v2/internal/errors"
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type testActionItem struct {
|
|
Address string `db:"address"`
|
|
Name string `db:"name"`
|
|
}
|
|
|
|
type dbTestMockLogger struct {
|
|
Messages []string
|
|
}
|
|
|
|
func (dtml *dbTestMockLogger) Printf(format string, v ...interface{}) {
|
|
dtml.Messages = append(dtml.Messages, fmt.Sprintf(format, v...))
|
|
}
|
|
|
|
func (dtml *dbTestMockLogger) Reset() {
|
|
dtml.Messages = dtml.Messages[0:0]
|
|
}
|
|
|
|
type databaseSuite struct {
|
|
suite.Suite
|
|
}
|
|
|
|
func (ds *databaseSuite) TestLogger() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
|
|
mock.ExpectExec(`SELECT \* FROM "items" WHERE "id" = ?`).
|
|
WithArgs(1).
|
|
WillReturnResult(sqlmock.NewResult(0, 0))
|
|
|
|
db := dbv2.New("db-mock", mDB)
|
|
logger := new(dbTestMockLogger)
|
|
db.Logger(logger)
|
|
var items []testActionItem
|
|
ds.NoError(db.ScanStructs(&items, `SELECT * FROM "items"`))
|
|
_, err = db.Exec(`SELECT * FROM "items" WHERE "id" = ?`, 1)
|
|
ds.NoError(err)
|
|
db.Trace("TEST", "")
|
|
ds.Equal([]string{
|
|
"[db] QUERY [query:=`SELECT * FROM \"items\"`]",
|
|
"[db] EXEC [query:=`SELECT * FROM \"items\" WHERE \"id\" = ?` args:=[1]]",
|
|
"[db] TEST",
|
|
}, logger.Messages)
|
|
}
|
|
|
|
func (ds *databaseSuite) TestScanStructs() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
mock.ExpectQuery(`SELECT "test" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
|
|
|
|
db := dbv2.New("db-mock", mDB)
|
|
var items []testActionItem
|
|
ds.NoError(db.ScanStructs(&items, `SELECT * FROM "items"`))
|
|
ds.Len(items, 2)
|
|
ds.Equal("111 Test Addr", items[0].Address)
|
|
ds.Equal("Test1", items[0].Name)
|
|
|
|
ds.Equal("211 Test Addr", items[1].Address)
|
|
ds.Equal("Test2", items[1].Name)
|
|
|
|
items = items[0:0]
|
|
ds.EqualError(db.ScanStructs(items, `SELECT * FROM "items"`),
|
|
"db: type must be a pointer to a slice when scanning into structs")
|
|
ds.EqualError(db.ScanStructs(&testActionItem{}, `SELECT * FROM "items"`),
|
|
"db: type must be a pointer to a slice when scanning into structs")
|
|
ds.EqualError(db.ScanStructs(&items, `SELECT "test" FROM "items"`),
|
|
`db: unable to find corresponding field to column "test" returned by query`)
|
|
}
|
|
|
|
func (ds *databaseSuite) TestScanStruct() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
mock.ExpectQuery(`SELECT \* FROM "items" LIMIT 1`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).FromCSVString("111 Test Addr,Test1"))
|
|
|
|
mock.ExpectQuery(`SELECT "test" FROM "items" LIMIT 1`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
|
|
|
|
db := dbv2.New("mock", mDB)
|
|
var item testActionItem
|
|
found, err := db.ScanStruct(&item, `SELECT * FROM "items" LIMIT 1`)
|
|
ds.NoError(err)
|
|
ds.True(found)
|
|
ds.Equal("111 Test Addr", item.Address)
|
|
ds.Equal("Test1", item.Name)
|
|
|
|
_, err = db.ScanStruct(item, `SELECT * FROM "items" LIMIT 1`)
|
|
ds.EqualError(err, "db: type must be a pointer to a struct when scanning into a struct")
|
|
_, err = db.ScanStruct([]testActionItem{}, `SELECT * FROM "items" LIMIT 1`)
|
|
ds.EqualError(err, "db: type must be a pointer to a struct when scanning into a struct")
|
|
_, err = db.ScanStruct(&item, `SELECT "test" FROM "items" LIMIT 1`)
|
|
ds.EqualError(err, `db: unable to find corresponding field to column "test" returned by query`)
|
|
}
|
|
|
|
func (ds *databaseSuite) TestScanVals() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
mock.ExpectQuery(`SELECT "id" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
|
|
mock.ExpectQuery(`SELECT "id" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
|
|
mock.ExpectQuery(`SELECT "id" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
|
|
|
|
db := dbv2.New("mock", mDB)
|
|
var ids []uint32
|
|
ds.NoError(db.ScanVals(&ids, `SELECT "id" FROM "items"`))
|
|
ds.Len(ids, 5)
|
|
|
|
ds.EqualError(db.ScanVals([]uint32{}, `SELECT "id" FROM "items"`),
|
|
"db: type must be a pointer to a slice when scanning into vals")
|
|
ds.EqualError(db.ScanVals(testActionItem{}, `SELECT "id" FROM "items"`),
|
|
"db: type must be a pointer to a slice when scanning into vals")
|
|
}
|
|
|
|
func (ds *databaseSuite) TestScanVal() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
mock.ExpectQuery(`SELECT "id" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("10"))
|
|
|
|
db := dbv2.New("mock", mDB)
|
|
var id int64
|
|
found, err := db.ScanVal(&id, `SELECT "id" FROM "items"`)
|
|
ds.NoError(err)
|
|
ds.Equal(int64(10), id)
|
|
ds.True(found)
|
|
|
|
found, err = db.ScanVal([]int64{}, `SELECT "id" FROM "items"`)
|
|
ds.False(found)
|
|
ds.EqualError(err, "db: type must be a pointer when scanning into val")
|
|
found, err = db.ScanVal(10, `SELECT "id" FROM "items"`)
|
|
ds.False(found)
|
|
ds.EqualError(err, "db: type must be a pointer when scanning into val")
|
|
}
|
|
|
|
func (ds *databaseSuite) TestExec() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
mock.ExpectExec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE \("name" IS NULL\)`).
|
|
WithArgs().
|
|
WillReturnResult(sqlmock.NewResult(0, 0))
|
|
|
|
mock.ExpectExec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE \("name" IS NULL\)`).
|
|
WithArgs().
|
|
WillReturnError(errors.New("mock error"))
|
|
|
|
db := dbv2.New("mock", mDB)
|
|
_, err = db.Exec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE ("name" IS NULL)`)
|
|
ds.NoError(err)
|
|
_, err = db.Exec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE ("name" IS NULL)`)
|
|
ds.EqualError(err, "db: mock error")
|
|
}
|
|
|
|
func (ds *databaseSuite) TestQuery() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnError(errors.New("mock error"))
|
|
|
|
db := dbv2.New("mock", mDB)
|
|
_, err = db.Query(`SELECT * FROM "items"`) //nolint:rowserrcheck // not checking row scan
|
|
ds.NoError(err, "dbv2 - mock error")
|
|
|
|
_, err = db.Query(`SELECT * FROM "items"`) //nolint:rowserrcheck // not checking row scan
|
|
ds.EqualError(err, "db: mock error")
|
|
}
|
|
|
|
func (ds *databaseSuite) TestQueryRow() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnError(errors.New("mock error"))
|
|
|
|
db := dbv2.New("mock", mDB)
|
|
rows := db.QueryRow(`SELECT * FROM "items"`)
|
|
var address string
|
|
var name string
|
|
ds.NoError(rows.Scan(&address, &name))
|
|
|
|
rows = db.QueryRow(`SELECT * FROM "items"`)
|
|
ds.EqualError(rows.Scan(&address, &name), "db: mock error")
|
|
}
|
|
|
|
func (ds *databaseSuite) TestPrepare() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
mock.ExpectPrepare("SELECT \\* FROM test WHERE id = \\?")
|
|
db := dbv2.New("mock", mDB)
|
|
stmt, err := db.Prepare("SELECT * FROM test WHERE id = ?")
|
|
ds.NoError(err)
|
|
ds.NotNil(stmt)
|
|
}
|
|
|
|
func (ds *databaseSuite) TestBegin() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectBegin().WillReturnError(errors.New("transaction error"))
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
ds.NoError(err)
|
|
ds.Equal("mock", tx.Dialect())
|
|
|
|
_, err = db.Begin()
|
|
ds.EqualError(err, "db: transaction error")
|
|
}
|
|
|
|
func (ds *databaseSuite) TestBeginTx() {
|
|
ctx := context.Background()
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectBegin().WillReturnError(errors.New("transaction error"))
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.BeginTx(ctx, nil)
|
|
ds.NoError(err)
|
|
ds.Equal("mock", tx.Dialect())
|
|
|
|
_, err = db.BeginTx(ctx, nil)
|
|
ds.EqualError(err, "db: transaction error")
|
|
}
|
|
|
|
func (ds *databaseSuite) TestWithTx() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
|
|
db := dbv2.New("mock", mDB)
|
|
|
|
cases := []struct {
|
|
expectf func(sqlmock.Sqlmock)
|
|
f func(*dbv2.TxDatabase) error
|
|
wantErr bool
|
|
errStr string
|
|
}{
|
|
{
|
|
expectf: func(mock sqlmock.Sqlmock) {
|
|
mock.ExpectBegin()
|
|
mock.ExpectCommit()
|
|
},
|
|
f: func(_ *dbv2.TxDatabase) error { return nil },
|
|
wantErr: false,
|
|
},
|
|
{
|
|
expectf: func(mock sqlmock.Sqlmock) {
|
|
mock.ExpectBegin().WillReturnError(errors.New("transaction begin error"))
|
|
},
|
|
f: func(_ *dbv2.TxDatabase) error { return nil },
|
|
wantErr: true,
|
|
errStr: "db: transaction begin error",
|
|
},
|
|
{
|
|
expectf: func(mock sqlmock.Sqlmock) {
|
|
mock.ExpectBegin()
|
|
mock.ExpectRollback()
|
|
},
|
|
f: func(_ *dbv2.TxDatabase) error { return errors.New("transaction error") },
|
|
wantErr: true,
|
|
errStr: "db: transaction error",
|
|
},
|
|
{
|
|
expectf: func(mock sqlmock.Sqlmock) {
|
|
mock.ExpectBegin()
|
|
mock.ExpectRollback().WillReturnError(errors.New("transaction rollback error"))
|
|
},
|
|
f: func(_ *dbv2.TxDatabase) error { return errors.New("something wrong") },
|
|
wantErr: true,
|
|
errStr: "db: transaction rollback error",
|
|
},
|
|
{
|
|
expectf: func(mock sqlmock.Sqlmock) {
|
|
mock.ExpectBegin()
|
|
mock.ExpectCommit().WillReturnError(errors.New("commit error"))
|
|
},
|
|
f: func(_ *dbv2.TxDatabase) error { return nil },
|
|
wantErr: true,
|
|
errStr: "db: commit error",
|
|
},
|
|
}
|
|
for _, c := range cases {
|
|
c.expectf(mock)
|
|
err := db.WithTx(c.f)
|
|
if c.wantErr {
|
|
ds.EqualError(err, c.errStr)
|
|
} else {
|
|
ds.NoError(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ds *databaseSuite) TestRollbackOnPanic() {
|
|
mDB, mock, err := sqlmock.New()
|
|
|
|
defer func() {
|
|
p := recover()
|
|
if p == nil {
|
|
ds.Fail("there should be a panic")
|
|
}
|
|
ds.Require().Equal("a problem has happened", p.(string))
|
|
ds.Require().NoError(mock.ExpectationsWereMet())
|
|
}()
|
|
|
|
ds.NoError(err)
|
|
|
|
mock.ExpectBegin()
|
|
mock.ExpectRollback()
|
|
|
|
db := dbv2.New("mock", mDB)
|
|
_ = db.WithTx(func(_ *dbv2.TxDatabase) error {
|
|
panic("a problem has happened")
|
|
})
|
|
}
|
|
|
|
func (ds *databaseSuite) TestDataRace() {
|
|
mDB, mock, err := sqlmock.New()
|
|
ds.NoError(err)
|
|
db := dbv2.New("mock", mDB)
|
|
|
|
const concurrency = 10
|
|
|
|
for i := 0; i < concurrency; i++ {
|
|
mock.ExpectQuery(`SELECT "address", "name" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
for i := 0; i < concurrency; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
sql := db.From("items").Limit(1)
|
|
var item testActionItem
|
|
found, err := sql.ScanStruct(&item)
|
|
ds.NoError(err)
|
|
ds.True(found)
|
|
ds.Equal(item.Address, "111 Test Addr")
|
|
ds.Equal(item.Name, "Test1")
|
|
}()
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestDatabaseSuite(t *testing.T) {
|
|
suite.Run(t, new(databaseSuite))
|
|
}
|
|
|
|
type txdatabaseSuite struct {
|
|
suite.Suite
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestLogger() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
|
|
mock.ExpectExec(`SELECT \* FROM "items" WHERE "id" = ?`).
|
|
WithArgs(1).
|
|
WillReturnResult(sqlmock.NewResult(0, 0))
|
|
mock.ExpectCommit()
|
|
|
|
tx, err := dbv2.New("db-mock", mDB).Begin()
|
|
tds.NoError(err)
|
|
logger := new(dbTestMockLogger)
|
|
tx.Logger(logger)
|
|
var items []testActionItem
|
|
tds.NoError(tx.ScanStructs(&items, `SELECT * FROM "items"`))
|
|
_, err = tx.Exec(`SELECT * FROM "items" WHERE "id" = ?`, 1)
|
|
tds.NoError(err)
|
|
tds.NoError(tx.Commit())
|
|
tds.Equal([]string{
|
|
"[dbv2 - transaction] QUERY [query:=`SELECT * FROM \"items\"`] ",
|
|
"[dbv2 - transaction] EXEC [query:=`SELECT * FROM \"items\" WHERE \"id\" = ?` args:=[1]] ",
|
|
"[dbv2 - transaction] COMMIT",
|
|
}, logger.Messages)
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestLogger_FromDb() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
|
|
mock.ExpectExec(`SELECT \* FROM "items" WHERE "id" = ?`).
|
|
WithArgs(1).
|
|
WillReturnResult(sqlmock.NewResult(0, 0))
|
|
mock.ExpectCommit()
|
|
|
|
db := dbv2.New("db-mock", mDB)
|
|
logger := new(dbTestMockLogger)
|
|
db.Logger(logger)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
|
|
var items []testActionItem
|
|
tds.NoError(tx.ScanStructs(&items, `SELECT * FROM "items"`))
|
|
_, err = tx.Exec(`SELECT * FROM "items" WHERE "id" = ?`, 1)
|
|
tds.NoError(err)
|
|
tds.NoError(tx.Commit())
|
|
tds.Equal([]string{
|
|
"[dbv2 - transaction] QUERY [query:=`SELECT * FROM \"items\"`] ",
|
|
"[dbv2 - transaction] EXEC [query:=`SELECT * FROM \"items\" WHERE \"id\" = ?` args:=[1]] ",
|
|
"[dbv2 - transaction] COMMIT",
|
|
}, logger.Messages)
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestCommit() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectCommit()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
tds.NoError(tx.Commit())
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestRollback() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectRollback()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
tds.NoError(tx.Rollback())
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestFrom() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectCommit()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
tds.NotNil(dbv2.From("test"))
|
|
tds.NoError(tx.Commit())
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestScanStructs() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
mock.ExpectQuery(`SELECT "test" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
|
|
mock.ExpectCommit()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
var items []testActionItem
|
|
tds.NoError(tx.ScanStructs(&items, `SELECT * FROM "items"`))
|
|
tds.Len(items, 2)
|
|
tds.Equal("111 Test Addr", items[0].Address)
|
|
tds.Equal("Test1", items[0].Name)
|
|
|
|
tds.Equal("211 Test Addr", items[1].Address)
|
|
tds.Equal("Test2", items[1].Name)
|
|
|
|
items = items[0:0]
|
|
tds.EqualError(tx.ScanStructs(items, `SELECT * FROM "items"`),
|
|
"db: type must be a pointer to a slice when scanning into structs")
|
|
tds.EqualError(tx.ScanStructs(&testActionItem{}, `SELECT * FROM "items"`),
|
|
"db: type must be a pointer to a slice when scanning into structs")
|
|
tds.EqualError(tx.ScanStructs(&items, `SELECT "test" FROM "items"`),
|
|
`db: unable to find corresponding field to column "test" returned by query`)
|
|
tds.NoError(tx.Commit())
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestScanStruct() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(`SELECT \* FROM "items" LIMIT 1`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).FromCSVString("111 Test Addr,Test1"))
|
|
|
|
mock.ExpectQuery(`SELECT "test" FROM "items" LIMIT 1`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
|
|
mock.ExpectCommit()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
var item testActionItem
|
|
found, err := tx.ScanStruct(&item, `SELECT * FROM "items" LIMIT 1`)
|
|
tds.NoError(err)
|
|
tds.True(found)
|
|
tds.Equal("111 Test Addr", item.Address)
|
|
tds.Equal("Test1", item.Name)
|
|
|
|
_, err = tx.ScanStruct(item, `SELECT * FROM "items" LIMIT 1`)
|
|
tds.EqualError(err, "db: type must be a pointer to a struct when scanning into a struct")
|
|
_, err = tx.ScanStruct([]testActionItem{}, `SELECT * FROM "items" LIMIT 1`)
|
|
tds.EqualError(err, "db: type must be a pointer to a struct when scanning into a struct")
|
|
_, err = tx.ScanStruct(&item, `SELECT "test" FROM "items" LIMIT 1`)
|
|
tds.EqualError(err, `db: unable to find corresponding field to column "test" returned by query`)
|
|
tds.NoError(tx.Commit())
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestScanVals() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(`SELECT "id" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
|
|
mock.ExpectQuery(`SELECT "id" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
|
|
mock.ExpectQuery(`SELECT "id" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
|
|
mock.ExpectCommit()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
var ids []uint32
|
|
tds.NoError(tx.ScanVals(&ids, `SELECT "id" FROM "items"`))
|
|
tds.Len(ids, 5)
|
|
|
|
tds.EqualError(tx.ScanVals([]uint32{}, `SELECT "id" FROM "items"`),
|
|
"db: type must be a pointer to a slice when scanning into vals")
|
|
tds.EqualError(tx.ScanVals(testActionItem{}, `SELECT "id" FROM "items"`),
|
|
"db: type must be a pointer to a slice when scanning into vals")
|
|
tds.NoError(tx.Commit())
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestScanVal() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(`SELECT "id" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("10"))
|
|
mock.ExpectCommit()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
var id int64
|
|
found, err := tx.ScanVal(&id, `SELECT "id" FROM "items"`)
|
|
tds.NoError(err)
|
|
tds.Equal(int64(10), id)
|
|
tds.True(found)
|
|
|
|
found, err = tx.ScanVal([]int64{}, `SELECT "id" FROM "items"`)
|
|
tds.False(found)
|
|
tds.EqualError(err, "db: type must be a pointer when scanning into val")
|
|
found, err = tx.ScanVal(10, `SELECT "id" FROM "items"`)
|
|
tds.False(found)
|
|
tds.EqualError(err, "db: type must be a pointer when scanning into val")
|
|
tds.NoError(tx.Commit())
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestExec() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectExec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE \("name" IS NULL\)`).
|
|
WithArgs().
|
|
WillReturnResult(sqlmock.NewResult(0, 0))
|
|
|
|
mock.ExpectExec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE \("name" IS NULL\)`).
|
|
WithArgs().
|
|
WillReturnError(errors.New("mock error"))
|
|
mock.ExpectCommit()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
_, err = tx.Exec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE ("name" IS NULL)`)
|
|
tds.NoError(err)
|
|
_, err = tx.Exec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE ("name" IS NULL)`)
|
|
tds.EqualError(err, "db: mock error")
|
|
tds.NoError(tx.Commit())
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestQuery() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnError(errors.New("mock error"))
|
|
mock.ExpectCommit()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
_, err = tx.Query(`SELECT * FROM "items"`) //nolint:rowserrcheck // not checking row scan
|
|
tds.NoError(err, "dbv2 - mock error")
|
|
|
|
_, err = tx.Query(`SELECT * FROM "items"`) //nolint:rowserrcheck // not checking row scan
|
|
tds.EqualError(err, "db: mock error")
|
|
tds.NoError(tx.Commit())
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestQueryRow() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
|
|
mock.ExpectQuery(`SELECT \* FROM "items"`).
|
|
WithArgs().
|
|
WillReturnError(errors.New("mock error"))
|
|
mock.ExpectCommit()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
rows := tx.QueryRow(`SELECT * FROM "items"`)
|
|
var address string
|
|
var name string
|
|
tds.NoError(rows.Scan(&address, &name))
|
|
|
|
rows = tx.QueryRow(`SELECT * FROM "items"`)
|
|
tds.EqualError(rows.Scan(&address, &name), "db: mock error")
|
|
tds.NoError(tx.Commit())
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestWrap() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
mock.ExpectCommit()
|
|
mock.ExpectBegin()
|
|
mock.ExpectRollback()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
tds.NoError(tx.Wrap(func() error {
|
|
return nil
|
|
}))
|
|
tx, err = db.Begin()
|
|
tds.NoError(err)
|
|
tds.EqualError(tx.Wrap(func() error {
|
|
return errors.New("tx error")
|
|
}), "db: tx error")
|
|
}
|
|
|
|
func (tds *txdatabaseSuite) TestDataRace() {
|
|
mDB, mock, err := sqlmock.New()
|
|
tds.NoError(err)
|
|
mock.ExpectBegin()
|
|
db := dbv2.New("mock", mDB)
|
|
tx, err := db.Begin()
|
|
tds.NoError(err)
|
|
|
|
const concurrency = 10
|
|
|
|
for i := 0; i < concurrency; i++ {
|
|
mock.ExpectQuery(`SELECT "address", "name" FROM "items"`).
|
|
WithArgs().
|
|
WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
|
|
FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
for i := 0; i < concurrency; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
sql := tx.From("items").Limit(1)
|
|
var item testActionItem
|
|
found, err := sql.ScanStruct(&item)
|
|
tds.NoError(err)
|
|
tds.True(found)
|
|
tds.Equal(item.Address, "111 Test Addr")
|
|
tds.Equal(item.Name, "Test1")
|
|
}()
|
|
}
|
|
|
|
wg.Wait()
|
|
mock.ExpectCommit()
|
|
tds.NoError(tx.Commit())
|
|
}
|
|
|
|
func TestTxDatabaseSuite(t *testing.T) {
|
|
suite.Run(t, new(txdatabaseSuite))
|
|
}
|