package core import ( "os" "testing" "time" "github.com/pocketbase/pocketbase/migrations/logs" "github.com/pocketbase/pocketbase/tools/logger" "github.com/pocketbase/pocketbase/tools/mailer" "github.com/pocketbase/pocketbase/tools/migrate" ) func TestNewBaseApp(t *testing.T) { const testDataDir = "./pb_base_app_test_data_dir/" defer os.RemoveAll(testDataDir) app := NewBaseApp(BaseAppConfig{ DataDir: testDataDir, EncryptionEnv: "test_env", }) if app.dataDir != testDataDir { t.Fatalf("expected dataDir %q, got %q", testDataDir, app.dataDir) } if app.encryptionEnv != "test_env" { t.Fatalf("expected encryptionEnv test_env, got %q", app.dataDir) } if app.store == nil { t.Fatal("expected store to be set, got nil") } if app.settings == nil { t.Fatal("expected settings to be set, got nil") } if app.subscriptionsBroker == nil { t.Fatal("expected subscriptionsBroker to be set, got nil") } } func TestBaseAppBootstrap(t *testing.T) { const testDataDir = "./pb_base_app_test_data_dir/" defer os.RemoveAll(testDataDir) app := NewBaseApp(BaseAppConfig{ DataDir: testDataDir, EncryptionEnv: "pb_test_env", }) defer app.ResetBootstrapState() if app.IsBootstrapped() { t.Fatal("Didn't expect the application to be bootstrapped.") } if err := app.Bootstrap(); err != nil { t.Fatal(err) } if !app.IsBootstrapped() { t.Fatal("Expected the application to be bootstrapped.") } if stat, err := os.Stat(testDataDir); err != nil || !stat.IsDir() { t.Fatal("Expected test data directory to be created.") } if app.dao == nil { t.Fatal("Expected app.dao to be initialized, got nil.") } if app.dao.BeforeCreateFunc == nil { t.Fatal("Expected app.dao.BeforeCreateFunc to be set, got nil.") } if app.dao.AfterCreateFunc == nil { t.Fatal("Expected app.dao.AfterCreateFunc to be set, got nil.") } if app.dao.BeforeUpdateFunc == nil { t.Fatal("Expected app.dao.BeforeUpdateFunc to be set, got nil.") } if app.dao.AfterUpdateFunc == nil { t.Fatal("Expected app.dao.AfterUpdateFunc to be set, got nil.") } if app.dao.BeforeDeleteFunc == nil { t.Fatal("Expected app.dao.BeforeDeleteFunc to be set, got nil.") } if app.dao.AfterDeleteFunc == nil { t.Fatal("Expected app.dao.AfterDeleteFunc to be set, got nil.") } if app.logsDao == nil { t.Fatal("Expected app.logsDao to be initialized, got nil.") } if app.settings == nil { t.Fatal("Expected app.settings to be initialized, got nil.") } if app.logger == nil { t.Fatal("Expected app.logger to be initialized, got nil.") } if _, ok := app.logger.Handler().(*logger.BatchHandler); !ok { t.Fatal("Expected app.logger handler to be initialized.") } // reset if err := app.ResetBootstrapState(); err != nil { t.Fatal(err) } if app.dao != nil { t.Fatalf("Expected app.dao to be nil, got %v.", app.dao) } if app.logsDao != nil { t.Fatalf("Expected app.logsDao to be nil, got %v.", app.logsDao) } } func TestBaseAppGetters(t *testing.T) { const testDataDir = "./pb_base_app_test_data_dir/" defer os.RemoveAll(testDataDir) app := NewBaseApp(BaseAppConfig{ DataDir: testDataDir, EncryptionEnv: "pb_test_env", }) defer app.ResetBootstrapState() if err := app.Bootstrap(); err != nil { t.Fatal(err) } if app.dao != app.Dao() { t.Fatalf("Expected app.Dao %v, got %v", app.Dao(), app.dao) } if app.dao.ConcurrentDB() != app.DB() { t.Fatalf("Expected app.DB %v, got %v", app.DB(), app.dao.ConcurrentDB()) } if app.logsDao != app.LogsDao() { t.Fatalf("Expected app.LogsDao %v, got %v", app.LogsDao(), app.logsDao) } if app.logsDao.ConcurrentDB() != app.LogsDB() { t.Fatalf("Expected app.LogsDB %v, got %v", app.LogsDB(), app.logsDao.ConcurrentDB()) } if app.dataDir != app.DataDir() { t.Fatalf("Expected app.DataDir %v, got %v", app.DataDir(), app.dataDir) } if app.encryptionEnv != app.EncryptionEnv() { t.Fatalf("Expected app.EncryptionEnv %v, got %v", app.EncryptionEnv(), app.encryptionEnv) } if app.settings != app.Settings() { t.Fatalf("Expected app.Settings %v, got %v", app.Settings(), app.settings) } if app.store != app.Store() { t.Fatalf("Expected app.Store %v, got %v", app.Store(), app.store) } if app.logger != app.Logger() { t.Fatalf("Expected app.Logger %v, got %v", app.Logger(), app.logger) } if app.subscriptionsBroker != app.SubscriptionsBroker() { t.Fatalf("Expected app.SubscriptionsBroker %v, got %v", app.SubscriptionsBroker(), app.subscriptionsBroker) } if app.onBeforeServe != app.OnBeforeServe() || app.OnBeforeServe() == nil { t.Fatalf("Getter app.OnBeforeServe does not match or nil (%v vs %v)", app.OnBeforeServe(), app.onBeforeServe) } } func TestBaseAppNewMailClient(t *testing.T) { const testDataDir = "./pb_base_app_test_data_dir/" defer os.RemoveAll(testDataDir) app := NewBaseApp(BaseAppConfig{ DataDir: testDataDir, EncryptionEnv: "pb_test_env", }) client1 := app.NewMailClient() if val, ok := client1.(*mailer.Sendmail); !ok { t.Fatalf("Expected mailer.Sendmail instance, got %v", val) } app.Settings().Smtp.Enabled = true client2 := app.NewMailClient() if val, ok := client2.(*mailer.SmtpClient); !ok { t.Fatalf("Expected mailer.SmtpClient instance, got %v", val) } } func TestBaseAppNewFilesystem(t *testing.T) { const testDataDir = "./pb_base_app_test_data_dir/" defer os.RemoveAll(testDataDir) app := NewBaseApp(BaseAppConfig{ DataDir: testDataDir, EncryptionEnv: "pb_test_env", }) // local local, localErr := app.NewFilesystem() if localErr != nil { t.Fatal(localErr) } if local == nil { t.Fatal("Expected local filesystem instance, got nil") } // misconfigured s3 app.Settings().S3.Enabled = true s3, s3Err := app.NewFilesystem() if s3Err == nil { t.Fatal("Expected S3 error, got nil") } if s3 != nil { t.Fatalf("Expected nil s3 filesystem, got %v", s3) } } func TestBaseAppNewBackupsFilesystem(t *testing.T) { const testDataDir = "./pb_base_app_test_data_dir/" defer os.RemoveAll(testDataDir) app := NewBaseApp(BaseAppConfig{ DataDir: testDataDir, EncryptionEnv: "pb_test_env", }) // local local, localErr := app.NewBackupsFilesystem() if localErr != nil { t.Fatal(localErr) } if local == nil { t.Fatal("Expected local backups filesystem instance, got nil") } // misconfigured s3 app.Settings().Backups.S3.Enabled = true s3, s3Err := app.NewBackupsFilesystem() if s3Err == nil { t.Fatal("Expected S3 error, got nil") } if s3 != nil { t.Fatalf("Expected nil s3 backups filesystem, got %v", s3) } } func TestBaseAppLoggerWrites(t *testing.T) { testDataDir, err := os.MkdirTemp("", "logger_writes") if err != nil { t.Fatal(err) } defer os.RemoveAll(testDataDir) app := NewBaseApp(BaseAppConfig{ DataDir: testDataDir, }) if err := app.Bootstrap(); err != nil { t.Fatal(err) } // init logs migrations runner, err := migrate.NewRunner(app.LogsDB(), logs.LogsMigrations) if err != nil { t.Fatalf("Logs runner error: %v", err) } if _, err := runner.Up(); err != nil { t.Fatalf("Logs migration execution error: %v", err) } threshold := 200 // disabled logs retention { app.Settings().Logs.MaxDays = 0 for i := 0; i < threshold+1; i++ { app.Logger().Error("test") } if total := totalLogs(app, t); total != 0 { t.Fatalf("Expected no logs, got %d", total) } } // test batch logs writes { app.Settings().Logs.MaxDays = 1 for i := 0; i < threshold-1; i++ { app.Logger().Error("test") } if total := totalLogs(app, t); total != 0 { t.Fatalf("Expected no logs, got %d", total) } // should trigger batch write app.Logger().Error("test") // should be added for the next batch write app.Logger().Error("test") if total := totalLogs(app, t); total != threshold { t.Fatalf("Expected %d logs, got %d", threshold, total) } // wait for ~3 secs to check the timer trigger time.Sleep(3200 * time.Millisecond) if total := totalLogs(app, t); total != threshold+1 { t.Fatalf("Expected %d logs, got %d", threshold+1, total) } } } func totalLogs(app App, t *testing.T) int { var total int err := app.LogsDao().LogQuery().Select("count(*)").Row(&total) if err != nil { t.Fatalf("Failed to fetch total logs: %v", err) } return total }