mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-26 06:16:56 +09:00
Integration test processes now get their own work directory, config file and random ports, so several runs can execute in parallel on one machine. The slowest tests also lose fixed sleeps and needlessly expensive setup, with their assertions kept. - Batch PostgreSQL sequence resets, the main per-test fixture cost there - Cheap hashing params in hash and OAuth2 fixture tests, full-cost vectors stay - Single fetch for negative Actions runner assertions instead of 1s polls - Queue flushes and backdated timestamps instead of sleeps - In-memory SQLite test journal, tmpfs for PostgreSQL in CI - E2E instance writes SSH keys into its work directory instead of `~/.ssh/authorized_keys`
243 lines
6.7 KiB
Go
243 lines
6.7 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package unittest
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dev/models/db"
|
|
"gitea.dev/models/system"
|
|
"gitea.dev/modules/cache"
|
|
"gitea.dev/modules/git"
|
|
"gitea.dev/modules/setting"
|
|
"gitea.dev/modules/setting/config"
|
|
"gitea.dev/modules/storage"
|
|
"gitea.dev/modules/tempdir"
|
|
"gitea.dev/modules/testlogger"
|
|
"gitea.dev/modules/util"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"xorm.io/xorm"
|
|
"xorm.io/xorm/names"
|
|
)
|
|
|
|
// TestOptions represents test options
|
|
type TestOptions struct {
|
|
FixtureFiles []string
|
|
SetUp func() error // SetUp will be executed before all tests in this package
|
|
TearDown func() error // TearDown will be executed after all tests in this package
|
|
}
|
|
|
|
// MainTest a reusable TestMain(..) function for unit tests that need to use a
|
|
// test database. Creates the test database, and sets necessary settings.
|
|
func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
|
|
os.Exit(mainTest(m, testOptsArg...))
|
|
}
|
|
|
|
func mainTest(m *testing.M, testOptsArg ...*TestOptions) int {
|
|
testOpts := util.OptionalArg(testOptsArg, &TestOptions{})
|
|
|
|
tempWorkPath, tempCleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("unit-test-dir-")
|
|
if err != nil {
|
|
return testlogger.MainErrorf("Failed to create temp dir for unit test: %v", err)
|
|
}
|
|
defer tempCleanup()
|
|
|
|
defer setting.MockBuiltinPaths(tempWorkPath, "", "")()
|
|
setting.SetupGiteaTestEnv()
|
|
|
|
giteaRoot := setting.GetGiteaTestSourceRoot()
|
|
fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles}
|
|
if err := CreateTestEngine(filepath.Join(tempWorkPath, "sqlite-test.db"), fixturesOpts); err != nil {
|
|
return testlogger.MainErrorf("Error creating test database engine: %v", err)
|
|
}
|
|
|
|
setting.AppURL = "https://try.gitea.io/"
|
|
setting.RunUser = "runuser"
|
|
setting.SSH.User = "sshuser"
|
|
setting.SSH.BuiltinServerUser = "builtinuser"
|
|
setting.SSH.Port = 3000
|
|
setting.SSH.Domain = "try.gitea.io"
|
|
setting.Database.Type = "sqlite3"
|
|
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
|
|
setting.GravatarSource = "https://secure.gravatar.com/avatar/"
|
|
setting.IncomingEmail.ReplyToAddress = "incoming+%{token}@localhost"
|
|
|
|
config.SetDynGetter(system.NewDatabaseDynKeyGetter())
|
|
|
|
if err = cache.Init(); err != nil {
|
|
return testlogger.MainErrorf("cache.Init: %v", err)
|
|
}
|
|
if err = storage.Init(); err != nil {
|
|
return testlogger.MainErrorf("storage.Init: %v", err)
|
|
}
|
|
if err = SyncDirs(filepath.Join(giteaRoot, "tests", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
|
|
return testlogger.MainErrorf("util.SyncDirs: %v", err)
|
|
}
|
|
|
|
if err = git.InitFull(); err != nil {
|
|
return testlogger.MainErrorf("git.Init: %v", err)
|
|
}
|
|
|
|
if testOpts.SetUp != nil {
|
|
if err := testOpts.SetUp(); err != nil {
|
|
return testlogger.MainErrorf("set up failed: %v", err)
|
|
}
|
|
}
|
|
|
|
exitStatus := m.Run()
|
|
|
|
if testOpts.TearDown != nil {
|
|
if err := testOpts.TearDown(); err != nil {
|
|
return testlogger.MainErrorf("tear down failed: %v", err)
|
|
}
|
|
}
|
|
return exitStatus
|
|
}
|
|
|
|
func ResetTestDatabase() (cleanup func(), err error) {
|
|
defer func() {
|
|
if cleanup == nil {
|
|
cleanup = func() {}
|
|
}
|
|
}()
|
|
|
|
connOpts := db.GlobalConnOptions()
|
|
driverDefault, connStrDefault, err := db.ConnStrDefaultDatabase(connOpts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
driverDatabase, connStrDatabase, err := db.ConnStr(connOpts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if connOpts.Type.IsSQLite3() {
|
|
if !strings.HasSuffix(connOpts.SQLitePath, "-test.db") {
|
|
return nil, errors.New(`testing database file for sqlite3 must end in "-test.db"`)
|
|
}
|
|
_ = os.Remove(connOpts.SQLitePath)
|
|
err = os.MkdirAll(filepath.Dir(connOpts.SQLitePath), os.ModePerm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cleanup = func() {
|
|
_ = os.Remove(connOpts.SQLitePath)
|
|
_ = os.Remove(filepath.Dir(connOpts.SQLitePath))
|
|
}
|
|
return cleanup, nil
|
|
}
|
|
|
|
if !strings.Contains(connOpts.Database, "test") {
|
|
return nil, fmt.Errorf(`testing database name for %s must contain "test"`, connOpts.Database)
|
|
}
|
|
|
|
quotedDbName := connOpts.Database
|
|
if connOpts.Type.IsMSSQL() {
|
|
quotedDbName = `[` + connOpts.Database + `]`
|
|
}
|
|
|
|
sqlExec := func(sqlDB *sql.DB, sql string) error {
|
|
_, err := sqlDB.Exec(sql)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute SQL %q: %w", sql, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
createDatabase := func() error {
|
|
sqlDB, err := sql.Open(driverDefault, connStrDefault)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer sqlDB.Close()
|
|
if err = sqlExec(sqlDB, "DROP DATABASE IF EXISTS "+quotedDbName); err != nil {
|
|
return err
|
|
}
|
|
return sqlExec(sqlDB, "CREATE DATABASE "+quotedDbName)
|
|
}
|
|
if err = createDatabase(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cleanup = func() {
|
|
sqlDB, err := sql.Open(driverDefault, connStrDefault)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer sqlDB.Close()
|
|
_, _ = sqlDB.Exec("DROP DATABASE IF EXISTS " + quotedDbName)
|
|
}
|
|
|
|
createDatabaseSchema := func() error {
|
|
if !connOpts.Type.IsPostgreSQL() {
|
|
return nil
|
|
}
|
|
if connOpts.Schema == "" {
|
|
return nil
|
|
}
|
|
sqlDB, err := sql.Open(driverDatabase, connStrDatabase)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer sqlDB.Close()
|
|
if err = sqlExec(sqlDB, "DROP SCHEMA IF EXISTS "+connOpts.Schema); err != nil {
|
|
return err
|
|
}
|
|
return sqlExec(sqlDB, "CREATE SCHEMA "+connOpts.Schema)
|
|
}
|
|
|
|
return cleanup, createDatabaseSchema()
|
|
}
|
|
|
|
// FixturesOptions fixtures needs to be loaded options
|
|
type FixturesOptions struct {
|
|
Dir string
|
|
Files []string
|
|
}
|
|
|
|
// CreateTestEngine creates a test database and loads the fixture data from fixturesDir
|
|
func CreateTestEngine(testSQLiteFile string, opts FixturesOptions) error {
|
|
driver, connStr, err := db.ConnStr(db.ConnOptions{Type: setting.DatabaseTypeSQLite3, SQLitePath: testSQLiteFile, SQLiteBusyTimeout: setting.DefaultSQLiteBusyTimeout, SQLiteJournalMode: "MEMORY"})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
x, err := xorm.NewEngine(driver, connStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
x.SetMapper(names.GonicMapper{})
|
|
db.SetDefaultEngine(context.Background(), x)
|
|
|
|
if err = db.SyncAllTables(); err != nil {
|
|
return err
|
|
}
|
|
switch os.Getenv("GITEA_TEST_LOG_SQL") {
|
|
case "true", "1":
|
|
x.ShowSQL(true)
|
|
}
|
|
|
|
return InitFixtures(opts)
|
|
}
|
|
|
|
// PrepareTestDatabase load test fixtures into test database
|
|
func PrepareTestDatabase() error {
|
|
return LoadFixtures()
|
|
}
|
|
|
|
// PrepareTestEnv prepares the environment for unit tests. Can only be called
|
|
// by tests that use the above MainTest(..) function.
|
|
func PrepareTestEnv(t testing.TB) {
|
|
assert.NoError(t, PrepareTestDatabase())
|
|
metaPath := filepath.Join(setting.GetGiteaTestSourceRoot(), "tests", "gitea-repositories-meta")
|
|
assert.NoError(t, SyncDirs(metaPath, setting.RepoRootPath))
|
|
}
|