summaryrefslogtreecommitdiffstats
path: root/models/migrations/test/tests.go
diff options
context:
space:
mode:
Diffstat (limited to 'models/migrations/test/tests.go')
-rw-r--r--models/migrations/test/tests.go274
1 files changed, 274 insertions, 0 deletions
diff --git a/models/migrations/test/tests.go b/models/migrations/test/tests.go
new file mode 100644
index 0000000..0e37233
--- /dev/null
+++ b/models/migrations/test/tests.go
@@ -0,0 +1,274 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+//nolint:forbidigo
+package test
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "os"
+ "path"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/testlogger"
+ "code.gitea.io/gitea/modules/util"
+
+ "github.com/stretchr/testify/require"
+ "xorm.io/xorm"
+)
+
+// PrepareTestEnv prepares the test environment and reset the database. The skip parameter should usually be 0.
+// Provide models to be sync'd with the database - in particular any models you expect fixtures to be loaded from.
+//
+// fixtures in `models/migrations/fixtures/<TestName>` will be loaded automatically
+func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, func()) {
+ t.Helper()
+ ourSkip := 2
+ ourSkip += skip
+ deferFn := testlogger.PrintCurrentTest(t, ourSkip)
+ require.NoError(t, os.RemoveAll(setting.RepoRootPath))
+ require.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
+ ownerDirs, err := os.ReadDir(setting.RepoRootPath)
+ if err != nil {
+ require.NoError(t, err, "unable to read the new repo root: %v\n", err)
+ }
+ for _, ownerDir := range ownerDirs {
+ if !ownerDir.Type().IsDir() {
+ continue
+ }
+ repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
+ if err != nil {
+ require.NoError(t, err, "unable to read the new repo root: %v\n", err)
+ }
+ for _, repoDir := range repoDirs {
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0o755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0o755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0o755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0o755)
+ }
+ }
+
+ if err := deleteDB(); err != nil {
+ t.Errorf("unable to reset database: %v", err)
+ return nil, deferFn
+ }
+
+ x, err := newXORMEngine()
+ require.NoError(t, err)
+ if x != nil {
+ oldDefer := deferFn
+ deferFn = func() {
+ oldDefer()
+ if err := x.Close(); err != nil {
+ t.Errorf("error during close: %v", err)
+ }
+ if err := deleteDB(); err != nil {
+ t.Errorf("unable to reset database: %v", err)
+ }
+ }
+ }
+ if err != nil {
+ return x, deferFn
+ }
+
+ if len(syncModels) > 0 {
+ if err := x.Sync(syncModels...); err != nil {
+ t.Errorf("error during sync: %v", err)
+ return x, deferFn
+ }
+ }
+
+ fixturesDir := filepath.Join(filepath.Dir(setting.AppPath), "models", "migrations", "fixtures", t.Name())
+
+ if _, err := os.Stat(fixturesDir); err == nil {
+ t.Logf("initializing fixtures from: %s", fixturesDir)
+ if err := unittest.InitFixtures(
+ unittest.FixturesOptions{
+ Dir: fixturesDir,
+ }, x); err != nil {
+ t.Errorf("error whilst initializing fixtures from %s: %v", fixturesDir, err)
+ return x, deferFn
+ }
+ if err := unittest.LoadFixtures(x); err != nil {
+ t.Errorf("error whilst loading fixtures from %s: %v", fixturesDir, err)
+ return x, deferFn
+ }
+ } else if !os.IsNotExist(err) {
+ t.Errorf("unexpected error whilst checking for existence of fixtures: %v", err)
+ } else {
+ t.Logf("no fixtures found in: %s", fixturesDir)
+ }
+
+ return x, deferFn
+}
+
+func MainTest(m *testing.M) {
+ log.RegisterEventWriter("test", testlogger.NewTestLoggerWriter)
+
+ giteaRoot := base.SetupGiteaRoot()
+ if giteaRoot == "" {
+ fmt.Println("Environment variable $GITEA_ROOT not set")
+ os.Exit(1)
+ }
+ giteaBinary := "gitea"
+ if runtime.GOOS == "windows" {
+ giteaBinary += ".exe"
+ }
+ setting.AppPath = path.Join(giteaRoot, giteaBinary)
+ if _, err := os.Stat(setting.AppPath); err != nil {
+ fmt.Printf("Could not find gitea binary at %s\n", setting.AppPath)
+ os.Exit(1)
+ }
+
+ giteaConf := os.Getenv("GITEA_CONF")
+ if giteaConf == "" {
+ giteaConf = path.Join(filepath.Dir(setting.AppPath), "tests/sqlite.ini")
+ fmt.Printf("Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf)
+ }
+
+ if !path.IsAbs(giteaConf) {
+ setting.CustomConf = path.Join(giteaRoot, giteaConf)
+ } else {
+ setting.CustomConf = giteaConf
+ }
+
+ tmpDataPath, err := os.MkdirTemp("", "data")
+ if err != nil {
+ fmt.Printf("Unable to create temporary data path %v\n", err)
+ os.Exit(1)
+ }
+
+ setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
+ setting.AppDataPath = tmpDataPath
+
+ unittest.InitSettings()
+ if err = git.InitFull(context.Background()); err != nil {
+ fmt.Printf("Unable to InitFull: %v\n", err)
+ os.Exit(1)
+ }
+ setting.LoadDBSetting()
+ setting.InitLoggersForTest()
+
+ exitStatus := m.Run()
+
+ if err := testlogger.WriterCloser.Reset(); err != nil && exitStatus == 0 {
+ fmt.Printf("testlogger.WriterCloser.Reset: error ignored: %v\n", err)
+ }
+ if err := removeAllWithRetry(setting.RepoRootPath); err != nil {
+ fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
+ }
+ if err := removeAllWithRetry(tmpDataPath); err != nil {
+ fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
+ }
+ os.Exit(exitStatus)
+}
+
+func newXORMEngine() (*xorm.Engine, error) {
+ if err := db.InitEngine(context.Background()); err != nil {
+ return nil, err
+ }
+ x := unittest.GetXORMEngine()
+ return x, nil
+}
+
+func deleteDB() error {
+ switch {
+ case setting.Database.Type.IsSQLite3():
+ if err := util.Remove(setting.Database.Path); err != nil {
+ return err
+ }
+ return os.MkdirAll(path.Dir(setting.Database.Path), os.ModePerm)
+
+ case setting.Database.Type.IsMySQL():
+ db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/",
+ setting.Database.User, setting.Database.Passwd, setting.Database.Host))
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ databaseName := strings.SplitN(setting.Database.Name, "?", 2)[0]
+
+ if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", databaseName)); err != nil {
+ return err
+ }
+
+ if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", databaseName)); err != nil {
+ return err
+ }
+ return nil
+ case setting.Database.Type.IsPostgreSQL():
+ db, err := sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/?sslmode=%s",
+ setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.SSLMode))
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name)); err != nil {
+ return err
+ }
+
+ if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil {
+ return err
+ }
+ db.Close()
+
+ // Check if we need to setup a specific schema
+ if len(setting.Database.Schema) != 0 {
+ db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
+ setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema))
+ if err != nil {
+ return err
+ }
+ defer schrows.Close()
+
+ if !schrows.Next() {
+ // Create and setup a DB schema
+ _, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema))
+ if err != nil {
+ return err
+ }
+ }
+
+ // Make the user's default search path the created schema; this will affect new connections
+ _, err = db.Exec(fmt.Sprintf(`ALTER USER "%s" SET search_path = %s`, setting.Database.User, setting.Database.Schema))
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+
+ return nil
+}
+
+func removeAllWithRetry(dir string) error {
+ var err error
+ for i := 0; i < 20; i++ {
+ err = os.RemoveAll(dir)
+ if err == nil {
+ break
+ }
+ time.Sleep(100 * time.Millisecond)
+ }
+ return err
+}