1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2024-11-21 13:35:49 +02:00
pocketbase/daos/base_test.go

871 lines
24 KiB
Go

package daos_test
import (
"errors"
"testing"
"time"
"github.com/pocketbase/pocketbase/daos"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/tests"
)
func TestNew(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
dao := daos.New(testApp.DB())
if dao.DB() != testApp.DB() {
t.Fatal("The 2 db instances are different")
}
}
func TestNewMultiDB(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
dao := daos.NewMultiDB(testApp.Dao().ConcurrentDB(), testApp.Dao().NonconcurrentDB())
if dao.DB() != testApp.Dao().ConcurrentDB() {
t.Fatal("[db-concurrentDB] The 2 db instances are different")
}
if dao.ConcurrentDB() != testApp.Dao().ConcurrentDB() {
t.Fatal("[concurrentDB-concurrentDB] The 2 db instances are different")
}
if dao.NonconcurrentDB() != testApp.Dao().NonconcurrentDB() {
t.Fatal("[nonconcurrentDB-nonconcurrentDB] The 2 db instances are different")
}
}
func TestDaoClone(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
hookCalls := map[string]int{}
dao := daos.NewMultiDB(testApp.Dao().ConcurrentDB(), testApp.Dao().NonconcurrentDB())
dao.MaxLockRetries = 1
dao.ModelQueryTimeout = 2
dao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
hookCalls["BeforeDeleteFunc"]++
return action()
}
dao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
hookCalls["BeforeUpdateFunc"]++
return action()
}
dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
hookCalls["BeforeCreateFunc"]++
return action()
}
dao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
hookCalls["AfterDeleteFunc"]++
return nil
}
dao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
hookCalls["AfterUpdateFunc"]++
return nil
}
dao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
hookCalls["AfterCreateFunc"]++
return nil
}
clone := dao.Clone()
clone.MaxLockRetries = 3
clone.ModelQueryTimeout = 4
clone.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
hookCalls["NewAfterCreateFunc"]++
return nil
}
if dao.MaxLockRetries == clone.MaxLockRetries {
t.Fatal("Expected different MaxLockRetries")
}
if dao.ModelQueryTimeout == clone.ModelQueryTimeout {
t.Fatal("Expected different ModelQueryTimeout")
}
emptyAction := func() error { return nil }
// trigger hooks
dao.BeforeDeleteFunc(nil, nil, emptyAction)
dao.BeforeUpdateFunc(nil, nil, emptyAction)
dao.BeforeCreateFunc(nil, nil, emptyAction)
dao.AfterDeleteFunc(nil, nil)
dao.AfterUpdateFunc(nil, nil)
dao.AfterCreateFunc(nil, nil)
clone.BeforeDeleteFunc(nil, nil, emptyAction)
clone.BeforeUpdateFunc(nil, nil, emptyAction)
clone.BeforeCreateFunc(nil, nil, emptyAction)
clone.AfterDeleteFunc(nil, nil)
clone.AfterUpdateFunc(nil, nil)
clone.AfterCreateFunc(nil, nil)
expectations := []struct {
hook string
total int
}{
{"BeforeDeleteFunc", 2},
{"BeforeUpdateFunc", 2},
{"BeforeCreateFunc", 2},
{"AfterDeleteFunc", 2},
{"AfterUpdateFunc", 2},
{"AfterCreateFunc", 1},
{"NewAfterCreateFunc", 1},
}
for _, e := range expectations {
if hookCalls[e.hook] != e.total {
t.Errorf("Expected %s to be caleed %d", e.hook, e.total)
}
}
}
func TestDaoWithoutHooks(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
hookCalls := map[string]int{}
dao := daos.NewMultiDB(testApp.Dao().ConcurrentDB(), testApp.Dao().NonconcurrentDB())
dao.MaxLockRetries = 1
dao.ModelQueryTimeout = 2
dao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
hookCalls["BeforeDeleteFunc"]++
return action()
}
dao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
hookCalls["BeforeUpdateFunc"]++
return action()
}
dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
hookCalls["BeforeCreateFunc"]++
return action()
}
dao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
hookCalls["AfterDeleteFunc"]++
return nil
}
dao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
hookCalls["AfterUpdateFunc"]++
return nil
}
dao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
hookCalls["AfterCreateFunc"]++
return nil
}
new := dao.WithoutHooks()
if new.MaxLockRetries != dao.MaxLockRetries {
t.Fatalf("Expected MaxLockRetries %d, got %d", new.Clone().MaxLockRetries, dao.MaxLockRetries)
}
if new.ModelQueryTimeout != dao.ModelQueryTimeout {
t.Fatalf("Expected ModelQueryTimeout %d, got %d", new.Clone().ModelQueryTimeout, dao.ModelQueryTimeout)
}
if new.BeforeDeleteFunc != nil {
t.Fatal("Expected BeforeDeleteFunc to be nil")
}
if new.BeforeUpdateFunc != nil {
t.Fatal("Expected BeforeUpdateFunc to be nil")
}
if new.BeforeCreateFunc != nil {
t.Fatal("Expected BeforeCreateFunc to be nil")
}
if new.AfterDeleteFunc != nil {
t.Fatal("Expected AfterDeleteFunc to be nil")
}
if new.AfterUpdateFunc != nil {
t.Fatal("Expected AfterUpdateFunc to be nil")
}
if new.AfterCreateFunc != nil {
t.Fatal("Expected AfterCreateFunc to be nil")
}
}
func TestDaoModelQuery(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
dao := daos.New(testApp.DB())
scenarios := []struct {
model models.Model
expected string
}{
{
&models.Collection{},
"SELECT {{_collections}}.* FROM `_collections`",
},
{
&models.Admin{},
"SELECT {{_admins}}.* FROM `_admins`",
},
{
&models.Request{},
"SELECT {{_requests}}.* FROM `_requests`",
},
}
for i, scenario := range scenarios {
sql := dao.ModelQuery(scenario.model).Build().SQL()
if sql != scenario.expected {
t.Errorf("(%d) Expected select %s, got %s", i, scenario.expected, sql)
}
}
}
func TestDaoModelQueryCancellation(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
dao := daos.New(testApp.DB())
m := &models.Admin{}
if err := dao.ModelQuery(m).One(m); err != nil {
t.Fatalf("Failed to execute control query: %v", err)
}
dao.ModelQueryTimeout = 0 * time.Millisecond
if err := dao.ModelQuery(m).One(m); err == nil {
t.Fatal("Expected to be cancelled, got nil")
}
}
func TestDaoFindById(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
scenarios := []struct {
model models.Model
id string
expectError bool
}{
// missing id
{
&models.Collection{},
"missing",
true,
},
// existing collection id
{
&models.Collection{},
"wsmn24bux7wo113",
false,
},
// existing admin id
{
&models.Admin{},
"sbmbsdb40jyxf7h",
false,
},
}
for i, scenario := range scenarios {
err := testApp.Dao().FindById(scenario.model, scenario.id)
hasErr := err != nil
if hasErr != scenario.expectError {
t.Errorf("(%d) Expected %v, got %v", i, scenario.expectError, err)
}
if !scenario.expectError && scenario.id != scenario.model.GetId() {
t.Errorf("(%d) Expected model with id %v, got %v", i, scenario.id, scenario.model.GetId())
}
}
}
func TestDaoRunInTransaction(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
// failed nested transaction
testApp.Dao().RunInTransaction(func(txDao *daos.Dao) error {
admin, _ := txDao.FindAdminByEmail("test@example.com")
return txDao.RunInTransaction(func(tx2Dao *daos.Dao) error {
if err := tx2Dao.DeleteAdmin(admin); err != nil {
t.Fatal(err)
}
return errors.New("test error")
})
})
// admin should still exist
admin1, _ := testApp.Dao().FindAdminByEmail("test@example.com")
if admin1 == nil {
t.Fatal("Expected admin test@example.com to not be deleted")
}
// successful nested transaction
testApp.Dao().RunInTransaction(func(txDao *daos.Dao) error {
admin, _ := txDao.FindAdminByEmail("test@example.com")
return txDao.RunInTransaction(func(tx2Dao *daos.Dao) error {
return tx2Dao.DeleteAdmin(admin)
})
})
// admin should have been deleted
admin2, _ := testApp.Dao().FindAdminByEmail("test@example.com")
if admin2 != nil {
t.Fatalf("Expected admin test@example.com to be deleted, found %v", admin2)
}
}
func TestDaoSaveCreate(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
model := &models.Admin{}
model.Email = "test_new@example.com"
model.Avatar = 8
if err := testApp.Dao().Save(model); err != nil {
t.Fatal(err)
}
// refresh
model, _ = testApp.Dao().FindAdminByEmail("test_new@example.com")
if model.Avatar != 8 {
t.Fatalf("Expected model avatar field to be 8, got %v", model.Avatar)
}
expectedHooks := []string{"OnModelBeforeCreate", "OnModelAfterCreate"}
for _, h := range expectedHooks {
if v, ok := testApp.EventCalls[h]; !ok || v != 1 {
t.Fatalf("Expected event %s to be called exactly one time, got %d", h, v)
}
}
}
func TestDaoSaveWithInsertId(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
model := &models.Admin{}
model.Id = "test"
model.Email = "test_new@example.com"
model.MarkAsNew()
if err := testApp.Dao().Save(model); err != nil {
t.Fatal(err)
}
// refresh
model, _ = testApp.Dao().FindAdminById("test")
if model == nil {
t.Fatal("Failed to find admin with id 'test'")
}
expectedHooks := []string{"OnModelBeforeCreate", "OnModelAfterCreate"}
for _, h := range expectedHooks {
if v, ok := testApp.EventCalls[h]; !ok || v != 1 {
t.Fatalf("Expected event %s to be called exactly one time, got %d", h, v)
}
}
}
func TestDaoSaveUpdate(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
model, _ := testApp.Dao().FindAdminByEmail("test@example.com")
model.Avatar = 8
if err := testApp.Dao().Save(model); err != nil {
t.Fatal(err)
}
// refresh
model, _ = testApp.Dao().FindAdminByEmail("test@example.com")
if model.Avatar != 8 {
t.Fatalf("Expected model avatar field to be updated to 8, got %v", model.Avatar)
}
expectedHooks := []string{"OnModelBeforeUpdate", "OnModelAfterUpdate"}
for _, h := range expectedHooks {
if v, ok := testApp.EventCalls[h]; !ok || v != 1 {
t.Fatalf("Expected event %s to be called exactly one time, got %d", h, v)
}
}
}
type dummyColumnValueMapper struct {
models.Admin
}
func (a *dummyColumnValueMapper) ColumnValueMap() map[string]any {
return map[string]any{
"email": a.Email,
"passwordHash": a.PasswordHash,
"tokenKey": "custom_token_key",
}
}
func TestDaoSaveWithColumnValueMapper(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
model := &dummyColumnValueMapper{}
model.Id = "test_mapped_id" // explicitly set an id
model.Email = "test_mapped_create@example.com"
model.TokenKey = "test_unmapped_token_key" // not used in the map
model.SetPassword("123456")
model.MarkAsNew()
if err := testApp.Dao().Save(model); err != nil {
t.Fatal(err)
}
createdModel, _ := testApp.Dao().FindAdminById("test_mapped_id")
if createdModel == nil {
t.Fatal("[create] Failed to find model with id 'test_mapped_id'")
}
if createdModel.Email != model.Email {
t.Fatalf("Expected model with email %q, got %q", model.Email, createdModel.Email)
}
if createdModel.TokenKey != "custom_token_key" {
t.Fatalf("Expected model with tokenKey %q, got %q", "custom_token_key", createdModel.TokenKey)
}
model.Email = "test_mapped_update@example.com"
model.Avatar = 9 // not mapped and expect to be ignored
if err := testApp.Dao().Save(model); err != nil {
t.Fatal(err)
}
updatedModel, _ := testApp.Dao().FindAdminById("test_mapped_id")
if updatedModel == nil {
t.Fatal("[update] Failed to find model with id 'test_mapped_id'")
}
if updatedModel.Email != model.Email {
t.Fatalf("Expected model with email %q, got %q", model.Email, createdModel.Email)
}
if updatedModel.Avatar != 0 {
t.Fatalf("Expected model avatar 0, got %v", updatedModel.Avatar)
}
}
func TestDaoDelete(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
model, _ := testApp.Dao().FindAdminByEmail("test@example.com")
if err := testApp.Dao().Delete(model); err != nil {
t.Fatal(err)
}
model, _ = testApp.Dao().FindAdminByEmail("test@example.com")
if model != nil {
t.Fatalf("Expected model to be deleted, found %v", model)
}
expectedHooks := []string{"OnModelBeforeDelete", "OnModelAfterDelete"}
for _, h := range expectedHooks {
if v, ok := testApp.EventCalls[h]; !ok || v != 1 {
t.Fatalf("Expected event %s to be called exactly one time, got %d", h, v)
}
}
}
func TestDaoRetryCreate(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
// init mock retry dao
retryBeforeCreateHookCalls := 0
retryAfterCreateHookCalls := 0
retryDao := daos.New(testApp.DB())
retryDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
retryBeforeCreateHookCalls++
return errors.New("database is locked")
}
retryDao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
retryAfterCreateHookCalls++
return nil
}
model := &models.Admin{Email: "new@example.com"}
if err := retryDao.Save(model); err != nil {
t.Fatalf("Expected nil after retry, got error: %v", err)
}
// the before hook is expected to be called only once because
// it is ignored after the first "database is locked" error
if retryBeforeCreateHookCalls != 1 {
t.Fatalf("Expected before hook calls to be 1, got %d", retryBeforeCreateHookCalls)
}
if retryAfterCreateHookCalls != 1 {
t.Fatalf("Expected after hook calls to be 1, got %d", retryAfterCreateHookCalls)
}
// with non-locking error
retryBeforeCreateHookCalls = 0
retryAfterCreateHookCalls = 0
retryDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
retryBeforeCreateHookCalls++
return errors.New("non-locking error")
}
dummy := &models.Admin{Email: "test@example.com"}
if err := retryDao.Save(dummy); err == nil {
t.Fatal("Expected error, got nil")
}
if retryBeforeCreateHookCalls != 1 {
t.Fatalf("Expected before hook calls to be 1, got %d", retryBeforeCreateHookCalls)
}
if retryAfterCreateHookCalls != 0 {
t.Fatalf("Expected after hook calls to be 0, got %d", retryAfterCreateHookCalls)
}
}
func TestDaoRetryUpdate(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
model, err := testApp.Dao().FindAdminByEmail("test@example.com")
if err != nil {
t.Fatal(err)
}
// init mock retry dao
retryBeforeUpdateHookCalls := 0
retryAfterUpdateHookCalls := 0
retryDao := daos.New(testApp.DB())
retryDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
retryBeforeUpdateHookCalls++
return errors.New("database is locked")
}
retryDao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
retryAfterUpdateHookCalls++
return nil
}
if err := retryDao.Save(model); err != nil {
t.Fatalf("Expected nil after retry, got error: %v", err)
}
// the before hook is expected to be called only once because
// it is ignored after the first "database is locked" error
if retryBeforeUpdateHookCalls != 1 {
t.Fatalf("Expected before hook calls to be 1, got %d", retryBeforeUpdateHookCalls)
}
if retryAfterUpdateHookCalls != 1 {
t.Fatalf("Expected after hook calls to be 1, got %d", retryAfterUpdateHookCalls)
}
// with non-locking error
retryBeforeUpdateHookCalls = 0
retryAfterUpdateHookCalls = 0
retryDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
retryBeforeUpdateHookCalls++
return errors.New("non-locking error")
}
if err := retryDao.Save(model); err == nil {
t.Fatal("Expected error, got nil")
}
if retryBeforeUpdateHookCalls != 1 {
t.Fatalf("Expected before hook calls to be 1, got %d", retryBeforeUpdateHookCalls)
}
if retryAfterUpdateHookCalls != 0 {
t.Fatalf("Expected after hook calls to be 0, got %d", retryAfterUpdateHookCalls)
}
}
func TestDaoRetryDelete(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
// init mock retry dao
retryBeforeDeleteHookCalls := 0
retryAfterDeleteHookCalls := 0
retryDao := daos.New(testApp.DB())
retryDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
retryBeforeDeleteHookCalls++
return errors.New("database is locked")
}
retryDao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
retryAfterDeleteHookCalls++
return nil
}
model, _ := retryDao.FindAdminByEmail("test@example.com")
if err := retryDao.Delete(model); err != nil {
t.Fatalf("Expected nil after retry, got error: %v", err)
}
// the before hook is expected to be called only once because
// it is ignored after the first "database is locked" error
if retryBeforeDeleteHookCalls != 1 {
t.Fatalf("Expected before hook calls to be 1, got %d", retryBeforeDeleteHookCalls)
}
if retryAfterDeleteHookCalls != 1 {
t.Fatalf("Expected after hook calls to be 1, got %d", retryAfterDeleteHookCalls)
}
// with non-locking error
retryBeforeDeleteHookCalls = 0
retryAfterDeleteHookCalls = 0
retryDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
retryBeforeDeleteHookCalls++
return errors.New("non-locking error")
}
dummy := &models.Admin{}
dummy.RefreshId()
dummy.MarkAsNotNew()
if err := retryDao.Delete(dummy); err == nil {
t.Fatal("Expected error, got nil")
}
if retryBeforeDeleteHookCalls != 1 {
t.Fatalf("Expected before hook calls to be 1, got %d", retryBeforeDeleteHookCalls)
}
if retryAfterDeleteHookCalls != 0 {
t.Fatalf("Expected after hook calls to be 0, got %d", retryAfterDeleteHookCalls)
}
}
func TestDaoBeforeHooksError(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
baseDao := testApp.Dao()
baseDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
return errors.New("before_create")
}
baseDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
return errors.New("before_update")
}
baseDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
return errors.New("before_delete")
}
existingModel, _ := testApp.Dao().FindAdminByEmail("test@example.com")
// test create error
// ---
newModel := &models.Admin{}
if err := baseDao.Save(newModel); err.Error() != "before_create" {
t.Fatalf("Expected before_create error, got %v", err)
}
// test update error
// ---
if err := baseDao.Save(existingModel); err.Error() != "before_update" {
t.Fatalf("Expected before_update error, got %v", err)
}
// test delete error
// ---
if err := baseDao.Delete(existingModel); err.Error() != "before_delete" {
t.Fatalf("Expected before_delete error, got %v", err)
}
}
func TestDaoTransactionHooksCallsOnFailure(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
beforeCreateFuncCalls := 0
beforeUpdateFuncCalls := 0
beforeDeleteFuncCalls := 0
afterCreateFuncCalls := 0
afterUpdateFuncCalls := 0
afterDeleteFuncCalls := 0
baseDao := testApp.Dao()
baseDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
beforeCreateFuncCalls++
return action()
}
baseDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
beforeUpdateFuncCalls++
return action()
}
baseDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
beforeDeleteFuncCalls++
return action()
}
baseDao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
afterCreateFuncCalls++
return nil
}
baseDao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
afterUpdateFuncCalls++
return nil
}
baseDao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
afterDeleteFuncCalls++
return nil
}
existingModel, _ := testApp.Dao().FindAdminByEmail("test@example.com")
baseDao.RunInTransaction(func(txDao1 *daos.Dao) error {
return txDao1.RunInTransaction(func(txDao2 *daos.Dao) error {
// test create
// ---
newModel := &models.Admin{}
newModel.Email = "test_new1@example.com"
newModel.SetPassword("123456")
if err := txDao2.Save(newModel); err != nil {
t.Fatal(err)
}
// test update (twice)
// ---
if err := txDao2.Save(existingModel); err != nil {
t.Fatal(err)
}
if err := txDao2.Save(existingModel); err != nil {
t.Fatal(err)
}
// test delete
// ---
if err := txDao2.Delete(existingModel); err != nil {
t.Fatal(err)
}
return errors.New("test_tx_error")
})
})
if beforeCreateFuncCalls != 1 {
t.Fatalf("Expected beforeCreateFuncCalls to be called 1 times, got %d", beforeCreateFuncCalls)
}
if beforeUpdateFuncCalls != 2 {
t.Fatalf("Expected beforeUpdateFuncCalls to be called 2 times, got %d", beforeUpdateFuncCalls)
}
if beforeDeleteFuncCalls != 1 {
t.Fatalf("Expected beforeDeleteFuncCalls to be called 1 times, got %d", beforeDeleteFuncCalls)
}
if afterCreateFuncCalls != 0 {
t.Fatalf("Expected afterCreateFuncCalls to be called 0 times, got %d", afterCreateFuncCalls)
}
if afterUpdateFuncCalls != 0 {
t.Fatalf("Expected afterUpdateFuncCalls to be called 0 times, got %d", afterUpdateFuncCalls)
}
if afterDeleteFuncCalls != 0 {
t.Fatalf("Expected afterDeleteFuncCalls to be called 0 times, got %d", afterDeleteFuncCalls)
}
}
func TestDaoTransactionHooksCallsOnSuccess(t *testing.T) {
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
beforeCreateFuncCalls := 0
beforeUpdateFuncCalls := 0
beforeDeleteFuncCalls := 0
afterCreateFuncCalls := 0
afterUpdateFuncCalls := 0
afterDeleteFuncCalls := 0
baseDao := testApp.Dao()
baseDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
beforeCreateFuncCalls++
return action()
}
baseDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
beforeUpdateFuncCalls++
return action()
}
baseDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
beforeDeleteFuncCalls++
return action()
}
baseDao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
afterCreateFuncCalls++
return nil
}
baseDao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
afterUpdateFuncCalls++
return nil
}
baseDao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
afterDeleteFuncCalls++
return nil
}
existingModel, _ := testApp.Dao().FindAdminByEmail("test@example.com")
baseDao.RunInTransaction(func(txDao1 *daos.Dao) error {
return txDao1.RunInTransaction(func(txDao2 *daos.Dao) error {
// test create
// ---
newModel := &models.Admin{}
newModel.Email = "test_new1@example.com"
newModel.SetPassword("123456")
if err := txDao2.Save(newModel); err != nil {
t.Fatal(err)
}
// test update (twice)
// ---
if err := txDao2.Save(existingModel); err != nil {
t.Fatal(err)
}
if err := txDao2.Save(existingModel); err != nil {
t.Fatal(err)
}
// test delete
// ---
if err := txDao2.Delete(existingModel); err != nil {
t.Fatal(err)
}
return nil
})
})
if beforeCreateFuncCalls != 1 {
t.Fatalf("Expected beforeCreateFuncCalls to be called 1 times, got %d", beforeCreateFuncCalls)
}
if beforeUpdateFuncCalls != 2 {
t.Fatalf("Expected beforeUpdateFuncCalls to be called 2 times, got %d", beforeUpdateFuncCalls)
}
if beforeDeleteFuncCalls != 1 {
t.Fatalf("Expected beforeDeleteFuncCalls to be called 1 times, got %d", beforeDeleteFuncCalls)
}
if afterCreateFuncCalls != 1 {
t.Fatalf("Expected afterCreateFuncCalls to be called 1 times, got %d", afterCreateFuncCalls)
}
if afterUpdateFuncCalls != 2 {
t.Fatalf("Expected afterUpdateFuncCalls to be called 2 times, got %d", afterUpdateFuncCalls)
}
if afterDeleteFuncCalls != 1 {
t.Fatalf("Expected afterDeleteFuncCalls to be called 1 times, got %d", afterDeleteFuncCalls)
}
}