mydb/internal/testsuite/record_suite.go

429 lines
8.5 KiB
Go
Raw Normal View History

2023-09-18 15:15:42 +08:00
package testsuite
import (
"context"
"database/sql"
"fmt"
"time"
"git.hexq.cn/tiglog/mydb"
"git.hexq.cn/tiglog/mydb/internal/sqlbuilder"
"github.com/stretchr/testify/suite"
)
type AccountsStore struct {
mydb.Collection
}
type UsersStore struct {
mydb.Collection
}
type LogsStore struct {
mydb.Collection
}
func Accounts(sess mydb.Session) mydb.Store {
return &AccountsStore{sess.Collection("accounts")}
}
func Users(sess mydb.Session) *UsersStore {
return &UsersStore{sess.Collection("users")}
}
func Logs(sess mydb.Session) *LogsStore {
return &LogsStore{sess.Collection("logs")}
}
type Log struct {
ID uint64 `db:"id,omitempty"`
Message string `db:"message"`
}
func (*Log) Store(sess mydb.Session) mydb.Store {
return Logs(sess)
}
var _ = mydb.Store(&LogsStore{})
type Account struct {
ID uint64 `db:"id,omitempty"`
Name string `db:"name"`
Disabled bool `db:"disabled"`
CreatedAt *time.Time `db:"created_at,omitempty"`
}
func (*Account) Store(sess mydb.Session) mydb.Store {
return Accounts(sess)
}
func (account *Account) AfterCreate(sess mydb.Session) error {
message := fmt.Sprintf("Account %q was created.", account.Name)
return sess.Save(&Log{Message: message})
}
type User struct {
ID uint64 `db:"id,omitempty"`
AccountID uint64 `db:"account_id"`
Username string `db:"username"`
}
func (user *User) AfterCreate(sess mydb.Session) error {
message := fmt.Sprintf("User %q was created.", user.Username)
return sess.Save(&Log{Message: message})
}
func (*User) Store(sess mydb.Session) mydb.Store {
return Users(sess)
}
type RecordTestSuite struct {
suite.Suite
Helper
}
func (s *RecordTestSuite) AfterTest(suiteName, testName string) {
err := s.TearDown()
s.NoError(err)
}
func (s *RecordTestSuite) BeforeTest(suiteName, testName string) {
err := s.TearUp()
s.NoError(err)
sess := s.Helper.Session()
cols, err := sess.Collections()
s.NoError(err)
for i := range cols {
err = cols[i].Truncate()
s.NoError(err)
}
}
func (s *RecordTestSuite) TestFindOne() {
var err error
sess := s.Session()
user := User{Username: "jose"}
err = sess.Save(&user)
s.NoError(err)
s.NotZero(user.ID)
userID := user.ID
user = User{}
err = Users(sess).Find(userID).One(&user)
s.NoError(err)
s.Equal("jose", user.Username)
user = User{}
err = sess.Get(&user, mydb.Cond{"username": "jose"})
s.NoError(err)
s.Equal("jose", user.Username)
user.Username = "Catalina"
err = sess.Save(&user)
s.NoError(err)
user = User{}
err = sess.Get(&user, userID)
s.NoError(err)
s.Equal("Catalina", user.Username)
err = sess.Delete(&user)
s.NoError(err)
err = sess.Get(&user, userID)
s.Error(err)
err = sess.Collection("users").
Find(userID).
One(&user)
s.Error(err)
}
func (s *RecordTestSuite) TestAccounts() {
sess := s.Session()
user := User{Username: "peter"}
err := sess.Save(&user)
s.NoError(err)
user = User{Username: "peter"}
err = sess.Save(&user)
s.Error(err, "username should be unique")
account1 := Account{Name: "skywalker"}
err = sess.Save(&account1)
s.NoError(err)
account2 := Account{}
err = sess.Get(&account2, account1.ID)
s.NoError(err)
s.Equal(account1.Name, account2.Name)
var account3 Account
err = sess.Get(&account3, account1.ID)
s.NoError(err)
s.Equal(account1.Name, account3.Name)
var a Account
err = sess.Get(&a, account1.ID)
s.NoError(err)
s.NotNil(a)
account1.Disabled = true
err = sess.Save(&account1)
s.NoError(err)
count, err := Accounts(sess).Count()
s.NoError(err)
s.Equal(uint64(1), count)
err = sess.Delete(&account1)
s.NoError(err)
count, err = Accounts(sess).Find().Count()
s.NoError(err)
s.Zero(count)
}
func (s *RecordTestSuite) TestDelete() {
sess := s.Session()
account := Account{Name: "Pressly"}
err := sess.Save(&account)
s.NoError(err)
s.NotZero(account.ID)
// Delete by query -- without callbacks
err = Accounts(sess).
Find(account.ID).
Delete()
s.NoError(err)
count, err := Accounts(sess).Find(account.ID).Count()
s.Zero(count)
s.NoError(err)
}
func (s *RecordTestSuite) TestSlices() {
sess := s.Session()
err := sess.Save(&Account{Name: "Apple"})
s.NoError(err)
err = sess.Save(&Account{Name: "Google"})
s.NoError(err)
var accounts []*Account
err = Accounts(sess).
Find(mydb.Cond{}).
All(&accounts)
s.NoError(err)
s.Len(accounts, 2)
}
func (s *RecordTestSuite) TestSelectOnlyIDs() {
sess := s.Session()
err := sess.Save(&Account{Name: "Apple"})
s.NoError(err)
err = sess.Save(&Account{Name: "Google"})
s.NoError(err)
var ids []struct {
Id int64 `db:"id"`
}
err = Accounts(sess).
Find().
Select("id").All(&ids)
s.NoError(err)
s.Len(ids, 2)
s.NotEmpty(ids[0])
}
func (s *RecordTestSuite) TestTx() {
sess := s.Session()
user := User{Username: "peter"}
err := sess.Save(&user)
s.NoError(err)
// This transaction should fail because user is a UNIQUE value and we already
// have a "peter".
err = sess.Tx(func(tx mydb.Session) error {
return tx.Save(&User{Username: "peter"})
})
s.Error(err)
// This transaction should fail because user is a UNIQUE value and we already
// have a "peter".
err = sess.Tx(func(tx mydb.Session) error {
return tx.Save(&User{Username: "peter"})
})
s.Error(err)
// This transaction will have no errors, but we'll produce one in order for
// it to rollback at the last moment.
err = sess.Tx(func(tx mydb.Session) error {
if err := tx.Save(&User{Username: "Joe"}); err != nil {
return err
}
if err := tx.Save(&User{Username: "Cool"}); err != nil {
return err
}
return fmt.Errorf("Rolling back for no reason.")
})
s.Error(err)
// Attempt to add two new unique values, if the transaction above had not
// been rolled back this transaction will fail.
err = sess.Tx(func(tx mydb.Session) error {
if err := tx.Save(&User{Username: "Joe"}); err != nil {
return err
}
if err := tx.Save(&User{Username: "Cool"}); err != nil {
return err
}
return nil
})
s.NoError(err)
// If the transaction above was successful, this one will fail.
err = sess.Tx(func(tx mydb.Session) error {
if err := tx.Save(&User{Username: "Joe"}); err != nil {
return err
}
if err := tx.Save(&User{Username: "Cool"}); err != nil {
return err
}
return nil
})
s.Error(err)
}
func (s *RecordTestSuite) TestInheritedTx() {
sess := s.Session()
sqlDB := sess.Driver().(*sql.DB)
user := User{Username: "peter"}
err := sess.Save(&user)
s.NoError(err)
// Create a transaction
2023-09-18 15:37:08 +08:00
sqlTx, err := sqlDB.Begin()
2023-09-18 15:15:42 +08:00
s.NoError(err)
// And pass that transaction to upper/db, this whole session is a transaction.
upperTx, err := sqlbuilder.BindTx(s.Adapter(), sqlTx)
s.NoError(err)
// Should fail because user is a UNIQUE value and we already have a "peter".
err = upperTx.Save(&User{Username: "peter"})
s.Error(err)
// The transaction is controlled outside upper/mydb.
err = sqlTx.Rollback()
s.NoError(err)
// The sqlTx is worthless now.
err = upperTx.Save(&User{Username: "peter-2"})
s.Error(err)
// But we can create a new one.
2023-09-18 15:37:08 +08:00
sqlTx, err = sqlDB.Begin()
2023-09-18 15:15:42 +08:00
s.NoError(err)
s.NotNil(sqlTx)
// And create another session.
upperTx, err = sqlbuilder.BindTx(s.Adapter(), sqlTx)
s.NoError(err)
// Adding two new values.
err = upperTx.Save(&User{Username: "Joe-2"})
s.NoError(err)
err = upperTx.Save(&User{Username: "Cool-2"})
s.NoError(err)
// And a value that is going to be rolled back.
err = upperTx.Save(&Account{Name: "Rolled back"})
s.NoError(err)
// This session happens to be a transaction, let's rollback everything.
err = sqlTx.Rollback()
s.NoError(err)
// Start again.
2023-09-18 15:37:08 +08:00
sqlTx, err = sqlDB.Begin()
2023-09-18 15:15:42 +08:00
s.NoError(err)
tx, err := sqlbuilder.BindTx(s.Adapter(), sqlTx)
s.NoError(err)
// Attempt to add two unique values.
err = tx.Save(&User{Username: "Joe-2"})
s.NoError(err)
err = tx.Save(&User{Username: "Cool-2"})
s.NoError(err)
// And a value that is going to be commited.
err = tx.Save(&Account{Name: "Commited!"})
s.NoError(err)
// Yes, commit them.
err = sqlTx.Commit()
s.NoError(err)
}
func (s *RecordTestSuite) TestUnknownCollection() {
var err error
sess := s.Session()
err = sess.Save(nil)
s.Error(err)
_, err = sess.Collection("users").Insert(&User{Username: "Foo"})
s.NoError(err)
}
func (s *RecordTestSuite) TestContextCanceled() {
var err error
sess := s.Session()
err = sess.Collection("users").Truncate()
s.NoError(err)
{
ctx, cancelFn := context.WithTimeout(context.Background(), time.Minute)
canceledSess := sess.WithContext(ctx)
cancelFn()
user := User{Username: "foo"}
err = canceledSess.Save(&user)
s.Error(err)
c, err := sess.Collection("users").Count()
s.NoError(err)
s.Equal(uint64(0), c)
}
}