2022-07-09 16:17:41 +02:00
|
|
|
// Package tests provides common helpers and mocks used in PocketBase application tests.
|
2022-07-06 23:19:05 +02:00
|
|
|
package tests
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
|
|
|
|
|
|
|
"github.com/pocketbase/pocketbase/core"
|
|
|
|
"github.com/pocketbase/pocketbase/tools/mailer"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TestApp is a wrapper app instance used for testing.
|
|
|
|
type TestApp struct {
|
|
|
|
*core.BaseApp
|
|
|
|
|
|
|
|
// EventCalls defines a map to inspect which app events
|
|
|
|
// (and how many times) were triggered.
|
|
|
|
EventCalls map[string]int
|
|
|
|
TestMailer *TestMailer
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cleanup resets the test application state and removes the test
|
|
|
|
// app's dataDir from the filesystem.
|
|
|
|
//
|
|
|
|
// After this call, the app instance shouldn't be used anymore.
|
|
|
|
func (t *TestApp) Cleanup() {
|
|
|
|
t.ResetEventCalls()
|
|
|
|
t.ResetBootstrapState()
|
|
|
|
|
|
|
|
if t.DataDir() != "" {
|
|
|
|
os.RemoveAll(t.DataDir())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TestApp) NewMailClient() mailer.Mailer {
|
|
|
|
t.TestMailer.Reset()
|
|
|
|
return t.TestMailer
|
|
|
|
}
|
|
|
|
|
|
|
|
// ResetEventCalls resets the EventCalls counter.
|
|
|
|
func (t *TestApp) ResetEventCalls() {
|
|
|
|
t.EventCalls = make(map[string]int)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewTestApp creates and initializes a full application instance for testing.
|
|
|
|
//
|
|
|
|
// It is the caller's responsibility to call `app.Cleanup()`
|
|
|
|
// when the app is no longer needed.
|
|
|
|
func NewTestApp() (*TestApp, error) {
|
|
|
|
tempDir, err := NewTempDataDir()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
app := core.NewBaseApp(tempDir, "pb_test_env", false)
|
|
|
|
|
|
|
|
// load data dir and db connections
|
|
|
|
if err := app.Bootstrap(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// force disable request logs because the logs db call execute in a separate
|
|
|
|
// go routine and it is possible to panic due to earlier api test completion.
|
|
|
|
app.Settings().Logs.MaxDays = 0
|
|
|
|
|
|
|
|
t := &TestApp{
|
|
|
|
BaseApp: app,
|
|
|
|
EventCalls: make(map[string]int),
|
|
|
|
TestMailer: &TestMailer{},
|
|
|
|
}
|
|
|
|
|
|
|
|
// no need to count since this is executed always
|
|
|
|
// t.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
|
|
|
// t.EventCalls["OnBeforeServe"]++
|
|
|
|
// return nil
|
|
|
|
// })
|
|
|
|
|
|
|
|
t.OnModelBeforeCreate().Add(func(e *core.ModelEvent) error {
|
|
|
|
t.EventCalls["OnModelBeforeCreate"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnModelAfterCreate().Add(func(e *core.ModelEvent) error {
|
|
|
|
t.EventCalls["OnModelAfterCreate"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnModelBeforeUpdate().Add(func(e *core.ModelEvent) error {
|
|
|
|
t.EventCalls["OnModelBeforeUpdate"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnModelAfterUpdate().Add(func(e *core.ModelEvent) error {
|
|
|
|
t.EventCalls["OnModelAfterUpdate"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnModelBeforeDelete().Add(func(e *core.ModelEvent) error {
|
|
|
|
t.EventCalls["OnModelBeforeDelete"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnModelAfterDelete().Add(func(e *core.ModelEvent) error {
|
|
|
|
t.EventCalls["OnModelAfterDelete"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnRecordsListRequest().Add(func(e *core.RecordsListEvent) error {
|
|
|
|
t.EventCalls["OnRecordsListRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnRecordViewRequest().Add(func(e *core.RecordViewEvent) error {
|
|
|
|
t.EventCalls["OnRecordViewRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnRecordBeforeCreateRequest().Add(func(e *core.RecordCreateEvent) error {
|
|
|
|
t.EventCalls["OnRecordBeforeCreateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnRecordAfterCreateRequest().Add(func(e *core.RecordCreateEvent) error {
|
|
|
|
t.EventCalls["OnRecordAfterCreateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnRecordBeforeUpdateRequest().Add(func(e *core.RecordUpdateEvent) error {
|
|
|
|
t.EventCalls["OnRecordBeforeUpdateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnRecordAfterUpdateRequest().Add(func(e *core.RecordUpdateEvent) error {
|
|
|
|
t.EventCalls["OnRecordAfterUpdateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnRecordBeforeDeleteRequest().Add(func(e *core.RecordDeleteEvent) error {
|
|
|
|
t.EventCalls["OnRecordBeforeDeleteRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnRecordAfterDeleteRequest().Add(func(e *core.RecordDeleteEvent) error {
|
|
|
|
t.EventCalls["OnRecordAfterDeleteRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnUsersListRequest().Add(func(e *core.UsersListEvent) error {
|
|
|
|
t.EventCalls["OnUsersListRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnUserViewRequest().Add(func(e *core.UserViewEvent) error {
|
|
|
|
t.EventCalls["OnUserViewRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnUserBeforeCreateRequest().Add(func(e *core.UserCreateEvent) error {
|
|
|
|
t.EventCalls["OnUserBeforeCreateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnUserAfterCreateRequest().Add(func(e *core.UserCreateEvent) error {
|
|
|
|
t.EventCalls["OnUserAfterCreateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnUserBeforeUpdateRequest().Add(func(e *core.UserUpdateEvent) error {
|
|
|
|
t.EventCalls["OnUserBeforeUpdateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnUserAfterUpdateRequest().Add(func(e *core.UserUpdateEvent) error {
|
|
|
|
t.EventCalls["OnUserAfterUpdateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnUserBeforeDeleteRequest().Add(func(e *core.UserDeleteEvent) error {
|
|
|
|
t.EventCalls["OnUserBeforeDeleteRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnUserAfterDeleteRequest().Add(func(e *core.UserDeleteEvent) error {
|
|
|
|
t.EventCalls["OnUserAfterDeleteRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnUserBeforeOauth2Register().Add(func(e *core.UserOauth2RegisterEvent) error {
|
|
|
|
t.EventCalls["OnUserBeforeOauth2Register"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnUserAfterOauth2Register().Add(func(e *core.UserOauth2RegisterEvent) error {
|
|
|
|
t.EventCalls["OnUserAfterOauth2Register"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnUserAuthRequest().Add(func(e *core.UserAuthEvent) error {
|
|
|
|
t.EventCalls["OnUserAuthRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnMailerBeforeAdminResetPasswordSend().Add(func(e *core.MailerAdminEvent) error {
|
|
|
|
t.EventCalls["OnMailerBeforeAdminResetPasswordSend"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnMailerAfterAdminResetPasswordSend().Add(func(e *core.MailerAdminEvent) error {
|
|
|
|
t.EventCalls["OnMailerAfterAdminResetPasswordSend"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnMailerBeforeUserResetPasswordSend().Add(func(e *core.MailerUserEvent) error {
|
|
|
|
t.EventCalls["OnMailerBeforeUserResetPasswordSend"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnMailerAfterUserResetPasswordSend().Add(func(e *core.MailerUserEvent) error {
|
|
|
|
t.EventCalls["OnMailerAfterUserResetPasswordSend"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnMailerBeforeUserVerificationSend().Add(func(e *core.MailerUserEvent) error {
|
|
|
|
t.EventCalls["OnMailerBeforeUserVerificationSend"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnMailerAfterUserVerificationSend().Add(func(e *core.MailerUserEvent) error {
|
|
|
|
t.EventCalls["OnMailerAfterUserVerificationSend"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnMailerBeforeUserChangeEmailSend().Add(func(e *core.MailerUserEvent) error {
|
|
|
|
t.EventCalls["OnMailerBeforeUserChangeEmailSend"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnMailerAfterUserChangeEmailSend().Add(func(e *core.MailerUserEvent) error {
|
|
|
|
t.EventCalls["OnMailerAfterUserChangeEmailSend"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnRealtimeConnectRequest().Add(func(e *core.RealtimeConnectEvent) error {
|
|
|
|
t.EventCalls["OnRealtimeConnectRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnRealtimeBeforeSubscribeRequest().Add(func(e *core.RealtimeSubscribeEvent) error {
|
|
|
|
t.EventCalls["OnRealtimeBeforeSubscribeRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnRealtimeAfterSubscribeRequest().Add(func(e *core.RealtimeSubscribeEvent) error {
|
|
|
|
t.EventCalls["OnRealtimeAfterSubscribeRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnSettingsListRequest().Add(func(e *core.SettingsListEvent) error {
|
|
|
|
t.EventCalls["OnSettingsListRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnSettingsBeforeUpdateRequest().Add(func(e *core.SettingsUpdateEvent) error {
|
|
|
|
t.EventCalls["OnSettingsBeforeUpdateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnSettingsAfterUpdateRequest().Add(func(e *core.SettingsUpdateEvent) error {
|
|
|
|
t.EventCalls["OnSettingsAfterUpdateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnCollectionsListRequest().Add(func(e *core.CollectionsListEvent) error {
|
|
|
|
t.EventCalls["OnCollectionsListRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnCollectionViewRequest().Add(func(e *core.CollectionViewEvent) error {
|
|
|
|
t.EventCalls["OnCollectionViewRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnCollectionBeforeCreateRequest().Add(func(e *core.CollectionCreateEvent) error {
|
|
|
|
t.EventCalls["OnCollectionBeforeCreateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnCollectionAfterCreateRequest().Add(func(e *core.CollectionCreateEvent) error {
|
|
|
|
t.EventCalls["OnCollectionAfterCreateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnCollectionBeforeUpdateRequest().Add(func(e *core.CollectionUpdateEvent) error {
|
|
|
|
t.EventCalls["OnCollectionBeforeUpdateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnCollectionAfterUpdateRequest().Add(func(e *core.CollectionUpdateEvent) error {
|
|
|
|
t.EventCalls["OnCollectionAfterUpdateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnCollectionBeforeDeleteRequest().Add(func(e *core.CollectionDeleteEvent) error {
|
|
|
|
t.EventCalls["OnCollectionBeforeDeleteRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnCollectionAfterDeleteRequest().Add(func(e *core.CollectionDeleteEvent) error {
|
|
|
|
t.EventCalls["OnCollectionAfterDeleteRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnAdminsListRequest().Add(func(e *core.AdminsListEvent) error {
|
|
|
|
t.EventCalls["OnAdminsListRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnAdminViewRequest().Add(func(e *core.AdminViewEvent) error {
|
|
|
|
t.EventCalls["OnAdminViewRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnAdminBeforeCreateRequest().Add(func(e *core.AdminCreateEvent) error {
|
|
|
|
t.EventCalls["OnAdminBeforeCreateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnAdminAfterCreateRequest().Add(func(e *core.AdminCreateEvent) error {
|
|
|
|
t.EventCalls["OnAdminAfterCreateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnAdminBeforeUpdateRequest().Add(func(e *core.AdminUpdateEvent) error {
|
|
|
|
t.EventCalls["OnAdminBeforeUpdateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnAdminAfterUpdateRequest().Add(func(e *core.AdminUpdateEvent) error {
|
|
|
|
t.EventCalls["OnAdminAfterUpdateRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnAdminBeforeDeleteRequest().Add(func(e *core.AdminDeleteEvent) error {
|
|
|
|
t.EventCalls["OnAdminBeforeDeleteRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnAdminAfterDeleteRequest().Add(func(e *core.AdminDeleteEvent) error {
|
|
|
|
t.EventCalls["OnAdminAfterDeleteRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnAdminAuthRequest().Add(func(e *core.AdminAuthEvent) error {
|
|
|
|
t.EventCalls["OnAdminAuthRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
t.OnFileDownloadRequest().Add(func(e *core.FileDownloadEvent) error {
|
|
|
|
t.EventCalls["OnFileDownloadRequest"]++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return t, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewTempDataDir creates a new temporary directory copy of the test data.
|
|
|
|
//
|
|
|
|
// It is the caller's responsibility to call `os.RemoveAll(dir)`
|
|
|
|
// when the directory is no longer needed.
|
|
|
|
func NewTempDataDir() (string, error) {
|
|
|
|
tempDir, err := os.MkdirTemp("", "pb_test_*")
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, currentFile, _, _ := runtime.Caller(0)
|
|
|
|
testDataDir := filepath.Join(path.Dir(currentFile), "data")
|
|
|
|
|
|
|
|
// copy everything from testDataDir to tempDir
|
|
|
|
if err := copyDir(testDataDir, tempDir); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return tempDir, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
// Helpers
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
|
|
|
|
func copyDir(src string, dest string) error {
|
|
|
|
if err := os.MkdirAll(dest, os.ModePerm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
sourceDir, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer sourceDir.Close()
|
|
|
|
|
|
|
|
items, err := sourceDir.Readdir(-1)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range items {
|
|
|
|
fullSrcPath := filepath.Join(src, item.Name())
|
|
|
|
fullDestPath := filepath.Join(dest, item.Name())
|
|
|
|
|
|
|
|
var copyErr error
|
|
|
|
if item.IsDir() {
|
|
|
|
copyErr = copyDir(fullSrcPath, fullDestPath)
|
|
|
|
} else {
|
|
|
|
copyErr = copyFile(fullSrcPath, fullDestPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
if copyErr != nil {
|
|
|
|
return copyErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func copyFile(src string, dest string) error {
|
|
|
|
srcFile, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer srcFile.Close()
|
|
|
|
|
|
|
|
destFile, err := os.Create(dest)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer destFile.Close()
|
|
|
|
|
|
|
|
if _, err := io.Copy(destFile, srcFile); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|