Files
db/database_test.go
whatandClaude 304d553b3c docs: Add CLAUDE.md with codebase guidance
Create comprehensive documentation for future Claude Code instances working in this repository, including:
- Development commands for testing, building, and code quality
- Core architecture overview of the SQL query builder system
- Directory structure and component explanations
- Testing patterns and conventions
- Key dependencies and their purposes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-27 15:47:28 +08:00

760 lines
22 KiB
Go

package db_test
import (
"context"
"fmt"
"sync"
"testing"
dbv2 "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/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))
}