Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cmd/admin_user_change_password_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package cmd

import (
"io"
"testing"

"code.gitea.io/gitea/models/db"
Expand Down Expand Up @@ -82,7 +83,9 @@ func TestChangePasswordCommand(t *testing.T) {

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := microcmdUserChangePassword().Run(ctx, tc.args)
cmd := microcmdUserChangePassword()
cmd.Writer, cmd.ErrWriter = io.Discard, io.Discard
err := cmd.Run(ctx, tc.args)
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
})
Expand Down
2 changes: 2 additions & 0 deletions cmd/cert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package cmd

import (
"io"
"path/filepath"
"testing"

Expand Down Expand Up @@ -107,6 +108,7 @@ func TestCertCommandFailures(t *testing.T) {
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
app := cmdCert()
app.Writer, app.ErrWriter = io.Discard, io.Discard
tempDir := t.TempDir()

certFile := filepath.Join(tempDir, "cert.pem")
Expand Down
2 changes: 1 addition & 1 deletion cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cl
if setting.InstallLock {
// During config loading, there might also be logs (for example: deprecation warnings).
// It must make sure that console logger is set up before config is loaded.
log.Error("Config is loaded before console logger is setup, it will cause bugs. Please fix it.")
log.Error("Config is loaded before console logger is setup, it will cause bugs. Please fix it. CustomConf=%s", setting.CustomConf)
return nil, errors.New("console logger must be setup before config is loaded")
}
level := defaultLevel
Expand Down
4 changes: 2 additions & 2 deletions cmd/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ func runTestApp(app *cli.Command, args ...string) (runResult, error) {
}

func TestCliCmd(t *testing.T) {
defaultWorkPath := filepath.Dir(setting.AppPath)
defaultWorkPath := filepath.FromSlash("/tmp/mocked-work-path")
defaultCustomPath := filepath.Join(defaultWorkPath, "custom")
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
defer setting.MockBuiltinPaths(defaultWorkPath, "", "")()

cli.CommandHelpTemplate = "(command help template)"
cli.RootCommandHelpTemplate = "(app help template)"
Expand Down Expand Up @@ -157,7 +158,6 @@ func TestCliCmd(t *testing.T) {

for _, c := range cases {
t.Run(c.cmd, func(t *testing.T) {
defer test.MockVariableValue(&setting.InstallLock, false)()
app := newTestApp(cli.Command{
Action: func(ctx context.Context, cmd *cli.Command) error {
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
Expand Down
3 changes: 1 addition & 2 deletions models/migrations/base/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,16 +202,15 @@ func LoadTableSchemasMap(t *testing.T, x *xorm.Engine) map[string]*schemas.Table

func mainTest(m *testing.M) int {
testlogger.Init()
setting.SetupGiteaTestEnv()

tmpDataPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("data")
if err != nil {
testlogger.Panicf("Unable to create temporary data path %v\n", err)
}
defer cleanup()

setting.AppDataPath = tmpDataPath

unittest.InitSettingsForTesting()
if err = git.InitFull(); err != nil {
testlogger.Panicf("Unable to InitFull: %v\n", err)
}
Expand Down
38 changes: 3 additions & 35 deletions models/unittest/testdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ import (

"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/setting/config"
"code.gitea.io/gitea/modules/storage"
Expand All @@ -29,37 +27,6 @@ import (
"xorm.io/xorm/names"
)

// InitSettingsForTesting initializes config provider and load common settings for tests
func InitSettingsForTesting() {
setting.SetupGiteaTestEnv()

log.OsExiter = func(code int) {
if code != 0 {
// non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens, show a full stacktrace for more details
panic(fmt.Errorf("non-zero exit code during testing: %d", code))
}
os.Exit(0)
}
if setting.CustomConf == "" {
setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini")
_ = os.Remove(setting.CustomConf)
}

// init paths and config system for testing
getTestEnv := func(key string) string {
return ""
}
setting.InitWorkPathAndCommonConfig(getTestEnv, setting.ArgWorkPathAndCustomConf{CustomConf: setting.CustomConf})

if err := setting.PrepareAppDataPath(); err != nil {
log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
}
// register the dummy hash algorithm function used in the test fixtures
_ = hash.Register("dummy", hash.NewDummyHasher)

setting.PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
}

// TestOptions represents test options
type TestOptions struct {
FixtureFiles []string
Expand All @@ -75,11 +42,12 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) {

func mainTest(m *testing.M, testOptsArg ...*TestOptions) int {
testOpts := util.OptionalArg(testOptsArg, &TestOptions{})
InitSettingsForTesting()
setting.SetupGiteaTestEnv()
giteaRoot := setting.GetGiteaTestSourceRoot()
fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles}
if err := CreateTestEngine(fixturesOpts); err != nil {
testlogger.Panicf("Error creating test engine: %v\n", err)
_, _ = fmt.Fprintf(os.Stderr, "Error creating test database engine: %v\n", err)
os.Exit(1)
}

setting.AppURL = "https://try.gitea.io/"
Expand Down
6 changes: 6 additions & 0 deletions modules/setting/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ func InitWorkPathAndCfgProvider(getEnvFn func(name string) string, args ArgWorkP
CustomConf = tmpCustomConf.Value
}

func MockBuiltinPaths(workPath, customPath, customConf string) func() {
oldApp, oldCustom, oldConf := appWorkPathBuiltin, customPathBuiltin, customConfBuiltin
appWorkPathBuiltin, customPathBuiltin, customConfBuiltin = workPath, customPath, customConf
return func() { appWorkPathBuiltin, customPathBuiltin, customConfBuiltin = oldApp, oldCustom, oldConf }
}

// AppDataTempDir returns a managed temporary directory for the application data.
// Using empty sub will get the managed base temp directory, and it's safe to delete it.
// Gitea only creates subdirectories under it, but not the APP_TEMP_PATH directory itself.
Expand Down
104 changes: 71 additions & 33 deletions modules/setting/testenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"runtime"
"strings"

"code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
)

Expand All @@ -25,48 +27,84 @@ func SetupGiteaTestEnv() {
}

IsInTesting = true
giteaRoot := os.Getenv("GITEA_TEST_ROOT")
if giteaRoot == "" {
_, filename, _, _ := runtime.Caller(0)
giteaRoot = filepath.Dir(filepath.Dir(filepath.Dir(filename)))
fixturesDir := filepath.Join(giteaRoot, "models", "fixtures")
if _, err := os.Stat(fixturesDir); err != nil {
panic("in gitea source code directory, fixtures directory not found: " + fixturesDir)

log.OsExiter = func(code int) {
if code != 0 {
// non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens, show a full stacktrace for more details
panic(fmt.Errorf("non-zero exit code during testing: %d", code))
}
os.Exit(0)
}

appWorkPathBuiltin = giteaRoot
AppWorkPath = giteaRoot
AppPath = filepath.Join(giteaRoot, "gitea") + util.Iif(IsWindows, ".exe", "")
StaticRootPath = giteaRoot // need to load assets (options, public) from the source code directory for testing

// giteaConf (GITEA_CONF) must be relative because it is used in the git hooks as "$GITEA_ROOT/$GITEA_CONF"
giteaConf := os.Getenv("GITEA_TEST_CONF")
if giteaConf == "" {
// By default, use sqlite.ini for testing, then IDE like GoLand can start the test process with debugger.
// It's easier for developers to debug bugs step by step with a debugger.
// Notice: when doing "ssh push", Gitea executes sub processes, debugger won't work for the sub processes.
giteaConf = "tests/sqlite.ini"
_, _ = fmt.Fprintf(os.Stderr, "Environment variable GITEA_TEST_CONF not set - defaulting to %s\n", giteaConf)
if !EnableSQLite3 {
_, _ = fmt.Fprintf(os.Stderr, "sqlite3 requires: -tags sqlite,sqlite_unlock_notify\n")
os.Exit(1)
initGiteaRoot := func() string {
giteaRoot := os.Getenv("GITEA_TEST_ROOT")
if giteaRoot == "" {
_, filename, _, _ := runtime.Caller(0)
giteaRoot = filepath.Dir(filepath.Dir(filepath.Dir(filename)))
fixturesDir := filepath.Join(giteaRoot, "models", "fixtures")
if _, err := os.Stat(fixturesDir); err != nil {
panic("in gitea source code directory, fixtures directory not found: " + fixturesDir)
}
}
giteaTestSourceRoot = &giteaRoot
return giteaRoot
}

initGiteaPaths := func() {
appWorkPathBuiltin = *giteaTestSourceRoot
AppWorkPath = appWorkPathBuiltin
AppPath = filepath.Join(AppWorkPath, "gitea") + util.Iif(IsWindows, ".exe", "")
StaticRootPath = AppWorkPath // need to load assets (options, public) from the source code directory for testing
}
// CustomConf must be absolute path to make tests pass,
CustomConf = filepath.Join(AppWorkPath, giteaConf)

// also unset unnecessary env vars for testing (only keep "GITEA_TEST_*" ones)
UnsetUnnecessaryEnvVars()
for _, env := range os.Environ() {
if strings.HasPrefix(env, "GIT_") || (strings.HasPrefix(env, "GITEA_") && !strings.HasPrefix(env, "GITEA_TEST_")) {
k, _, _ := strings.Cut(env, "=")
_ = os.Unsetenv(k)

initGiteaConf := func() string {
// giteaConf (GITEA_CONF) must be relative because it is used in the git hooks as "$GITEA_ROOT/$GITEA_CONF"
giteaConf := os.Getenv("GITEA_TEST_CONF")
if giteaConf == "" {
// if no GITEA_TEST_CONF, then it is in unit test, use a temp (non-existing / empty) config file
giteaConf = "custom/conf/app-test-tmp.ini"
customConfBuiltin = filepath.Join(AppWorkPath, giteaConf)
CustomConf = customConfBuiltin
_ = os.Remove(CustomConf)
} else {
// CustomConf must be absolute path to make tests pass,
CustomConf = filepath.Join(AppWorkPath, giteaConf)
}
return giteaConf
}

cleanUpEnv := func() {
// also unset unnecessary env vars for testing (only keep "GITEA_TEST_*" ones)
UnsetUnnecessaryEnvVars()
for _, env := range os.Environ() {
if strings.HasPrefix(env, "GIT_") || (strings.HasPrefix(env, "GITEA_") && !strings.HasPrefix(env, "GITEA_TEST_")) {
k, _, _ := strings.Cut(env, "=")
_ = os.Unsetenv(k)
}
}
}

initWorkPathAndConfig := func() {
// init paths and config system for testing
getTestEnv := func(key string) string { return "" }
InitWorkPathAndCommonConfig(getTestEnv, ArgWorkPathAndCustomConf{CustomConf: CustomConf})

if err := PrepareAppDataPath(); err != nil {
log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
}

// register the dummy hash algorithm function used in the test fixtures
_ = hash.Register("dummy", hash.NewDummyHasher)
PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
}

giteaRoot := initGiteaRoot()
initGiteaPaths()
giteaConf := initGiteaConf()
cleanUpEnv()
initWorkPathAndConfig()

// TODO: some git repo hooks (test fixtures) still use these env variables, need to be refactored in the future
_ = os.Setenv("GITEA_ROOT", giteaRoot)
_ = os.Setenv("GITEA_CONF", giteaConf) // test fixture git hooks use "$GITEA_ROOT/$GITEA_CONF" in their scripts
giteaTestSourceRoot = &giteaRoot
}
2 changes: 1 addition & 1 deletion tests/integration/migration-test/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var currentEngine *xorm.Engine

func initMigrationTest(t *testing.T) func() {
testlogger.Init()
unittest.InitSettingsForTesting()
setting.SetupGiteaTestEnv()

assert.NotEmpty(t, setting.RepoRootPath)
assert.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
Expand Down
11 changes: 10 additions & 1 deletion tests/test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package tests
import (
"database/sql"
"fmt"
"os"
"path/filepath"
"testing"

Expand All @@ -26,7 +27,15 @@ import (

func InitTest() {
testlogger.Init()
unittest.InitSettingsForTesting()
if os.Getenv("GITEA_TEST_CONF") == "" {
// By default, use sqlite.ini for testing, then IDE like GoLand can start the test process with debugger.
// It's easier for developers to debug bugs step by step with a debugger.
// Notice: when doing "ssh push", Gitea executes sub processes, debugger won't work for the sub processes.
giteaConf := "tests/sqlite.ini"
_ = os.Setenv("GITEA_TEST_CONF", giteaConf)
_, _ = fmt.Fprintf(os.Stderr, "Environment variable GITEA_TEST_CONF not set - defaulting to %s\n", giteaConf)
}
setting.SetupGiteaTestEnv()
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"

if err := git.InitFull(); err != nil {
Expand Down