mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-07 08:13:50 +02:00
Fix cmd tests by mocking builtin paths (#37369)
After 07ada3666b, PrepareConsoleLoggerLevel can fail in tests when InstallLock is true, due to the incorrect config file is loaded. This PR fixes cmd test setup by mocking builtin paths Fixes #37368 --------- Co-authored-by: Morgan PEYRE <morgan.peyre@brickcode.tech> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
9894ebb79c
commit
8cfcef32c6
@ -4,6 +4,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
@ -82,7 +83,9 @@ func TestChangePasswordCommand(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
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.Error(t, err)
|
||||||
require.Contains(t, err.Error(), tc.expectedErr)
|
require.Contains(t, err.Error(), tc.expectedErr)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -107,6 +108,7 @@ func TestCertCommandFailures(t *testing.T) {
|
|||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
app := cmdCert()
|
app := cmdCert()
|
||||||
|
app.Writer, app.ErrWriter = io.Discard, io.Discard
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
certFile := filepath.Join(tempDir, "cert.pem")
|
certFile := filepath.Join(tempDir, "cert.pem")
|
||||||
|
|||||||
@ -124,7 +124,7 @@ func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cl
|
|||||||
if setting.InstallLock {
|
if setting.InstallLock {
|
||||||
// During config loading, there might also be logs (for example: deprecation warnings).
|
// 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.
|
// 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")
|
return nil, errors.New("console logger must be setup before config is loaded")
|
||||||
}
|
}
|
||||||
level := defaultLevel
|
level := defaultLevel
|
||||||
|
|||||||
@ -62,9 +62,10 @@ func runTestApp(app *cli.Command, args ...string) (runResult, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCliCmd(t *testing.T) {
|
func TestCliCmd(t *testing.T) {
|
||||||
defaultWorkPath := filepath.Dir(setting.AppPath)
|
defaultWorkPath := filepath.FromSlash("/tmp/mocked-work-path")
|
||||||
defaultCustomPath := filepath.Join(defaultWorkPath, "custom")
|
defaultCustomPath := filepath.Join(defaultWorkPath, "custom")
|
||||||
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
|
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
|
||||||
|
defer setting.MockBuiltinPaths(defaultWorkPath, "", "")()
|
||||||
|
|
||||||
cli.CommandHelpTemplate = "(command help template)"
|
cli.CommandHelpTemplate = "(command help template)"
|
||||||
cli.RootCommandHelpTemplate = "(app help template)"
|
cli.RootCommandHelpTemplate = "(app help template)"
|
||||||
@ -157,7 +158,6 @@ func TestCliCmd(t *testing.T) {
|
|||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.cmd, func(t *testing.T) {
|
t.Run(c.cmd, func(t *testing.T) {
|
||||||
defer test.MockVariableValue(&setting.InstallLock, false)()
|
|
||||||
app := newTestApp(cli.Command{
|
app := newTestApp(cli.Command{
|
||||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||||
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
||||||
|
|||||||
@ -202,16 +202,15 @@ func LoadTableSchemasMap(t *testing.T, x *xorm.Engine) map[string]*schemas.Table
|
|||||||
|
|
||||||
func mainTest(m *testing.M) int {
|
func mainTest(m *testing.M) int {
|
||||||
testlogger.Init()
|
testlogger.Init()
|
||||||
|
setting.SetupGiteaTestEnv()
|
||||||
|
|
||||||
tmpDataPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("data")
|
tmpDataPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("data")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
testlogger.Panicf("Unable to create temporary data path %v\n", err)
|
testlogger.Panicf("Unable to create temporary data path %v\n", err)
|
||||||
}
|
}
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
setting.AppDataPath = tmpDataPath
|
setting.AppDataPath = tmpDataPath
|
||||||
|
|
||||||
unittest.InitSettingsForTesting()
|
|
||||||
if err = git.InitFull(); err != nil {
|
if err = git.InitFull(); err != nil {
|
||||||
testlogger.Panicf("Unable to InitFull: %v\n", err)
|
testlogger.Panicf("Unable to InitFull: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,10 +13,8 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/system"
|
"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/cache"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/setting/config"
|
"code.gitea.io/gitea/modules/setting/config"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
@ -29,37 +27,6 @@ import (
|
|||||||
"xorm.io/xorm/names"
|
"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
|
// TestOptions represents test options
|
||||||
type TestOptions struct {
|
type TestOptions struct {
|
||||||
FixtureFiles []string
|
FixtureFiles []string
|
||||||
@ -75,11 +42,12 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
|
|||||||
|
|
||||||
func mainTest(m *testing.M, testOptsArg ...*TestOptions) int {
|
func mainTest(m *testing.M, testOptsArg ...*TestOptions) int {
|
||||||
testOpts := util.OptionalArg(testOptsArg, &TestOptions{})
|
testOpts := util.OptionalArg(testOptsArg, &TestOptions{})
|
||||||
InitSettingsForTesting()
|
setting.SetupGiteaTestEnv()
|
||||||
giteaRoot := setting.GetGiteaTestSourceRoot()
|
giteaRoot := setting.GetGiteaTestSourceRoot()
|
||||||
fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles}
|
fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles}
|
||||||
if err := CreateTestEngine(fixturesOpts); err != nil {
|
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/"
|
setting.AppURL = "https://try.gitea.io/"
|
||||||
|
|||||||
@ -198,6 +198,12 @@ func InitWorkPathAndCfgProvider(getEnvFn func(name string) string, args ArgWorkP
|
|||||||
CustomConf = tmpCustomConf.Value
|
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.
|
// 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.
|
// 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.
|
// Gitea only creates subdirectories under it, but not the APP_TEMP_PATH directory itself.
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/auth/password/hash"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,48 +27,84 @@ func SetupGiteaTestEnv() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
IsInTesting = true
|
IsInTesting = true
|
||||||
giteaRoot := os.Getenv("GITEA_TEST_ROOT")
|
|
||||||
if giteaRoot == "" {
|
log.OsExiter = func(code int) {
|
||||||
_, filename, _, _ := runtime.Caller(0)
|
if code != 0 {
|
||||||
giteaRoot = filepath.Dir(filepath.Dir(filepath.Dir(filename)))
|
// non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens, show a full stacktrace for more details
|
||||||
fixturesDir := filepath.Join(giteaRoot, "models", "fixtures")
|
panic(fmt.Errorf("non-zero exit code during testing: %d", code))
|
||||||
if _, err := os.Stat(fixturesDir); err != nil {
|
}
|
||||||
panic("in gitea source code directory, fixtures directory not found: " + fixturesDir)
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appWorkPathBuiltin = giteaRoot
|
initWorkPathAndConfig := func() {
|
||||||
AppWorkPath = giteaRoot
|
// init paths and config system for testing
|
||||||
AppPath = filepath.Join(giteaRoot, "gitea") + util.Iif(IsWindows, ".exe", "")
|
getTestEnv := func(key string) string { return "" }
|
||||||
StaticRootPath = giteaRoot // need to load assets (options, public) from the source code directory for testing
|
InitWorkPathAndCommonConfig(getTestEnv, ArgWorkPathAndCustomConf{CustomConf: CustomConf})
|
||||||
|
|
||||||
// giteaConf (GITEA_CONF) must be relative because it is used in the git hooks as "$GITEA_ROOT/$GITEA_CONF"
|
if err := PrepareAppDataPath(); err != nil {
|
||||||
giteaConf := os.Getenv("GITEA_TEST_CONF")
|
log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// 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)
|
// register the dummy hash algorithm function used in the test fixtures
|
||||||
UnsetUnnecessaryEnvVars()
|
_ = hash.Register("dummy", hash.NewDummyHasher)
|
||||||
for _, env := range os.Environ() {
|
PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
|
||||||
if strings.HasPrefix(env, "GIT_") || (strings.HasPrefix(env, "GITEA_") && !strings.HasPrefix(env, "GITEA_TEST_")) {
|
|
||||||
k, _, _ := strings.Cut(env, "=")
|
|
||||||
_ = os.Unsetenv(k)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// 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_ROOT", giteaRoot)
|
||||||
_ = os.Setenv("GITEA_CONF", giteaConf) // test fixture git hooks use "$GITEA_ROOT/$GITEA_CONF" in their scripts
|
_ = os.Setenv("GITEA_CONF", giteaConf) // test fixture git hooks use "$GITEA_ROOT/$GITEA_CONF" in their scripts
|
||||||
giteaTestSourceRoot = &giteaRoot
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ var currentEngine *xorm.Engine
|
|||||||
|
|
||||||
func initMigrationTest(t *testing.T) func() {
|
func initMigrationTest(t *testing.T) func() {
|
||||||
testlogger.Init()
|
testlogger.Init()
|
||||||
unittest.InitSettingsForTesting()
|
setting.SetupGiteaTestEnv()
|
||||||
|
|
||||||
assert.NotEmpty(t, setting.RepoRootPath)
|
assert.NotEmpty(t, setting.RepoRootPath)
|
||||||
assert.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
|
assert.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
|
||||||
|
|||||||
@ -6,6 +6,7 @@ package tests
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -26,7 +27,15 @@ import (
|
|||||||
|
|
||||||
func InitTest() {
|
func InitTest() {
|
||||||
testlogger.Init()
|
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"
|
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
|
||||||
|
|
||||||
if err := git.InitFull(); err != nil {
|
if err := git.InitFull(); err != nil {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user