diff --git a/internal/ai/vision/vision_test.go b/internal/ai/vision/vision_test.go index 8e84c3b03..7a719957d 100644 --- a/internal/ai/vision/vision_test.go +++ b/internal/ai/vision/vision_test.go @@ -15,7 +15,12 @@ import ( var assetsPath = fs.Abs("../../../assets") var examplesPath = filepath.Join(assetsPath, "examples") +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { // Init test logger. log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) @@ -27,7 +32,5 @@ func TestMain(m *testing.M) { ServiceUri = "" // Run unit tests. - code := m.Run() - - os.Exit(code) + return m.Run() } diff --git a/internal/api/api_test.go b/internal/api/api_test.go index 974756446..02b301c3d 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -20,7 +20,12 @@ import ( "github.com/photoprism/photoprism/pkg/http/header" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { // Init test logger. log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) @@ -31,22 +36,22 @@ func TestMain(m *testing.M) { // Init test config. c := config.TestConfig() + defer c.CleanupTestFolder() + defer func() { + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() + get.SetConfig(c) // Increase login rate limit for testing. limiter.Login = limiter.NewLimit(1, 10000) // Run unit tests. - code := m.Run() - - if err := c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } type CloseableResponseRecorder struct { diff --git a/internal/api/cache_test.go b/internal/api/cache_test.go index 141470329..a7cbe48cc 100644 --- a/internal/api/cache_test.go +++ b/internal/api/cache_test.go @@ -59,7 +59,7 @@ func TestRemoveFromAlbumCoverCache(t *testing.T) { cache.Flush() var album entity.Album - if err := query.UnscopedDb().Where("album_type = ? AND thumb_src = ?", entity.AlbumManual, entity.SrcAuto).First(&album).Error; err != nil { + if err := query.Db().Where("album_type = ? AND thumb_src = ?", entity.AlbumManual, entity.SrcAuto).First(&album).Error; err != nil { t.Skipf("no auto-managed manual album available: %v", err) } diff --git a/internal/api/download/download_test.go b/internal/api/download/download_test.go index dd273bba7..d2e5da6b6 100644 --- a/internal/api/download/download_test.go +++ b/internal/api/download/download_test.go @@ -10,7 +10,12 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { // Init test logger. log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) @@ -18,7 +23,5 @@ func TestMain(m *testing.M) { AllowedPaths = append(AllowedPaths, fs.Abs("./testdata")) // Run unit tests. - code := m.Run() - - os.Exit(code) + return m.Run() } diff --git a/internal/auth/jwt/jwt_test.go b/internal/auth/jwt/jwt_test.go index 0189f2fd5..fb3c9e5c0 100644 --- a/internal/auth/jwt/jwt_test.go +++ b/internal/auth/jwt/jwt_test.go @@ -11,19 +11,25 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { // Init test logger. log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) event.AuditLog = log - // Run unit tests. - code := m.Run() - - // Remove temporary SQLite files after running the tests. + // Remove temporary SQLite files before running the tests. fs.PurgeTestDbFiles(".", false) - os.Exit(code) + // Remove temporary SQLite files after running the tests. + defer fs.PurgeTestDbFiles(".", false) + + // Run unit tests. + return m.Run() } func newTestConfig(t *testing.T) *cfg.Config { diff --git a/internal/auth/oidc/oidc_test.go b/internal/auth/oidc/oidc_test.go index b32f07bc4..acc0f3891 100644 --- a/internal/auth/oidc/oidc_test.go +++ b/internal/auth/oidc/oidc_test.go @@ -9,14 +9,17 @@ import ( "github.com/photoprism/photoprism/internal/event" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { // Init test logger. log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) event.AuditLog = log // Run unit tests. - code := m.Run() - - os.Exit(code) + return m.Run() } diff --git a/internal/auth/session/session_test.go b/internal/auth/session/session_test.go index 44782c567..f4ac54ff7 100644 --- a/internal/auth/session/session_test.go +++ b/internal/auth/session/session_test.go @@ -11,21 +11,25 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) event.AuditLog = log c := config.TestConfig() + defer c.CleanupTestFolder() + defer func() { + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() - code := m.Run() - - // Remove temporary SQLite files after running the tests. - if err := c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/internal/commands/commands_test.go b/internal/commands/commands_test.go index 4282d15bd..87f1c64a3 100644 --- a/internal/commands/commands_test.go +++ b/internal/commands/commands_test.go @@ -21,7 +21,12 @@ import ( // "config: database not connected" during test runs, consider moving shutdown // behavior behind an interface or gating it for tests. +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { _ = os.Setenv("TF_CPP_MIN_LOG_LEVEL", "3") log = logrus.StandardLogger() @@ -35,8 +40,18 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } + defer os.RemoveAll(tempDir) c := config.NewMinimalTestConfigWithDb("commands", tempDir) + defer c.CleanupTestFolder() + defer func() { + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() + get.SetConfig(c) // Keep DB connection open for the duration of this package's tests to @@ -48,18 +63,7 @@ func TestMain(m *testing.M) { } // Run unit tests. - code := m.Run() - - if err = c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - _ = os.RemoveAll(tempDir) - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } // NewTestContext creates a new CLI test context with the flags and arguments provided. diff --git a/internal/commands/download_e2e_test.go b/internal/commands/download_e2e_test.go index 2bb6f9ef6..f4ef93115 100644 --- a/internal/commands/download_e2e_test.go +++ b/internal/commands/download_e2e_test.go @@ -36,6 +36,7 @@ func createFakeYtDlp(t *testing.T) string { if derr != nil { t.Fatalf("failed to create temp dir: %v", derr) } + t.Cleanup(func() { os.RemoveAll(dir) }) path := filepath.Join(dir, "yt-dlp") if runtime.GOOS == "windows" { // Not needed in CI/dev container. Keep simple stub. diff --git a/internal/config/client_config_test.go b/internal/config/client_config_test.go index b7000e03e..d3a3439e0 100644 --- a/internal/config/client_config_test.go +++ b/internal/config/client_config_test.go @@ -580,6 +580,7 @@ func TestConfig_ClientSessionConfig(t *testing.T) { f := cfg.Settings.Features assert.Equal(t, adminFeatures, f) }) + c.CleanupTestFolder() } func TestConfig_Flags(t *testing.T) { diff --git a/internal/config/config_storage_test.go b/internal/config/config_storage_test.go index 1b9e5dfc1..a1796cb67 100644 --- a/internal/config/config_storage_test.go +++ b/internal/config/config_storage_test.go @@ -168,11 +168,13 @@ func TestConfig_TempPath(t *testing.T) { t.Logf("temp paths match: '%s' == '%s'", d1, d2) } + d0 = TestConfig().tempPath() // Reset to use the cached test config, which TempPath will be using. if d4 := c.TempPath(); d4 != d0 { t.Fatalf("temp paths should match: '%s' <=> '%s'", d4, d0) } else { t.Logf("temp paths match: '%s' == '%s'", d4, d0) } + os.RemoveAll(d1) } func TestConfig_CmdCachePath(t *testing.T) { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index d927f0fa5..78c8acde0 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -22,23 +22,27 @@ func init() { hub.ApplyTestConfig() } +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { _ = os.Setenv("PHOTOPRISM_TEST", "true") log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) c := TestConfig() + defer c.CleanupTestFolder() + defer func() { + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() - code := m.Run() - - // Remove temporary SQLite files after running the tests. - if err := c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } func TestNewConfig(t *testing.T) { @@ -395,7 +399,7 @@ func TestConfig_ResolutionLimit(t *testing.T) { } func TestConfig_Serial(t *testing.T) { - c := NewConfig(CliTestContext()) + c := TestConfig() // Use complete test context, as NewConfig may not have the required file. result := c.Serial() diff --git a/internal/config/test.go b/internal/config/test.go index dd8d864f3..3f6eb466d 100644 --- a/internal/config/test.go +++ b/internal/config/test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "regexp" "strconv" + "strings" "sync" "testing" "time" @@ -225,6 +226,13 @@ func TestConfig() *Config { return testConfig } +// OnceTestConfig attempts to set testConfig if it hasn't already been done. +func OnceTestConfig(c *Config) { + // If this is the 1st call to NewTestConfig, then cache it. + // This is required for /internal/photoprism tests. + testConfigOnce.Do(func() { testConfig = c }) +} + // NewMinimalTestConfig creates a lightweight test Config (no DB, minimal filesystem). // // Not suitable for tests requiring a database or pre-created storage directories. @@ -313,15 +321,30 @@ func NewIsolatedTestConfig(dbName, dataPath string, createDirs bool) *Config { // NewTestConfig initializes test data so required directories exist before tests run. // See AGENTS.md (Test Data & Fixtures) and specs/dev/backend-testing.md for guidance. +// This now creates an isolated set of folders to ensure that cross package testing does not clash. +// You should use os.RemoveAll(c.StoragePath()) to remove the isolated folder created (assuming c := NewTestConfig("test")). func NewTestConfig(dbName string) *Config { defer log.Debug(capture.Time(time.Now(), "config: new test config created")) testConfigMutex.Lock() defer testConfigMutex.Unlock() + storagePath := os.Getenv("PHOTOPRISM_STORAGE_PATH") + if storagePath == "" { + storagePath = fs.Abs("../../storage") + } + + var tp string + var err error + if tp, err = os.MkdirTemp(storagePath, "test-photoprism-*"); err != nil { + log.Fatalf("config: %s", err.Error()) + } + + tp = filepath.Join(tp, fs.TestdataDir) + c := &Config{ cliCtx: CliTestContext(), - options: NewTestOptions(dbName), + options: NewTestOptionsForPath(dbName, tp), token: rnd.Base36(8), cache: gc.New(time.Second, time.Minute), } @@ -608,3 +631,21 @@ func (c *Config) AssertTestData(t *testing.T) { reportErr("SidecarPath") } } + +// CleanupTestFolder uses RemoveAll to remove the storage path above testdata. +func (c *Config) CleanupTestFolder() { + if c.options == nil { + log.Warn("config: c.options is nil in CleanupTestFolder") + return + } + td := c.StoragePath() + if strings.HasSuffix(td, "/testdata") && strings.Contains(td, "test-photoprism") { + td = strings.TrimSuffix(td, "/testdata") + if err := os.RemoveAll(td); err != nil { + log.Fatalf("config: %s (cleantestfolder)", err.Error()) + } + log.Debugf("config: cleaned up %s", td) + } else { + log.Warnf("config: %s not cleaned up", td) + } +} diff --git a/internal/config/test_test.go b/internal/config/test_test.go index 867f9f2fb..d3cec4a2e 100644 --- a/internal/config/test_test.go +++ b/internal/config/test_test.go @@ -1,6 +1,8 @@ package config import ( + "bytes" + "os" "testing" "github.com/jinzhu/gorm" @@ -57,3 +59,47 @@ func TestNewTestErrorConfig(t *testing.T) { assert.IsType(t, &gorm.DB{}, db) } + +func TestCleanupTestFolder(t *testing.T) { + t.Run("OptionsNil", func(t *testing.T) { + // Setup and capture log output + buffer := bytes.Buffer{} + log.SetOutput(&buffer) + + var c Config + c.CleanupTestFolder() + + // Reset logger + log.SetOutput(os.Stdout) + + assert.Contains(t, buffer.String(), "config: c.options is nil in CleanupTestFolder") + }) + + t.Run("NotExpectedPath", func(t *testing.T) { + // Setup and capture log output + buffer := bytes.Buffer{} + log.SetOutput(&buffer) + + c := Config{options: &Options{StoragePath: "/tmp/photoprism/testdata"}} + c.CleanupTestFolder() + + // Reset logger + log.SetOutput(os.Stdout) + + assert.Contains(t, buffer.String(), "config: /tmp/photoprism/testdata not cleaned up") + }) + + t.Run("Success", func(t *testing.T) { + // Setup and capture log output + buffer := bytes.Buffer{} + log.SetOutput(&buffer) + + c := Config{options: &Options{StoragePath: "/tmp/photoprism/test-photoprism-1394931550/testdata"}} + c.CleanupTestFolder() + + // Reset logger + log.SetOutput(os.Stdout) + + assert.Contains(t, buffer.String(), "config: cleaned up /tmp/photoprism/test-photoprism-1394931550") + }) +} diff --git a/internal/entity/entity_test.go b/internal/entity/entity_test.go index 29097d764..300bebce3 100644 --- a/internal/entity/entity_test.go +++ b/internal/entity/entity_test.go @@ -11,26 +11,27 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) event.AuditLog = log // Remove temporary SQLite files before running the tests. fs.PurgeTestDbFiles(".", false) + // Remove temporary SQLite files after running the tests. + defer fs.PurgeTestDbFiles(".", false) db := InitTestDb( os.Getenv("PHOTOPRISM_TEST_DRIVER"), os.Getenv("PHOTOPRISM_TEST_DSN")) + defer db.Close() - code := m.Run() - - // Remove temporary SQLite files after running the tests. - db.Close() - - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } func TestTypeString(t *testing.T) { diff --git a/internal/entity/query/covers_test.go b/internal/entity/query/covers_test.go index ff0daf08d..523ddc37a 100644 --- a/internal/entity/query/covers_test.go +++ b/internal/entity/query/covers_test.go @@ -16,6 +16,7 @@ func TestUpdateAlbumManualCovers(t *testing.T) { func TestUpdateAlbumManualCoversFiltered(t *testing.T) { var album entity.Album + assert.NoError(t, UpdateAlbumManualCovers()) if err := UnscopedDb().Where("album_type = ? AND thumb_src = ? AND thumb <> ''", entity.AlbumManual, entity.SrcAuto).First(&album).Error; err != nil { t.Skipf("no auto-managed manual album available: %v", err) } @@ -46,6 +47,7 @@ func TestUpdateAlbumFolderCovers(t *testing.T) { func TestUpdateAlbumFolderCoversFiltered(t *testing.T) { var album entity.Album + assert.NoError(t, UpdateAlbumFolderCovers()) if err := UnscopedDb().Where("album_type = ? AND thumb_src = ? AND album_path <> '' AND thumb <> ''", entity.AlbumFolder, entity.SrcAuto).First(&album).Error; err != nil { t.Skipf("no auto-managed folder album available: %v", err) } diff --git a/internal/entity/query/query_test.go b/internal/entity/query/query_test.go index 66d437c1c..aac6bb166 100644 --- a/internal/entity/query/query_test.go +++ b/internal/entity/query/query_test.go @@ -11,25 +11,26 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) // Remove temporary SQLite files before running the tests. fs.PurgeTestDbFiles(".", false) + // Remove temporary SQLite files after running the tests. + defer fs.PurgeTestDbFiles(".", false) db := entity.InitTestDb( os.Getenv("PHOTOPRISM_TEST_DRIVER"), os.Getenv("PHOTOPRISM_TEST_DSN")) + defer db.Close() - code := m.Run() - - // Remove temporary SQLite files after running the tests. - db.Close() - - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } func TestDbDialect(t *testing.T) { diff --git a/internal/entity/search/search_test.go b/internal/entity/search/search_test.go index f667eb0ec..4e7acce4c 100644 --- a/internal/entity/search/search_test.go +++ b/internal/entity/search/search_test.go @@ -10,23 +10,24 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) // Remove temporary SQLite files before running the tests. fs.PurgeTestDbFiles(".", false) + // Remove temporary SQLite files after running the tests. + defer fs.PurgeTestDbFiles(".", false) db := entity.InitTestDb( os.Getenv("PHOTOPRISM_TEST_DRIVER"), os.Getenv("PHOTOPRISM_TEST_DSN")) - defer db.Close() - code := m.Run() - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/internal/meta/meta_test.go b/internal/meta/meta_test.go index fbe0ce16a..7ec4e2df8 100644 --- a/internal/meta/meta_test.go +++ b/internal/meta/meta_test.go @@ -7,10 +7,14 @@ import ( "github.com/sirupsen/logrus" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) - code := m.Run() - os.Exit(code) + return m.Run() } diff --git a/internal/photoprism/backup/backup_test.go b/internal/photoprism/backup/backup_test.go index 09bc9733e..cb66642e6 100644 --- a/internal/photoprism/backup/backup_test.go +++ b/internal/photoprism/backup/backup_test.go @@ -13,7 +13,12 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) event.AuditLog = log @@ -22,18 +27,17 @@ func TestMain(m *testing.M) { fs.PurgeTestDbFiles(".", false) c := config.TestConfig() + defer c.CleanupTestFolder() + defer func() { + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() get.SetConfig(c) photoprism.SetConfig(c) - code := m.Run() - - // Remove temporary SQLite files after running the tests. - if err := c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/internal/photoprism/batch/batch_test.go b/internal/photoprism/batch/batch_test.go index 67a115491..94adecf5f 100644 --- a/internal/photoprism/batch/batch_test.go +++ b/internal/photoprism/batch/batch_test.go @@ -8,13 +8,19 @@ import ( "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/event" + "github.com/photoprism/photoprism/internal/mutex" "github.com/photoprism/photoprism/internal/photoprism" "github.com/photoprism/photoprism/internal/photoprism/get" "github.com/photoprism/photoprism/pkg/fs" ) -// TestMain configures shared state for the batch action tests. +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +// testMain configures shared state for the batch action tests. +func testMain(m *testing.M) (code int) { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) event.AuditLog = log @@ -23,18 +29,18 @@ func TestMain(m *testing.M) { fs.PurgeTestDbFiles(".", false) c := config.TestConfig() + defer c.CleanupTestFolder() + defer func() { + // Prevent UpdateCountsAsync from causing the test suite to fail due to the database closing before the goroutine has finished. + mutex.Index.Lock() + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() get.SetConfig(c) photoprism.SetConfig(c) - - code := m.Run() - - // Remove temporary SQLite files after running the tests. - if err := c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/internal/photoprism/get/get_test.go b/internal/photoprism/get/get_test.go index c743c80f7..c460b44a7 100644 --- a/internal/photoprism/get/get_test.go +++ b/internal/photoprism/get/get_test.go @@ -8,27 +8,29 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) (code int) { tempDir, err := os.MkdirTemp("", "internal-photoprism-get") if err != nil { panic(err) } + defer os.RemoveAll(tempDir) + c := config.NewMinimalTestConfigWithDb("test", tempDir) + defer c.CleanupTestFolder() + defer func() { + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() SetConfig(c) - code := m.Run() - - if err = c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - if err = os.RemoveAll(tempDir); err != nil { - log.Errorf("remove temp dir: %v", err) - } - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/internal/photoprism/photoprism_test.go b/internal/photoprism/photoprism_test.go index ab5d10891..454d25929 100644 --- a/internal/photoprism/photoprism_test.go +++ b/internal/photoprism/photoprism_test.go @@ -10,7 +10,12 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) @@ -18,16 +23,16 @@ func TestMain(m *testing.M) { fs.PurgeTestDbFiles(".", false) c := config.NewTestConfig("photoprism") + config.OnceTestConfig(c) SetConfig(c) + defer c.CleanupTestFolder() + defer func() { + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() - code := m.Run() - - if err := c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/internal/photoprism/thumbs_test.go b/internal/photoprism/thumbs_test.go index 59944531e..c6551d5bc 100644 --- a/internal/photoprism/thumbs_test.go +++ b/internal/photoprism/thumbs_test.go @@ -63,7 +63,7 @@ func TestThumb_Filename(t *testing.T) { t.Fatal(err) } - assert.True(t, strings.HasSuffix(filename, "/storage/testdata/cache/_tmp/9/9/9/99988_150x150_fit.jpg")) + assert.True(t, strings.HasSuffix(filename, "/testdata/cache/_tmp/9/9/9/99988_150x150_fit.jpg")) }) t.Run("InvalidHash", func(t *testing.T) { _, err := thumb.FileName("999", thumbsPath, 150, 150, thumb.ResampleFit, thumb.ResampleNearestNeighbor) diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 233ae06b4..6936489b3 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -13,7 +13,12 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) (code int) { // Init test logger. log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) @@ -24,20 +29,20 @@ func TestMain(m *testing.M) { // Init test config. c := config.TestConfig() + defer c.CleanupTestFolder() + defer func() { + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() + get.SetConfig(c) // Increase login rate limit for testing. limiter.Login = limiter.NewLimit(1, 10000) // Run unit tests. - code := m.Run() - - if err := c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/internal/server/wellknown/wellknown_test.go b/internal/server/wellknown/wellknown_test.go index 69a9277e0..4b20d8bba 100644 --- a/internal/server/wellknown/wellknown_test.go +++ b/internal/server/wellknown/wellknown_test.go @@ -4,18 +4,27 @@ import ( "os" "testing" + "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) (code int) { // Remove temporary SQLite files before running the tests. fs.PurgeTestDbFiles(".", false) + c := config.TestConfig() + defer c.CleanupTestFolder() + defer func() { + c.CloseDb() + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() + // Run unit tests. - code := m.Run() - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/internal/service/cluster/node/node_test.go b/internal/service/cluster/node/node_test.go index 6650e1700..a79025034 100644 --- a/internal/service/cluster/node/node_test.go +++ b/internal/service/cluster/node/node_test.go @@ -7,16 +7,18 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) -// TestMain ensures SQLite test DB artifacts are purged after the suite runs. +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +// TestMain ensures SQLite test DB artifacts are purged after the suite runs. +func testMain(m *testing.M) int { // Remove temporary SQLite files before running the tests. fs.PurgeTestDbFiles(".", false) + // Remove temporary SQLite files after running the tests. + defer fs.PurgeTestDbFiles(".", false) // Run unit tests. - code := m.Run() - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/internal/service/cluster/provisioner/provisioner_test.go b/internal/service/cluster/provisioner/provisioner_test.go index d67ea852e..1e5dc4e11 100644 --- a/internal/service/cluster/provisioner/provisioner_test.go +++ b/internal/service/cluster/provisioner/provisioner_test.go @@ -7,16 +7,18 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) -// TestMain ensures SQLite test DB artifacts are purged after the suite runs. +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +// TestMain ensures SQLite test DB artifacts are purged after the suite runs. +func testMain(m *testing.M) int { // Remove temporary SQLite files before running the tests. fs.PurgeTestDbFiles(".", false) + // Remove temporary SQLite files after running the tests. + defer fs.PurgeTestDbFiles(".", false) // Run unit tests. - code := m.Run() - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/internal/service/cluster/registry/registry_test.go b/internal/service/cluster/registry/registry_test.go index 79059350b..66813c476 100644 --- a/internal/service/cluster/registry/registry_test.go +++ b/internal/service/cluster/registry/registry_test.go @@ -14,18 +14,20 @@ import ( "github.com/photoprism/photoprism/pkg/rnd" ) -// TestMain ensures SQLite test DB artifacts are purged after the suite runs. +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +// TestMain ensures SQLite test DB artifacts are purged after the suite runs. +func testMain(m *testing.M) int { // Remove temporary SQLite files before running the tests. fs.PurgeTestDbFiles(".", false) + // Remove temporary SQLite files after running the tests. + defer fs.PurgeTestDbFiles(".", false) // Run unit tests. - code := m.Run() - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } func TestClientRegistry_GetAndDelete(t *testing.T) { diff --git a/internal/service/hub/hub_test.go b/internal/service/hub/hub_test.go index 9aee2c97f..2deaea65f 100644 --- a/internal/service/hub/hub_test.go +++ b/internal/service/hub/hub_test.go @@ -13,15 +13,18 @@ import ( "github.com/stretchr/testify/assert" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) ApplyTestConfig() - code := m.Run() - - os.Exit(code) + return m.Run() } // Token returns a random token with length of up to 10 characters. diff --git a/internal/thumb/avatar/avatar_test.go b/internal/thumb/avatar/avatar_test.go index 5184b9ead..890a063d2 100644 --- a/internal/thumb/avatar/avatar_test.go +++ b/internal/thumb/avatar/avatar_test.go @@ -12,7 +12,12 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) (code int) { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) @@ -20,21 +25,19 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } + defer os.RemoveAll(tempDir) c := config.NewMinimalTestConfigWithDb("avatar", tempDir) + defer c.CleanupTestFolder() + defer func() { + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() get.SetConfig(c) photoprism.SetConfig(c) - code := m.Run() - - if err = c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - _ = os.RemoveAll(tempDir) - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/internal/thumb/thumb_test.go b/internal/thumb/thumb_test.go index 5c0d88221..d182df81c 100644 --- a/internal/thumb/thumb_test.go +++ b/internal/thumb/thumb_test.go @@ -10,21 +10,23 @@ import ( "github.com/photoprism/photoprism/internal/event" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) event.AuditLog = log - code := m.Run() - - Shutdown() - + defer Shutdown() // Remove generated test files and folders. - _ = os.RemoveAll("testdata/1") - _ = os.RemoveAll("testdata/cache") - _ = os.RemoveAll("testdata/vips") + defer os.RemoveAll("testdata/1") + defer os.RemoveAll("testdata/cache") + defer os.RemoveAll("testdata/vips") - os.Exit(code) + return m.Run() } func TestNew(t *testing.T) { diff --git a/internal/workers/auto/auto_test.go b/internal/workers/auto/auto_test.go index fc06aae86..c9558055e 100644 --- a/internal/workers/auto/auto_test.go +++ b/internal/workers/auto/auto_test.go @@ -10,23 +10,30 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) (code int) { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) - c := config.TestConfig() - - // Run unit tests. - code := m.Run() - - if err := c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - // Remove temporary SQLite files after running the tests. + // Remove temporary SQLite files before running the tests. fs.PurgeTestDbFiles(".", false) - os.Exit(code) + c := config.TestConfig() + defer c.CleanupTestFolder() + defer func() { + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() + + // Run unit tests. + return m.Run() } func TestStart(t *testing.T) { diff --git a/internal/workers/workers_test.go b/internal/workers/workers_test.go index 011c204df..c0b9d28af 100644 --- a/internal/workers/workers_test.go +++ b/internal/workers/workers_test.go @@ -13,7 +13,12 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) (code int) { log = logrus.StandardLogger() log.SetLevel(logrus.TraceLevel) event.AuditLog = log @@ -22,19 +27,18 @@ func TestMain(m *testing.M) { fs.PurgeTestDbFiles(".", false) c := config.TestConfig() + defer c.CleanupTestFolder() + defer func() { + if err := c.CloseDb(); err != nil { + log.Errorf("close db: %v", err) + } + // Remove temporary SQLite files after running the tests. + fs.PurgeTestDbFiles(".", false) + }() get.SetConfig(c) photoprism.SetConfig(c) // Run unit tests. - code := m.Run() - - if err := c.CloseDb(); err != nil { - log.Errorf("close db: %v", err) - } - - // Remove temporary SQLite files after running the tests. - fs.PurgeTestDbFiles(".", false) - - os.Exit(code) + return m.Run() } diff --git a/pkg/fs/cache_test.go b/pkg/fs/cache_test.go index 383313273..5b413eb3a 100644 --- a/pkg/fs/cache_test.go +++ b/pkg/fs/cache_test.go @@ -43,6 +43,6 @@ func TestCachePath(t *testing.T) { assert.Equal(t, expected, result) assert.DirExists(t, expected) - _ = os.Remove(expected) + _ = os.RemoveAll(filepath.Join(os.TempDir(), ns)) }) } diff --git a/pkg/fs/fs_test.go b/pkg/fs/fs_test.go index f9947eb47..eb9b77be7 100644 --- a/pkg/fs/fs_test.go +++ b/pkg/fs/fs_test.go @@ -13,16 +13,19 @@ import ( "github.com/stretchr/testify/assert" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { if insensitive, err := CaseInsensitive(os.TempDir()); err != nil { fmt.Println(err) } else if insensitive { IgnoreCase() } - code := m.Run() - - os.Exit(code) + return m.Run() } func TestExists(t *testing.T) { diff --git a/pkg/i18n/i18n_test.go b/pkg/i18n/i18n_test.go index ba0a89ec0..b62713389 100644 --- a/pkg/i18n/i18n_test.go +++ b/pkg/i18n/i18n_test.go @@ -8,12 +8,15 @@ import ( "github.com/stretchr/testify/assert" ) +// TestMain executes testMain returning it's results. It is done this way so that defer can be used to cleanup. func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { gotext.Configure(localeDir, string(locale), "default") - code := m.Run() - - os.Exit(code) + return m.Run() } func TestMsg(t *testing.T) {