code.gitea.io/gitea@v1.22.3/tests/test_utils.go (about)

     1  // Copyright 2017 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  //nolint:forbidigo
     5  package tests
     6  
     7  import (
     8  	"context"
     9  	"database/sql"
    10  	"fmt"
    11  	"os"
    12  	"path"
    13  	"path/filepath"
    14  	"testing"
    15  
    16  	"code.gitea.io/gitea/models/db"
    17  	packages_model "code.gitea.io/gitea/models/packages"
    18  	"code.gitea.io/gitea/models/unittest"
    19  	"code.gitea.io/gitea/modules/base"
    20  	"code.gitea.io/gitea/modules/git"
    21  	"code.gitea.io/gitea/modules/graceful"
    22  	"code.gitea.io/gitea/modules/log"
    23  	repo_module "code.gitea.io/gitea/modules/repository"
    24  	"code.gitea.io/gitea/modules/setting"
    25  	"code.gitea.io/gitea/modules/storage"
    26  	"code.gitea.io/gitea/modules/testlogger"
    27  	"code.gitea.io/gitea/modules/util"
    28  	"code.gitea.io/gitea/routers"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  )
    32  
    33  func exitf(format string, args ...any) {
    34  	fmt.Printf(format+"\n", args...)
    35  	os.Exit(1)
    36  }
    37  
    38  func InitTest(requireGitea bool) {
    39  	log.RegisterEventWriter("test", testlogger.NewTestLoggerWriter)
    40  
    41  	giteaRoot := base.SetupGiteaRoot()
    42  	if giteaRoot == "" {
    43  		exitf("Environment variable $GITEA_ROOT not set")
    44  	}
    45  
    46  	// TODO: Speedup tests that rely on the event source ticker, confirm whether there is any bug or failure.
    47  	// setting.UI.Notification.EventSourceUpdateTime = time.Second
    48  
    49  	setting.AppWorkPath = giteaRoot
    50  	setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
    51  	if requireGitea {
    52  		giteaBinary := "gitea"
    53  		if setting.IsWindows {
    54  			giteaBinary += ".exe"
    55  		}
    56  		setting.AppPath = path.Join(giteaRoot, giteaBinary)
    57  		if _, err := os.Stat(setting.AppPath); err != nil {
    58  			exitf("Could not find gitea binary at %s", setting.AppPath)
    59  		}
    60  	}
    61  	giteaConf := os.Getenv("GITEA_CONF")
    62  	if giteaConf == "" {
    63  		// By default, use sqlite.ini for testing, then IDE like GoLand can start the test process with debugger.
    64  		// It's easier for developers to debug bugs step by step with a debugger.
    65  		// Notice: when doing "ssh push", Gitea executes sub processes, debugger won't work for the sub processes.
    66  		giteaConf = "tests/sqlite.ini"
    67  		_ = os.Setenv("GITEA_CONF", giteaConf)
    68  		fmt.Printf("Environment variable $GITEA_CONF not set, use default: %s\n", giteaConf)
    69  		if !setting.EnableSQLite3 {
    70  			exitf(`sqlite3 requires: import _ "github.com/mattn/go-sqlite3" or -tags sqlite,sqlite_unlock_notify`)
    71  		}
    72  	}
    73  	if !path.IsAbs(giteaConf) {
    74  		setting.CustomConf = filepath.Join(giteaRoot, giteaConf)
    75  	} else {
    76  		setting.CustomConf = giteaConf
    77  	}
    78  
    79  	unittest.InitSettings()
    80  	setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
    81  	_ = util.RemoveAll(repo_module.LocalCopyPath())
    82  
    83  	if err := git.InitFull(context.Background()); err != nil {
    84  		log.Fatal("git.InitOnceWithSync: %v", err)
    85  	}
    86  
    87  	setting.LoadDBSetting()
    88  	if err := storage.Init(); err != nil {
    89  		exitf("Init storage failed: %v", err)
    90  	}
    91  
    92  	switch {
    93  	case setting.Database.Type.IsMySQL():
    94  		connType := "tcp"
    95  		if len(setting.Database.Host) > 0 && setting.Database.Host[0] == '/' { // looks like a unix socket
    96  			connType = "unix"
    97  		}
    98  
    99  		db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@%s(%s)/",
   100  			setting.Database.User, setting.Database.Passwd, connType, setting.Database.Host))
   101  		defer db.Close()
   102  		if err != nil {
   103  			log.Fatal("sql.Open: %v", err)
   104  		}
   105  		if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", setting.Database.Name)); err != nil {
   106  			log.Fatal("db.Exec: %v", err)
   107  		}
   108  	case setting.Database.Type.IsPostgreSQL():
   109  		var db *sql.DB
   110  		var err error
   111  		if setting.Database.Host[0] == '/' {
   112  			db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@/%s?sslmode=%s&host=%s",
   113  				setting.Database.User, setting.Database.Passwd, setting.Database.Name, setting.Database.SSLMode, setting.Database.Host))
   114  		} else {
   115  			db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
   116  				setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
   117  		}
   118  
   119  		defer db.Close()
   120  		if err != nil {
   121  			log.Fatal("sql.Open: %v", err)
   122  		}
   123  		dbrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", setting.Database.Name))
   124  		if err != nil {
   125  			log.Fatal("db.Query: %v", err)
   126  		}
   127  		defer dbrows.Close()
   128  
   129  		if !dbrows.Next() {
   130  			if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil {
   131  				log.Fatal("db.Exec: CREATE DATABASE: %v", err)
   132  			}
   133  		}
   134  		// Check if we need to setup a specific schema
   135  		if len(setting.Database.Schema) == 0 {
   136  			break
   137  		}
   138  		db.Close()
   139  
   140  		if setting.Database.Host[0] == '/' {
   141  			db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@/%s?sslmode=%s&host=%s",
   142  				setting.Database.User, setting.Database.Passwd, setting.Database.Name, setting.Database.SSLMode, setting.Database.Host))
   143  		} else {
   144  			db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
   145  				setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
   146  		}
   147  		// This is a different db object; requires a different Close()
   148  		defer db.Close()
   149  		if err != nil {
   150  			log.Fatal("sql.Open: %v", err)
   151  		}
   152  		schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema))
   153  		if err != nil {
   154  			log.Fatal("db.Query: %v", err)
   155  		}
   156  		defer schrows.Close()
   157  
   158  		if !schrows.Next() {
   159  			// Create and setup a DB schema
   160  			if _, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema)); err != nil {
   161  				log.Fatal("db.Exec: CREATE SCHEMA: %v", err)
   162  			}
   163  		}
   164  
   165  	case setting.Database.Type.IsMSSQL():
   166  		host, port := setting.ParseMSSQLHostPort(setting.Database.Host)
   167  		db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;",
   168  			host, port, "master", setting.Database.User, setting.Database.Passwd))
   169  		if err != nil {
   170  			log.Fatal("sql.Open: %v", err)
   171  		}
   172  		if _, err := db.Exec(fmt.Sprintf("If(db_id(N'%s') IS NULL) BEGIN CREATE DATABASE %s; END;", setting.Database.Name, setting.Database.Name)); err != nil {
   173  			log.Fatal("db.Exec: %v", err)
   174  		}
   175  		defer db.Close()
   176  	}
   177  
   178  	routers.InitWebInstalled(graceful.GetManager().HammerContext())
   179  }
   180  
   181  func PrepareAttachmentsStorage(t testing.TB) {
   182  	// prepare attachments directory and files
   183  	assert.NoError(t, storage.Clean(storage.Attachments))
   184  
   185  	s, err := storage.NewStorage(setting.LocalStorageType, &setting.Storage{
   186  		Path: filepath.Join(filepath.Dir(setting.AppPath), "tests", "testdata", "data", "attachments"),
   187  	})
   188  	assert.NoError(t, err)
   189  	assert.NoError(t, s.IterateObjects("", func(p string, obj storage.Object) error {
   190  		_, err = storage.Copy(storage.Attachments, p, s, p)
   191  		return err
   192  	}))
   193  }
   194  
   195  func PrepareTestEnv(t testing.TB, skip ...int) func() {
   196  	t.Helper()
   197  	ourSkip := 1
   198  	if len(skip) > 0 {
   199  		ourSkip += skip[0]
   200  	}
   201  	deferFn := PrintCurrentTest(t, ourSkip)
   202  
   203  	// load database fixtures
   204  	assert.NoError(t, unittest.LoadFixtures())
   205  
   206  	// load git repo fixtures
   207  	assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
   208  	assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
   209  	ownerDirs, err := os.ReadDir(setting.RepoRootPath)
   210  	if err != nil {
   211  		assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
   212  	}
   213  	for _, ownerDir := range ownerDirs {
   214  		if !ownerDir.Type().IsDir() {
   215  			continue
   216  		}
   217  		repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
   218  		if err != nil {
   219  			assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
   220  		}
   221  		for _, repoDir := range repoDirs {
   222  			_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0o755)
   223  			_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0o755)
   224  			_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0o755)
   225  			_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0o755)
   226  			_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "pull"), 0o755)
   227  		}
   228  	}
   229  
   230  	// load LFS object fixtures
   231  	// (LFS storage can be on any of several backends, including remote servers, so we init it with the storage API)
   232  	lfsFixtures, err := storage.NewStorage(setting.LocalStorageType, &setting.Storage{
   233  		Path: filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-lfs-meta"),
   234  	})
   235  	assert.NoError(t, err)
   236  	assert.NoError(t, storage.Clean(storage.LFS))
   237  	assert.NoError(t, lfsFixtures.IterateObjects("", func(path string, _ storage.Object) error {
   238  		_, err := storage.Copy(storage.LFS, path, lfsFixtures, path)
   239  		return err
   240  	}))
   241  
   242  	// clear all package data
   243  	assert.NoError(t, db.TruncateBeans(db.DefaultContext,
   244  		&packages_model.Package{},
   245  		&packages_model.PackageVersion{},
   246  		&packages_model.PackageFile{},
   247  		&packages_model.PackageBlob{},
   248  		&packages_model.PackageProperty{},
   249  		&packages_model.PackageBlobUpload{},
   250  		&packages_model.PackageCleanupRule{},
   251  	))
   252  	assert.NoError(t, storage.Clean(storage.Packages))
   253  
   254  	return deferFn
   255  }
   256  
   257  func PrintCurrentTest(t testing.TB, skip ...int) func() {
   258  	t.Helper()
   259  	actualSkip := 1
   260  	if len(skip) > 0 {
   261  		actualSkip = skip[0] + 1
   262  	}
   263  	return testlogger.PrintCurrentTest(t, actualSkip)
   264  }
   265  
   266  // Printf takes a format and args and prints the string to os.Stdout
   267  func Printf(format string, args ...any) {
   268  	testlogger.Printf(format, args...)
   269  }