From af3af40139eea0c2a658df6cf19bf1ddc7e9cb7e Mon Sep 17 00:00:00 2001 From: Keith Martin Date: Mon, 13 Oct 2025 19:49:33 +1000 Subject: [PATCH 1/6] Tests: fix random selection of deleted record causing test failure --- internal/api/cache_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/cache_test.go b/internal/api/cache_test.go index 7aa977d52..4b51f5eac 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) } From 4e3134dae3d8ab484bffd07840b43fc4dc6375d2 Mon Sep 17 00:00:00 2001 From: Keith Martin Date: Mon, 13 Oct 2025 19:52:19 +1000 Subject: [PATCH 2/6] Tests: fix test folders being left after test --- internal/commands/commands_test.go | 2 +- internal/commands/download_e2e_test.go | 1 + internal/config/config_storage_test.go | 2 ++ internal/photoprism/get/get_test.go | 2 +- internal/thumb/avatar/avatar_test.go | 2 +- pkg/fs/cache_test.go | 2 +- 6 files changed, 7 insertions(+), 4 deletions(-) diff --git a/internal/commands/commands_test.go b/internal/commands/commands_test.go index 104689a4f..bfc444ee2 100644 --- a/internal/commands/commands_test.go +++ b/internal/commands/commands_test.go @@ -35,7 +35,6 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } - defer os.RemoveAll(tempDir) c := config.NewMinimalTestConfigWithDb("commands", tempDir) get.SetConfig(c) @@ -54,6 +53,7 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + os.RemoveAll(tempDir) // os.Exit prevents defer from executing. os.Exit(code) } 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/config_storage_test.go b/internal/config/config_storage_test.go index 550a28664..f41be3142 100644 --- a/internal/config/config_storage_test.go +++ b/internal/config/config_storage_test.go @@ -1,6 +1,7 @@ package config import ( + "os" "strings" "testing" @@ -168,6 +169,7 @@ func TestConfig_TempPath(t *testing.T) { } else { t.Logf("temp paths match: '%s' == '%s'", d4, d0) } + os.RemoveAll(d1) } func TestConfig_CmdCachePath(t *testing.T) { diff --git a/internal/photoprism/get/get_test.go b/internal/photoprism/get/get_test.go index 5cf7a5a6c..222277bbb 100644 --- a/internal/photoprism/get/get_test.go +++ b/internal/photoprism/get/get_test.go @@ -13,7 +13,6 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } - defer os.RemoveAll(tempDir) c := config.NewMinimalTestConfigWithDb("test", tempDir) SetConfig(c) @@ -24,5 +23,6 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + os.RemoveAll(tempDir) os.Exit(code) } diff --git a/internal/thumb/avatar/avatar_test.go b/internal/thumb/avatar/avatar_test.go index f1fc63c71..bd5990b7d 100644 --- a/internal/thumb/avatar/avatar_test.go +++ b/internal/thumb/avatar/avatar_test.go @@ -20,7 +20,6 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } - defer os.RemoveAll(tempDir) c := config.NewMinimalTestConfigWithDb("avatar", tempDir) get.SetConfig(c) @@ -32,5 +31,6 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + os.RemoveAll(tempDir) os.Exit(code) } 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)) }) } From 8253f8d221ec2c09329de5f18ac7e362111728a9 Mon Sep 17 00:00:00 2001 From: Keith Martin Date: Mon, 13 Oct 2025 19:52:47 +1000 Subject: [PATCH 3/6] Tests: ensure that required records are available --- internal/entity/query/covers_test.go | 2 ++ 1 file changed, 2 insertions(+) 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) } From d8829ce8860d6a068d2a44cc8405a537a6a8e21b Mon Sep 17 00:00:00 2001 From: Keith Martin Date: Mon, 13 Oct 2025 23:22:51 +1000 Subject: [PATCH 4/6] Tests: make each package execute against it's own testdata folder to ensure no interpackage test failures --- internal/api/api_test.go | 1 + internal/auth/session/session_test.go | 1 + internal/config/client_config_test.go | 1 + internal/config/config_storage_test.go | 1 + internal/config/config_test.go | 3 +- internal/config/test.go | 39 ++++++++++++++++++++- internal/photoprism/backup/backup_test.go | 1 + internal/photoprism/photoprism_test.go | 2 ++ internal/photoprism/thumbs_test.go | 2 +- internal/server/server_test.go | 1 + internal/server/wellknown/wellknown_test.go | 5 +++ internal/workers/auto/auto_test.go | 1 + internal/workers/workers_test.go | 1 + 13 files changed, 56 insertions(+), 3 deletions(-) diff --git a/internal/api/api_test.go b/internal/api/api_test.go index 3b3b1caac..1110291f1 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -43,6 +43,7 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + c.CleanupTestFolder() os.Exit(code) } diff --git a/internal/auth/session/session_test.go b/internal/auth/session/session_test.go index e3cc3482b..e4d2d919a 100644 --- a/internal/auth/session/session_test.go +++ b/internal/auth/session/session_test.go @@ -24,5 +24,6 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + c.CleanupTestFolder() os.Exit(code) } diff --git a/internal/config/client_config_test.go b/internal/config/client_config_test.go index ea9642bba..202f634fc 100644 --- a/internal/config/client_config_test.go +++ b/internal/config/client_config_test.go @@ -569,6 +569,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 f41be3142..c57eba41b 100644 --- a/internal/config/config_storage_test.go +++ b/internal/config/config_storage_test.go @@ -164,6 +164,7 @@ 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 { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 30e46f5f4..4197acf53 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -32,6 +32,7 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + c.CleanupTestFolder() os.Exit(code) } @@ -359,7 +360,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 8ca27465b..977752e0f 100644 --- a/internal/config/test.go +++ b/internal/config/test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "regexp" "strconv" + "strings" "sync" "testing" "time" @@ -221,6 +222,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. @@ -308,15 +316,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), } @@ -595,3 +618,17 @@ func (c *Config) AssertTestData(t *testing.T) { reportErr("SidecarPath") } } + +// CleanupTestFolder uses RemoveAll to remove the storage path above testdata. +func (c *Config) CleanupTestFolder() { + 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/photoprism/backup/backup_test.go b/internal/photoprism/backup/backup_test.go index 693cd3d98..c0ac4c642 100644 --- a/internal/photoprism/backup/backup_test.go +++ b/internal/photoprism/backup/backup_test.go @@ -29,5 +29,6 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + c.CleanupTestFolder() os.Exit(code) } diff --git a/internal/photoprism/photoprism_test.go b/internal/photoprism/photoprism_test.go index 876dc337f..b74bf15fc 100644 --- a/internal/photoprism/photoprism_test.go +++ b/internal/photoprism/photoprism_test.go @@ -18,6 +18,7 @@ func TestMain(m *testing.M) { fs.PurgeTestDbFiles(".", false) c := config.NewTestConfig("photoprism") + config.OnceTestConfig(c) SetConfig(c) defer c.CloseDb() @@ -26,5 +27,6 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + c.CleanupTestFolder() os.Exit(code) } 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 560ae0693..f5254cbeb 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -36,5 +36,6 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + c.CleanupTestFolder() os.Exit(code) } diff --git a/internal/server/wellknown/wellknown_test.go b/internal/server/wellknown/wellknown_test.go index 69a9277e0..e3c92b283 100644 --- a/internal/server/wellknown/wellknown_test.go +++ b/internal/server/wellknown/wellknown_test.go @@ -4,6 +4,7 @@ import ( "os" "testing" + "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/pkg/fs" ) @@ -11,11 +12,15 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files before running the tests. fs.PurgeTestDbFiles(".", false) + c := config.TestConfig() + defer c.CloseDb() + // Run unit tests. code := m.Run() // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + c.CleanupTestFolder() os.Exit(code) } diff --git a/internal/workers/auto/auto_test.go b/internal/workers/auto/auto_test.go index 5ae8ec4e7..7d0e23f53 100644 --- a/internal/workers/auto/auto_test.go +++ b/internal/workers/auto/auto_test.go @@ -26,6 +26,7 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + c.CleanupTestFolder() os.Exit(code) } diff --git a/internal/workers/workers_test.go b/internal/workers/workers_test.go index 99eaf102a..adb3a3c89 100644 --- a/internal/workers/workers_test.go +++ b/internal/workers/workers_test.go @@ -33,5 +33,6 @@ func TestMain(m *testing.M) { // Remove temporary SQLite files after running the tests. fs.PurgeTestDbFiles(".", false) + c.CleanupTestFolder() os.Exit(code) } From f9d8e8ff7ab2a4491c3fb629e22533d93f4e5cb5 Mon Sep 17 00:00:00 2001 From: Keith Martin Date: Mon, 13 Oct 2025 23:49:11 +1000 Subject: [PATCH 5/6] Tests: add unit tests of CleanupTestFolder --- internal/config/test.go | 4 ++++ internal/config/test_test.go | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/internal/config/test.go b/internal/config/test.go index 977752e0f..262d974ea 100644 --- a/internal/config/test.go +++ b/internal/config/test.go @@ -621,6 +621,10 @@ func (c *Config) AssertTestData(t *testing.T) { // 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") 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") + }) +} From 404e8005fe9b7c65b8d43db2c45680b4d0f14c4f Mon Sep 17 00:00:00 2001 From: Keith Martin Date: Sun, 23 Nov 2025 22:34:50 +1000 Subject: [PATCH 6/6] Tests: Allow defer in TestMain --- internal/ai/vision/vision_test.go | 9 ++++-- internal/api/api_test.go | 26 +++++++++------- internal/api/download/download_test.go | 9 ++++-- internal/auth/jwt/jwt_test.go | 16 +++++++--- internal/auth/oidc/oidc_test.go | 9 ++++-- internal/auth/session/session_test.go | 25 ++++++++------- internal/commands/commands_test.go | 29 +++++++++-------- internal/config/config_test.go | 25 ++++++++------- internal/entity/entity_test.go | 17 +++++----- internal/entity/query/query_test.go | 17 +++++----- internal/entity/search/search_test.go | 15 ++++----- internal/meta/meta_test.go | 8 +++-- internal/photoprism/backup/backup_test.go | 25 ++++++++------- internal/photoprism/batch/batch_test.go | 30 +++++++++++------- internal/photoprism/get/get_test.go | 31 ++++++++++--------- internal/photoprism/photoprism_test.go | 25 ++++++++------- internal/server/server_test.go | 26 +++++++++------- internal/server/wellknown/wellknown_test.go | 20 +++++++----- internal/service/cluster/node/node_test.go | 16 +++++----- .../cluster/provisioner/provisioner_test.go | 16 +++++----- .../service/cluster/registry/registry_test.go | 16 +++++----- internal/service/hub/hub_test.go | 9 ++++-- internal/thumb/avatar/avatar_test.go | 28 +++++++++-------- internal/thumb/thumb_test.go | 18 ++++++----- internal/workers/auto/auto_test.go | 30 +++++++++++------- internal/workers/workers_test.go | 25 ++++++++------- pkg/fs/fs_test.go | 9 ++++-- pkg/i18n/i18n_test.go | 9 ++++-- 28 files changed, 311 insertions(+), 227 deletions(-) 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 8f86dad88..68ca70564 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,23 +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) - - c.CleanupTestFolder() - os.Exit(code) + return m.Run() } type CloseableResponseRecorder struct { 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 7cd64938b..f4ac54ff7 100644 --- a/internal/auth/session/session_test.go +++ b/internal/auth/session/session_test.go @@ -11,22 +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) - - c.CleanupTestFolder() - os.Exit(code) + return m.Run() } diff --git a/internal/commands/commands_test.go b/internal/commands/commands_test.go index 7ff7b80a7..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,19 +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.RemoveAll(tempDir) // os.Exit prevents defer from executing. - os.Exit(code) + return m.Run() } // NewTestContext creates a new CLI test context with the flags and arguments provided. diff --git a/internal/config/config_test.go b/internal/config/config_test.go index a09997f94..ce2d40fcd 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -22,24 +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) - - c.CleanupTestFolder() - os.Exit(code) + return m.Run() } func TestNewConfig(t *testing.T) { 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/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 025fb05c6..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,19 +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) - - c.CleanupTestFolder() - 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 980e7b1dd..c460b44a7 100644 --- a/internal/photoprism/get/get_test.go +++ b/internal/photoprism/get/get_test.go @@ -8,28 +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.RemoveAll(tempDir) - os.Exit(code) + return m.Run() } diff --git a/internal/photoprism/photoprism_test.go b/internal/photoprism/photoprism_test.go index 3e5f5e2cd..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) @@ -20,16 +25,14 @@ func TestMain(m *testing.M) { 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) - - c.CleanupTestFolder() - os.Exit(code) + return m.Run() } diff --git a/internal/server/server_test.go b/internal/server/server_test.go index e9e2e8bf8..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,21 +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) - - c.CleanupTestFolder() - os.Exit(code) + return m.Run() } diff --git a/internal/server/wellknown/wellknown_test.go b/internal/server/wellknown/wellknown_test.go index e3c92b283..4b20d8bba 100644 --- a/internal/server/wellknown/wellknown_test.go +++ b/internal/server/wellknown/wellknown_test.go @@ -8,19 +8,23 @@ 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) { // Remove temporary SQLite files before running the tests. fs.PurgeTestDbFiles(".", false) c := config.TestConfig() - defer c.CloseDb() + 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) - - c.CleanupTestFolder() - 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 11b8ba4cb..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,22 +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.RemoveAll(tempDir) - 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 b1b8a36eb..c9558055e 100644 --- a/internal/workers/auto/auto_test.go +++ b/internal/workers/auto/auto_test.go @@ -10,24 +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) - c.CleanupTestFolder() - 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 5ee3dae36..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,20 +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) - - c.CleanupTestFolder() - os.Exit(code) + return m.Run() } diff --git a/pkg/fs/fs_test.go b/pkg/fs/fs_test.go index 0126dbf59..a5ab8d4de 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) {