Improve testing init, clean up webhook tests (#37412)

Avoid webhook test fixtures affect other tests (be triggered)

Also fixed more testing problems including path init, global config
pollution & conflict

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: wxiaoguang <2114189+wxiaoguang@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
Copilot
2026-04-25 18:55:18 +00:00
committed by GitHub
parent 24b60f8ff9
commit 9b9fb95559
25 changed files with 692 additions and 710 deletions

View File

@@ -1,38 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v3"
)
func TestDefaultCommand(t *testing.T) {
test := func(t *testing.T, args []string, expectedRetName string, expectedRetValid bool) {
called := false
cmd := &cli.Command{
DefaultCommand: "test",
Commands: []*cli.Command{
{
Name: "test",
Action: func(ctx context.Context, command *cli.Command) error {
retName, retValid := isValidDefaultSubCommand(command)
assert.Equal(t, expectedRetName, retName)
assert.Equal(t, expectedRetValid, retValid)
called = true
return nil
},
},
},
}
assert.NoError(t, cmd.Run(t.Context(), args))
assert.True(t, called)
}
test(t, []string{"./gitea"}, "", true)
test(t, []string{"./gitea", "test"}, "", true)
test(t, []string{"./gitea", "other"}, "other", false)
}

237
cmd/cmdtest/cmd_test.go Normal file
View File

@@ -0,0 +1,237 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Tests here reload the config system multiple times with uncontrollable details.
// So they must be in a separate package, to avoid affecting other tests
package cmdtest
import (
"context"
"errors"
"fmt"
"io"
"path/filepath"
"strings"
"testing"
"code.gitea.io/gitea/cmd"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v3"
)
func TestMain(m *testing.M) {
unittest.MainTest(m)
}
func makePathOutput(workPath, customPath, customConf string) string {
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf)
}
func newTestApp(testCmd cli.Command) *cli.Command {
app := cmd.NewMainApp(cmd.AppVersion{})
testCmd.Name = util.IfZero(testCmd.Name, "test-cmd")
cmd.PrepareSubcommandWithGlobalFlags(&testCmd)
app.Commands = append(app.Commands, &testCmd)
app.DefaultCommand = testCmd.Name
return app
}
type runResult struct {
Stdout string
Stderr string
ExitCode int
}
func runTestApp(app *cli.Command, args ...string) (runResult, error) {
outBuf := new(strings.Builder)
errBuf := new(strings.Builder)
app.Writer = outBuf
app.ErrWriter = errBuf
exitCode := -1
defer test.MockVariableValue(&cli.ErrWriter, app.ErrWriter)()
defer test.MockVariableValue(&cli.OsExiter, func(code int) {
if exitCode == -1 {
exitCode = code // save the exit code once and then reset the writer (to simulate the exit)
app.Writer, app.ErrWriter, cli.ErrWriter = io.Discard, io.Discard, io.Discard
}
})()
err := cmd.RunMainApp(app, args...)
return runResult{outBuf.String(), errBuf.String(), exitCode}, err
}
func TestCliCmd(t *testing.T) {
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)"
cli.SubcommandHelpTemplate = "(subcommand help template)"
cases := []struct {
env map[string]string
cmd string
exp string
}{
// help commands
{
cmd: "./gitea -h",
exp: "DEFAULT CONFIGURATION:",
},
{
cmd: "./gitea help",
exp: "DEFAULT CONFIGURATION:",
},
{
cmd: "./gitea -c /dev/null -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea help -c /dev/null",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null test-cmd -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -c /dev/null -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -h -c /dev/null",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null test-cmd help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -c /dev/null help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd help -c /dev/null",
exp: "ConfigFile: /dev/null",
},
// parse paths
{
cmd: "./gitea test-cmd",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, defaultCustomConf),
},
{
cmd: "./gitea -c /tmp/app.ini test-cmd",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
},
{
cmd: "./gitea test-cmd -c /tmp/app.ini",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd",
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/custom/conf/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd --work-path /tmp/other",
exp: makePathOutput("/tmp/other", "/tmp/other/custom", "/tmp/other/custom/conf/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd --config /tmp/app-other.ini",
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"),
},
}
for _, c := range cases {
t.Run(c.cmd, func(t *testing.T) {
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))
return nil
},
})
for k, v := range c.env {
t.Setenv(k, v)
}
args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
r, err := runTestApp(app, args...)
assert.NoError(t, err, c.cmd)
assert.NotEmpty(t, c.exp, c.cmd)
if !assert.Contains(t, r.Stdout, c.exp, c.cmd) {
t.Log("Full output:\n" + r.Stdout)
t.Log("Expected:\n" + c.exp)
}
})
}
}
func TestCliCmdError(t *testing.T) {
app := newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") }})
r, err := runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
assert.Empty(t, r.Stdout)
assert.Equal(t, "Command error: normal error\n", r.Stderr)
app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) }})
r, err = runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err)
assert.Equal(t, 2, r.ExitCode)
assert.Empty(t, r.Stdout)
assert.Equal(t, "exit error\n", r.Stderr)
app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }})
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
assert.Empty(t, r.Stdout)
assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr)
app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }})
r, err = runTestApp(app, "./gitea", "test-cmd")
assert.NoError(t, err)
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called
assert.Empty(t, r.Stdout)
assert.Empty(t, r.Stderr)
}
func TestCliCmdBefore(t *testing.T) {
ctxNew := context.WithValue(context.Background(), any("key"), "value")
configValues := map[string]string{}
setting.CustomConf = "/tmp/any.ini"
var actionCtx context.Context
app := newTestApp(cli.Command{
Before: func(context.Context, *cli.Command) (context.Context, error) {
configValues["before"] = setting.CustomConf
return ctxNew, nil
},
Action: func(ctx context.Context, cmd *cli.Command) error {
configValues["action"] = setting.CustomConf
actionCtx = ctx
return nil
},
})
_, err := runTestApp(app, "./gitea", "--config", "/dev/null", "test-cmd")
assert.NoError(t, err)
assert.Equal(t, ctxNew, actionCtx)
assert.Equal(t, "/tmp/any.ini", configValues["before"], "BeforeFunc must be called before preparing config")
assert.Equal(t, "/dev/null", configValues["action"])
}

View File

@@ -48,7 +48,7 @@ DEFAULT CONFIGURATION:
} }
} }
func prepareSubcommandWithGlobalFlags(originCmd *cli.Command) { func PrepareSubcommandWithGlobalFlags(originCmd *cli.Command) {
originBefore := originCmd.Before originBefore := originCmd.Before
originCmd.Before = func(ctxOrig context.Context, cmd *cli.Command) (ctx context.Context, err error) { originCmd.Before = func(ctxOrig context.Context, cmd *cli.Command) (ctx context.Context, err error) {
ctx = ctxOrig ctx = ctxOrig
@@ -145,7 +145,7 @@ func NewMainApp(appVer AppVersion) *cli.Command {
app.Before = PrepareConsoleLoggerLevel(log.INFO) app.Before = PrepareConsoleLoggerLevel(log.INFO)
for i := range subCmdWithConfig { for i := range subCmdWithConfig {
prepareSubcommandWithGlobalFlags(subCmdWithConfig[i]) PrepareSubcommandWithGlobalFlags(subCmdWithConfig[i])
} }
app.Commands = append(app.Commands, subCmdWithConfig...) app.Commands = append(app.Commands, subCmdWithConfig...)
app.Commands = append(app.Commands, subCmdStandalone...) app.Commands = append(app.Commands, subCmdStandalone...)

View File

@@ -5,17 +5,9 @@ package cmd
import ( import (
"context" "context"
"errors"
"fmt"
"io"
"path/filepath"
"strings"
"testing" "testing"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
@@ -25,209 +17,28 @@ func TestMain(m *testing.M) {
unittest.MainTest(m) unittest.MainTest(m)
} }
func makePathOutput(workPath, customPath, customConf string) string { func TestDefaultCommand(t *testing.T) {
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf) test := func(t *testing.T, args []string, expectedRetName string, expectedRetValid bool) {
} called := false
cmd := &cli.Command{
func newTestApp(testCmd cli.Command) *cli.Command { DefaultCommand: "test",
app := NewMainApp(AppVersion{}) Commands: []*cli.Command{
testCmd.Name = util.IfZero(testCmd.Name, "test-cmd") {
prepareSubcommandWithGlobalFlags(&testCmd) Name: "test",
app.Commands = append(app.Commands, &testCmd) Action: func(ctx context.Context, command *cli.Command) error {
app.DefaultCommand = testCmd.Name retName, retValid := isValidDefaultSubCommand(command)
return app assert.Equal(t, expectedRetName, retName)
} assert.Equal(t, expectedRetValid, retValid)
called = true
type runResult struct { return nil
Stdout string },
Stderr string
ExitCode int
}
func runTestApp(app *cli.Command, args ...string) (runResult, error) {
outBuf := new(strings.Builder)
errBuf := new(strings.Builder)
app.Writer = outBuf
app.ErrWriter = errBuf
exitCode := -1
defer test.MockVariableValue(&cli.ErrWriter, app.ErrWriter)()
defer test.MockVariableValue(&cli.OsExiter, func(code int) {
if exitCode == -1 {
exitCode = code // save the exit code once and then reset the writer (to simulate the exit)
app.Writer, app.ErrWriter, cli.ErrWriter = io.Discard, io.Discard, io.Discard
}
})()
err := RunMainApp(app, args...)
return runResult{outBuf.String(), errBuf.String(), exitCode}, err
}
func TestCliCmd(t *testing.T) {
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)"
cli.SubcommandHelpTemplate = "(subcommand help template)"
cases := []struct {
env map[string]string
cmd string
exp string
}{
// help commands
{
cmd: "./gitea -h",
exp: "DEFAULT CONFIGURATION:",
},
{
cmd: "./gitea help",
exp: "DEFAULT CONFIGURATION:",
},
{
cmd: "./gitea -c /dev/null -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea help -c /dev/null",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null test-cmd -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -c /dev/null -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -h -c /dev/null",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null test-cmd help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -c /dev/null help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd help -c /dev/null",
exp: "ConfigFile: /dev/null",
},
// parse paths
{
cmd: "./gitea test-cmd",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, defaultCustomConf),
},
{
cmd: "./gitea -c /tmp/app.ini test-cmd",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
},
{
cmd: "./gitea test-cmd -c /tmp/app.ini",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd",
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/custom/conf/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd --work-path /tmp/other",
exp: makePathOutput("/tmp/other", "/tmp/other/custom", "/tmp/other/custom/conf/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd --config /tmp/app-other.ini",
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"),
},
}
for _, c := range cases {
t.Run(c.cmd, func(t *testing.T) {
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))
return nil
}, },
}) },
for k, v := range c.env { }
t.Setenv(k, v) assert.NoError(t, cmd.Run(t.Context(), args))
} assert.True(t, called)
args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
r, err := runTestApp(app, args...)
assert.NoError(t, err, c.cmd)
assert.NotEmpty(t, c.exp, c.cmd)
if !assert.Contains(t, r.Stdout, c.exp, c.cmd) {
t.Log("Full output:\n" + r.Stdout)
t.Log("Expected:\n" + c.exp)
}
})
} }
} test(t, []string{"./gitea"}, "", true)
test(t, []string{"./gitea", "test"}, "", true)
func TestCliCmdError(t *testing.T) { test(t, []string{"./gitea", "other"}, "other", false)
app := newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") }})
r, err := runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
assert.Empty(t, r.Stdout)
assert.Equal(t, "Command error: normal error\n", r.Stderr)
app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) }})
r, err = runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err)
assert.Equal(t, 2, r.ExitCode)
assert.Empty(t, r.Stdout)
assert.Equal(t, "exit error\n", r.Stderr)
app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }})
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
assert.Empty(t, r.Stdout)
assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr)
app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }})
r, err = runTestApp(app, "./gitea", "test-cmd")
assert.NoError(t, err)
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called
assert.Empty(t, r.Stdout)
assert.Empty(t, r.Stderr)
}
func TestCliCmdBefore(t *testing.T) {
ctxNew := context.WithValue(context.Background(), any("key"), "value")
configValues := map[string]string{}
setting.CustomConf = "/tmp/any.ini"
var actionCtx context.Context
app := newTestApp(cli.Command{
Before: func(context.Context, *cli.Command) (context.Context, error) {
configValues["before"] = setting.CustomConf
return ctxNew, nil
},
Action: func(ctx context.Context, cmd *cli.Command) error {
configValues["action"] = setting.CustomConf
actionCtx = ctx
return nil
},
})
_, err := runTestApp(app, "./gitea", "--config", "/dev/null", "test-cmd")
assert.NoError(t, err)
assert.Equal(t, ctxNew, actionCtx)
assert.Equal(t, "/tmp/any.ini", configValues["before"], "BeforeFunc must be called before preparing config")
assert.Equal(t, "/dev/null", configValues["action"])
} }

View File

@@ -1,39 +1,2 @@
- []
id: 1
hook_id: 1
uuid: uuid1
is_delivered: true
is_succeed: false
request_content: >
{
"url": "/matrix-delivered",
"http_method":"PUT",
"headers": {
"X-Head": "42"
},
"body": "{}"
}
-
id: 2
hook_id: 1
uuid: uuid2
is_delivered: true
-
id: 3
hook_id: 1
uuid: uuid3
is_delivered: true
is_succeed: true
payload_content: '{"key":"value"}' # legacy task, payload saved in payload_content (and not in request_content)
request_content: >
{
"url": "/matrix-success",
"http_method":"PUT",
"headers": {
"X-Head": "42"
}
}
# DO NOT add more test data in the fixtures, test case should prepare their own test data separately and clearly # DO NOT add more test data in the fixtures, test case should prepare their own test data separately and clearly

View File

@@ -1,54 +1,2 @@
- []
id: 1
repo_id: 1
url: https://www.example.com/url1
content_type: 1 # json
events: '{"push_only":true,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":false}}'
is_active: true
-
id: 2
repo_id: 1
url: https://www.example.com/url2
content_type: 1 # json
events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}'
is_active: false
-
id: 3
owner_id: 3
repo_id: 3
url: https://www.example.com/url3
content_type: 1 # json
events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}'
is_active: true
-
id: 4
repo_id: 2
url: https://www.example.com/url4
content_type: 1 # json
events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
is_active: true
-
id: 5
repo_id: 0
owner_id: 0
url: https://www.example.com/url5
content_type: 1 # json
events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
is_active: true
is_system_webhook: true
-
id: 6
repo_id: 0
owner_id: 0
url: https://www.example.com/url6
content_type: 1 # json
events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
is_active: true
is_system_webhook: false
# DO NOT add more test data in the fixtures, test case should prepare their own test data separately and clearly # DO NOT add more test data in the fixtures, test case should prepare their own test data separately and clearly

View File

@@ -75,7 +75,7 @@ func deleteDB() error {
} }
db.Close() db.Close()
// Check if we need to setup a specific schema // Check if we need to set up a specific schema
if len(setting.Database.Schema) != 0 { if len(setting.Database.Schema) != 0 {
db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", 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)) setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
@@ -91,7 +91,7 @@ func deleteDB() error {
defer schrows.Close() defer schrows.Close()
if !schrows.Next() { if !schrows.Next() {
// Create and setup a DB schema // Create and set up a DB schema
_, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema) _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema)
if err != nil { if err != nil {
return err return err
@@ -134,7 +134,8 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
ourSkip := 2 ourSkip := 2
ourSkip += skip ourSkip += skip
deferFn := testlogger.PrintCurrentTest(t, ourSkip) deferFn := testlogger.PrintCurrentTest(t, ourSkip)
require.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath)) giteaRoot := setting.GetGiteaTestSourceRoot()
require.NoError(t, unittest.SyncDirs(filepath.Join(giteaRoot, "tests/gitea-repositories-meta"), setting.RepoRootPath))
if err := deleteDB(); err != nil { if err := deleteDB(); err != nil {
t.Fatalf("unable to reset database: %v", err) t.Fatalf("unable to reset database: %v", err)
@@ -166,7 +167,7 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
} }
} }
fixturesDir := filepath.Join(filepath.Dir(setting.AppPath), "models", "migrations", "fixtures", t.Name()) fixturesDir := filepath.Join(giteaRoot, "models", "migrations", "fixtures", t.Name())
if _, err := os.Stat(fixturesDir); err == nil { if _, err := os.Stat(fixturesDir); err == nil {
t.Logf("initializing fixtures from: %s", fixturesDir) t.Logf("initializing fixtures from: %s", fixturesDir)
@@ -202,17 +203,18 @@ 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") tempWorkPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("migration-test-data-")
if err != nil { if err != nil {
testlogger.Panicf("Unable to create temporary data path %v\n", err) return testlogger.MainErrorf("Unable to create temporary dir for migration test: %v", err)
} }
defer cleanup() defer cleanup()
setting.AppDataPath = tmpDataPath
setting.MockBuiltinPaths(tempWorkPath, "", "")
setting.SetupGiteaTestEnv()
if err = git.InitFull(); err != nil { if err = git.InitFull(); err != nil {
testlogger.Panicf("Unable to InitFull: %v\n", err) return testlogger.MainErrorf("Unable to InitFull: %v", err)
} }
setting.LoadDBSetting() setting.LoadDBSetting()
setting.InitLoggersForTest() setting.InitLoggersForTest()

View File

@@ -42,12 +42,20 @@ 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{})
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() 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 {
_, _ = fmt.Fprintf(os.Stderr, "Error creating test database engine: %v\n", err) return testlogger.MainErrorf("Error creating test database engine: %v", err)
os.Exit(1)
} }
setting.AppURL = "https://try.gitea.io/" setting.AppURL = "https://try.gitea.io/"
@@ -59,59 +67,28 @@ func mainTest(m *testing.M, testOptsArg ...*TestOptions) int {
setting.SSH.Domain = "try.gitea.io" setting.SSH.Domain = "try.gitea.io"
setting.Database.Type = "sqlite3" setting.Database.Type = "sqlite3"
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"
repoRootPath, cleanup1, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("repos")
if err != nil {
testlogger.Panicf("TempDir: %v\n", err)
}
defer cleanup1()
setting.RepoRootPath = repoRootPath
appDataPath, cleanup2, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("appdata")
if err != nil {
testlogger.Panicf("TempDir: %v\n", err)
}
defer cleanup2()
setting.AppDataPath = appDataPath
setting.GravatarSource = "https://secure.gravatar.com/avatar/" setting.GravatarSource = "https://secure.gravatar.com/avatar/"
setting.Attachment.Storage.Path = filepath.Join(setting.AppDataPath, "attachments")
setting.LFS.Storage.Path = filepath.Join(setting.AppDataPath, "lfs")
setting.Avatar.Storage.Path = filepath.Join(setting.AppDataPath, "avatars")
setting.RepoAvatar.Storage.Path = filepath.Join(setting.AppDataPath, "repo-avatars")
setting.RepoArchive.Storage.Path = filepath.Join(setting.AppDataPath, "repo-archive")
setting.Packages.Storage.Path = filepath.Join(setting.AppDataPath, "packages")
setting.Actions.LogStorage.Path = filepath.Join(setting.AppDataPath, "actions_log")
setting.Git.HomePath = filepath.Join(setting.AppDataPath, "home")
setting.IncomingEmail.ReplyToAddress = "incoming+%{token}@localhost" setting.IncomingEmail.ReplyToAddress = "incoming+%{token}@localhost"
config.SetDynGetter(system.NewDatabaseDynKeyGetter()) config.SetDynGetter(system.NewDatabaseDynKeyGetter())
if err = cache.Init(); err != nil { if err = cache.Init(); err != nil {
testlogger.Panicf("cache.Init: %v\n", err) return testlogger.MainErrorf("cache.Init: %v", err)
} }
if err = storage.Init(); err != nil { if err = storage.Init(); err != nil {
testlogger.Panicf("storage.Init: %v\n", err) return testlogger.MainErrorf("storage.Init: %v", err)
} }
if err = SyncDirs(filepath.Join(giteaRoot, "tests", "gitea-repositories-meta"), setting.RepoRootPath); err != nil { if err = SyncDirs(filepath.Join(giteaRoot, "tests", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
testlogger.Panicf("util.SyncDirs: %v\n", err) return testlogger.MainErrorf("util.SyncDirs: %v", err)
} }
if err = git.InitFull(); err != nil { if err = git.InitFull(); err != nil {
testlogger.Panicf("git.Init: %v\n", err) return testlogger.MainErrorf("git.Init: %v", err)
} }
if testOpts.SetUp != nil { if testOpts.SetUp != nil {
if err := testOpts.SetUp(); err != nil { if err := testOpts.SetUp(); err != nil {
testlogger.Panicf("set up failed: %v\n", err) return testlogger.MainErrorf("set up failed: %v", err)
} }
} }
@@ -119,7 +96,7 @@ func mainTest(m *testing.M, testOptsArg ...*TestOptions) int {
if testOpts.TearDown != nil { if testOpts.TearDown != nil {
if err := testOpts.TearDown(); err != nil { if err := testOpts.TearDown(); err != nil {
testlogger.Panicf("tear down failed: %v\n", err) return testlogger.MainErrorf("tear down failed: %v", err)
} }
} }
return exitStatus return exitStatus

View File

@@ -15,5 +15,6 @@ func TestMain(m *testing.M) {
"webhook.yml", "webhook.yml",
"hook_task.yml", "hook_task.yml",
}, },
SetUp: prepareWebhookTestData,
}) })
} }

View File

@@ -10,28 +10,28 @@ import (
"code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/optional"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestListSystemWebhookOptions(t *testing.T) { func TestListSystemWebhookOptions(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hookSystem := unittest.AssertExistsAndLoadBean(t, &Webhook{URL: "https://www.example.com/system"})
hookDefault := unittest.AssertExistsAndLoadBean(t, &Webhook{URL: "https://www.example.com/default"})
opts := ListSystemWebhookOptions{IsSystem: optional.None[bool]()} opts := ListSystemWebhookOptions{IsSystem: optional.None[bool]()}
hooks, _, err := GetGlobalWebhooks(t.Context(), &opts) hooks, _, err := GetGlobalWebhooks(t.Context(), &opts)
assert.NoError(t, err) require.NoError(t, err)
if assert.Len(t, hooks, 2) { require.Len(t, hooks, 2)
assert.Equal(t, int64(5), hooks[0].ID) assert.Equal(t, hookSystem.ID, hooks[0].ID)
assert.Equal(t, int64(6), hooks[1].ID) assert.Equal(t, hookDefault.ID, hooks[1].ID)
}
opts.IsSystem = optional.Some(true) opts.IsSystem = optional.Some(true)
hooks, _, err = GetGlobalWebhooks(t.Context(), &opts) hooks, _, err = GetGlobalWebhooks(t.Context(), &opts)
assert.NoError(t, err) require.NoError(t, err)
if assert.Len(t, hooks, 1) { require.Len(t, hooks, 1)
assert.Equal(t, int64(5), hooks[0].ID) assert.Equal(t, hookSystem.ID, hooks[0].ID)
}
opts.IsSystem = optional.Some(false) opts.IsSystem = optional.Some(false)
hooks, _, err = GetGlobalWebhooks(t.Context(), &opts) hooks, _, err = GetGlobalWebhooks(t.Context(), &opts)
assert.NoError(t, err) require.NoError(t, err)
if assert.Len(t, hooks, 1) { require.Len(t, hooks, 1)
assert.Equal(t, int64(6), hooks[0].ID) assert.Equal(t, hookDefault.ID, hooks[0].ID)
}
} }

View File

@@ -4,6 +4,7 @@
package webhook package webhook
import ( import (
"context"
"testing" "testing"
"time" "time"
@@ -14,40 +15,105 @@ import (
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
webhook_module "code.gitea.io/gitea/modules/webhook" webhook_module "code.gitea.io/gitea/modules/webhook"
"github.com/google/uuid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"xorm.io/builder"
) )
func TestHookContentType_Name(t *testing.T) { func prepareWebhookTestData() error {
assert.Equal(t, "json", ContentTypeJSON.Name()) if err := unittest.PrepareTestDatabase(); err != nil {
assert.Equal(t, "form", ContentTypeForm.Name()) return err
}
var hooks []*Webhook
hooks = append(hooks, &Webhook{
RepoID: 1,
URL: "https://www.example.com/url1",
ContentType: ContentTypeJSON,
Events: `{"push_only":true,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":false}}`,
IsActive: true,
})
hooks = append(hooks, &Webhook{
RepoID: 1,
URL: "https://www.example.com/url2",
ContentType: ContentTypeJSON,
Events: `{}`,
IsActive: false,
})
hooks = append(hooks, &Webhook{
OwnerID: 3,
RepoID: 3,
URL: "https://www.example.com/url3",
ContentType: ContentTypeJSON,
Events: `{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}`,
IsActive: true,
})
hooks = append(hooks, &Webhook{
OwnerID: 3,
RepoID: 3,
URL: "https://www.example.com/url3",
ContentType: ContentTypeJSON,
Events: `{}`,
})
hooks = append(hooks, &Webhook{
RepoID: 2,
URL: "https://www.example.com/url4",
ContentType: ContentTypeJSON,
Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`,
IsActive: true,
})
hooks = append(hooks, &Webhook{
URL: "https://www.example.com/system",
ContentType: ContentTypeJSON,
Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`,
IsSystemWebhook: true,
})
hooks = append(hooks, &Webhook{
URL: "https://www.example.com/default",
ContentType: ContentTypeJSON,
Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`,
})
ctx := context.Background()
if err := db.TruncateBeans(ctx, &Webhook{}); err != nil {
return err
}
if err := db.Insert(ctx, hooks); err != nil {
return err
}
hook, _, _ := db.Get[Webhook](ctx, builder.Eq{"repo_id": 1, "is_active": true})
var tasks []*HookTask
tasks = append(tasks, &HookTask{HookID: hook.ID, UUID: uuid.New().String()})
tasks = append(tasks, &HookTask{HookID: hook.ID, UUID: uuid.New().String()})
tasks = append(tasks, &HookTask{HookID: hook.ID, UUID: uuid.New().String()})
if err := db.TruncateBeans(ctx, &HookTask{}); err != nil {
return err
}
return db.Insert(ctx, tasks)
} }
func TestIsValidHookContentType(t *testing.T) { func TestWebHookContentType(t *testing.T) {
assert.Equal(t, "json", ContentTypeJSON.Name())
assert.Equal(t, "form", ContentTypeForm.Name())
assert.True(t, IsValidHookContentType("json")) assert.True(t, IsValidHookContentType("json"))
assert.True(t, IsValidHookContentType("form")) assert.True(t, IsValidHookContentType("form"))
assert.False(t, IsValidHookContentType("invalid")) assert.False(t, IsValidHookContentType("invalid"))
} }
func TestWebhook_History(t *testing.T) { func TestWebhook_History(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, IsActive: true})
webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 1}) tasks, err := hook.History(t.Context(), 0)
tasks, err := webhook.History(t.Context(), 0) require.NoError(t, err)
assert.NoError(t, err) require.Len(t, tasks, 3)
if assert.Len(t, tasks, 3) {
assert.Equal(t, int64(3), tasks[0].ID)
assert.Equal(t, int64(2), tasks[1].ID)
assert.Equal(t, int64(1), tasks[2].ID)
}
webhook = unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2}) hook = unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, Events: "{}"})
tasks, err = webhook.History(t.Context(), 0) tasks, err = hook.History(t.Context(), 0)
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, tasks) assert.Empty(t, tasks)
} }
func TestWebhook_UpdateEvent(t *testing.T) { func TestWebhook_UpdateEvent(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, IsActive: true})
webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 1})
hookEvent := &webhook_module.HookEvent{ hookEvent := &webhook_module.HookEvent{
PushOnly: true, PushOnly: true,
SendEverything: false, SendEverything: false,
@@ -100,10 +166,10 @@ func TestCreateWebhook(t *testing.T) {
} }
func TestGetWebhookByRepoID(t *testing.T) { func TestGetWebhookByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, IsActive: true})
hook, err := GetWebhookByRepoID(t.Context(), 1, 1) loaded, err := GetWebhookByRepoID(t.Context(), 1, hook.ID)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, int64(1), hook.ID) assert.Equal(t, hook.ID, loaded.ID)
_, err = GetWebhookByRepoID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) _, err = GetWebhookByRepoID(t.Context(), unittest.NonexistentID, unittest.NonexistentID)
assert.Error(t, err) assert.Error(t, err)
@@ -111,10 +177,10 @@ func TestGetWebhookByRepoID(t *testing.T) {
} }
func TestGetWebhookByOwnerID(t *testing.T) { func TestGetWebhookByOwnerID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := unittest.AssertExistsAndLoadBean(t, &Webhook{OwnerID: 3})
hook, err := GetWebhookByOwnerID(t.Context(), 3, 3) loaded, err := GetWebhookByOwnerID(t.Context(), 3, hook.ID)
assert.NoError(t, err) require.NoError(t, err)
assert.Equal(t, int64(3), hook.ID) require.Equal(t, hook.ID, loaded.ID)
_, err = GetWebhookByOwnerID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) _, err = GetWebhookByOwnerID(t.Context(), unittest.NonexistentID, unittest.NonexistentID)
assert.Error(t, err) assert.Error(t, err)
@@ -122,48 +188,45 @@ func TestGetWebhookByOwnerID(t *testing.T) {
} }
func TestGetActiveWebhooksByRepoID(t *testing.T) { func TestGetActiveWebhooksByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, IsActive: true})
hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{RepoID: 1, IsActive: optional.Some(true)}) hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{RepoID: 1, IsActive: optional.Some(true)})
assert.NoError(t, err) require.NoError(t, err)
if assert.Len(t, hooks, 1) { require.Len(t, hooks, 1)
assert.Equal(t, int64(1), hooks[0].ID) assert.Equal(t, hook.ID, hooks[0].ID)
assert.True(t, hooks[0].IsActive) assert.True(t, hooks[0].IsActive)
}
} }
func TestGetWebhooksByRepoID(t *testing.T) { func TestGetWebhooksByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{RepoID: 1}) hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{RepoID: 1})
assert.NoError(t, err) require.NoError(t, err)
if assert.Len(t, hooks, 2) { require.Len(t, hooks, 2)
assert.Equal(t, int64(1), hooks[0].ID) assert.Equal(t, int64(1), hooks[0].RepoID)
assert.Equal(t, int64(2), hooks[1].ID) assert.True(t, hooks[0].IsActive)
} assert.Equal(t, int64(1), hooks[1].RepoID)
assert.False(t, hooks[1].IsActive)
} }
func TestGetActiveWebhooksByOwnerID(t *testing.T) { func TestGetActiveWebhooksByOwnerID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{OwnerID: 3, IsActive: optional.Some(true)}) hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{OwnerID: 3, IsActive: optional.Some(true)})
assert.NoError(t, err) require.NoError(t, err)
if assert.Len(t, hooks, 1) { require.Len(t, hooks, 1)
assert.Equal(t, int64(3), hooks[0].ID) assert.Equal(t, int64(3), hooks[0].OwnerID)
assert.True(t, hooks[0].IsActive) assert.True(t, hooks[0].IsActive)
}
} }
func TestGetWebhooksByOwnerID(t *testing.T) { func TestGetWebhooksByOwnerID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{OwnerID: 3}) hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{OwnerID: 3})
assert.NoError(t, err) require.NoError(t, err)
if assert.Len(t, hooks, 1) { require.Len(t, hooks, 2)
assert.Equal(t, int64(3), hooks[0].ID) assert.Equal(t, int64(3), hooks[0].OwnerID)
assert.True(t, hooks[0].IsActive) assert.True(t, hooks[0].IsActive)
} assert.Equal(t, int64(3), hooks[1].OwnerID)
assert.False(t, hooks[1].IsActive)
} }
func TestUpdateWebhook(t *testing.T) { func TestUpdateWebhook(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, Events: `{}`})
hook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2}) require.False(t, hook.IsActive)
hook.IsActive = true hook.IsActive = true
hook.ContentType = ContentTypeForm hook.ContentType = ContentTypeForm
unittest.AssertNotExistsBean(t, hook) unittest.AssertNotExistsBean(t, hook)
@@ -172,48 +235,36 @@ func TestUpdateWebhook(t *testing.T) {
} }
func TestDeleteWebhookByRepoID(t *testing.T) { func TestDeleteWebhookByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, Events: `{}`})
unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2, RepoID: 1}) assert.NoError(t, DeleteWebhookByRepoID(t.Context(), 1, hook.ID))
assert.NoError(t, DeleteWebhookByRepoID(t.Context(), 1, 2)) unittest.AssertNotExistsBean(t, &Webhook{ID: hook.ID, RepoID: 1})
unittest.AssertNotExistsBean(t, &Webhook{ID: 2, RepoID: 1})
err := DeleteWebhookByRepoID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) err := DeleteWebhookByRepoID(t.Context(), unittest.NonexistentID, unittest.NonexistentID)
assert.Error(t, err)
assert.True(t, IsErrWebhookNotExist(err)) assert.True(t, IsErrWebhookNotExist(err))
} }
func TestDeleteWebhookByOwnerID(t *testing.T) { func TestDeleteWebhookByOwnerID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := unittest.AssertExistsAndLoadBean(t, &Webhook{OwnerID: 3, Events: `{}`})
unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 3, OwnerID: 3}) assert.NoError(t, DeleteWebhookByOwnerID(t.Context(), 3, hook.ID))
assert.NoError(t, DeleteWebhookByOwnerID(t.Context(), 3, 3)) unittest.AssertNotExistsBean(t, &Webhook{ID: hook.ID, OwnerID: 3})
unittest.AssertNotExistsBean(t, &Webhook{ID: 3, OwnerID: 3})
err := DeleteWebhookByOwnerID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) err := DeleteWebhookByOwnerID(t.Context(), unittest.NonexistentID, unittest.NonexistentID)
assert.Error(t, err)
assert.True(t, IsErrWebhookNotExist(err)) assert.True(t, IsErrWebhookNotExist(err))
} }
func TestHookTasks(t *testing.T) { func TestHookTasks(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, IsActive: true})
hookTasks, err := HookTasks(t.Context(), 1, 1) hookTasks, err := HookTasks(t.Context(), hook.ID, 1)
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, hookTasks, 3) { assert.Len(t, hookTasks, 3)
assert.Equal(t, int64(3), hookTasks[0].ID)
assert.Equal(t, int64(2), hookTasks[1].ID)
assert.Equal(t, int64(1), hookTasks[2].ID)
}
hookTasks, err = HookTasks(t.Context(), unittest.NonexistentID, 1) hookTasks, err = HookTasks(t.Context(), unittest.NonexistentID, 1)
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, hookTasks) assert.Empty(t, hookTasks)
} }
func TestCreateHookTask(t *testing.T) { func TestCreateHookTask(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := unittest.AssertExistsAndLoadBean(t, &Webhook{OwnerID: 3, IsActive: true})
hookTask := &HookTask{ hookTask := &HookTask{HookID: hook.ID, PayloadVersion: 2}
HookID: 3,
PayloadVersion: 2,
}
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
_, err := CreateHookTask(t.Context(), hookTask) _, err := CreateHookTask(t.Context(), hookTask)
assert.NoError(t, err) assert.NoError(t, err)
@@ -221,20 +272,23 @@ func TestCreateHookTask(t *testing.T) {
} }
func TestUpdateHookTask(t *testing.T) { func TestUpdateHookTask(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := unittest.AssertExistsAndLoadBean(t, &Webhook{OwnerID: 3, IsActive: true})
hookTask := &HookTask{HookID: hook.ID, PayloadVersion: 2}
_, err := CreateHookTask(t.Context(), hookTask)
assert.NoError(t, err)
hook := unittest.AssertExistsAndLoadBean(t, &HookTask{ID: 1}) hookTask.PayloadContent = "new payload content"
hook.PayloadContent = "new payload content" hookTask.IsDelivered = true
hook.IsDelivered = true unittest.AssertNotExistsBean(t, hookTask)
unittest.AssertNotExistsBean(t, hook) assert.NoError(t, UpdateHookTask(t.Context(), hookTask))
assert.NoError(t, UpdateHookTask(t.Context(), hook)) unittest.AssertExistsAndLoadBean(t, hookTask)
unittest.AssertExistsAndLoadBean(t, hook)
} }
func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup1", ContentType: ContentTypeJSON, Events: `{"push_only":true}`}
require.NoError(t, db.Insert(t.Context(), hook))
hookTask := &HookTask{ hookTask := &HookTask{
HookID: 3, HookID: hook.ID,
IsDelivered: true, IsDelivered: true,
Delivered: timeutil.TimeStampNanoNow(), Delivered: timeutil.TimeStampNanoNow(),
PayloadVersion: 2, PayloadVersion: 2,
@@ -249,9 +303,10 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) {
} }
func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup2", ContentType: ContentTypeJSON, Events: `{"push_only":true}`}
require.NoError(t, db.Insert(t.Context(), hook))
hookTask := &HookTask{ hookTask := &HookTask{
HookID: 4, HookID: hook.ID,
IsDelivered: false, IsDelivered: false,
PayloadVersion: 2, PayloadVersion: 2,
} }
@@ -265,9 +320,10 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) {
} }
func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup3", ContentType: ContentTypeJSON, Events: `{"push_only":true}`}
require.NoError(t, db.Insert(t.Context(), hook))
hookTask := &HookTask{ hookTask := &HookTask{
HookID: 4, HookID: hook.ID,
IsDelivered: true, IsDelivered: true,
Delivered: timeutil.TimeStampNanoNow(), Delivered: timeutil.TimeStampNanoNow(),
PayloadVersion: 2, PayloadVersion: 2,
@@ -282,9 +338,10 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) {
} }
func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup4", ContentType: ContentTypeJSON, Events: `{"push_only":true}`}
require.NoError(t, db.Insert(t.Context(), hook))
hookTask := &HookTask{ hookTask := &HookTask{
HookID: 3, HookID: hook.ID,
IsDelivered: true, IsDelivered: true,
Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -8).UnixNano()), Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -8).UnixNano()),
PayloadVersion: 2, PayloadVersion: 2,
@@ -299,9 +356,10 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) {
} }
func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup5", ContentType: ContentTypeJSON, Events: `{"push_only":true}`}
require.NoError(t, db.Insert(t.Context(), hook))
hookTask := &HookTask{ hookTask := &HookTask{
HookID: 4, HookID: hook.ID,
IsDelivered: false, IsDelivered: false,
PayloadVersion: 2, PayloadVersion: 2,
} }
@@ -315,9 +373,10 @@ func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) {
} }
func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup6", ContentType: ContentTypeJSON, Events: `{"push_only":true}`}
require.NoError(t, db.Insert(t.Context(), hook))
hookTask := &HookTask{ hookTask := &HookTask{
HookID: 4, HookID: hook.ID,
IsDelivered: true, IsDelivered: true,
Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -6).UnixNano()), Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -6).UnixNano()),
PayloadVersion: 2, PayloadVersion: 2,

View File

@@ -192,13 +192,13 @@ func RunGitTests(m interface{ Run() int }) {
func runGitTests(m interface{ Run() int }) int { func runGitTests(m interface{ Run() int }) int {
gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home") gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home")
if err != nil { if err != nil {
testlogger.Panicf("unable to create temp dir: %s", err.Error()) return testlogger.MainErrorf("unable to create temp dir: %v", err)
} }
defer cleanup() defer cleanup()
setting.Git.HomePath = gitHomePath setting.Git.HomePath = gitHomePath
if err = InitFull(); err != nil { if err = InitFull(); err != nil {
testlogger.Panicf("failed to call Init: %s", err.Error()) return testlogger.MainErrorf("failed to call Init: %v", err)
} }
return m.Run() return m.Run()
} }

View File

@@ -24,7 +24,7 @@ func testMain(m *testing.M) int {
// "setting.Git.HomePath" is initialized in "git" package but really used in "gitcmd" package // "setting.Git.HomePath" is initialized in "git" package but really used in "gitcmd" package
gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home") gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home")
if err != nil { if err != nil {
testlogger.Panicf("failed to create temp dir: %v", err) return testlogger.MainErrorf("failed to create temp dir: %v", err)
} }
defer cleanup() defer cleanup()

View File

@@ -13,11 +13,7 @@ import (
) )
func TestManager(t *testing.T) { func TestManager(t *testing.T) {
oldAppDataPath := setting.AppDataPath
setting.AppDataPath = t.TempDir() setting.AppDataPath = t.TempDir()
defer func() {
setting.AppDataPath = oldAppDataPath
}()
newQueueFromConfig := func(name, cfg string) (*WorkerPoolQueue[int], error) { newQueueFromConfig := func(name, cfg string) (*WorkerPoolQueue[int], error) {
cfgProvider, err := setting.NewConfigProviderFromData(cfg) cfgProvider, err := setting.NewConfigProviderFromData(cfg)

View File

@@ -15,7 +15,7 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
) )
var giteaTestSourceRoot *string var giteaTestSourceRoot *string // intentionally use a pointer to make sure the uninitialized access panics
func GetGiteaTestSourceRoot() string { func GetGiteaTestSourceRoot() string {
return *giteaTestSourceRoot return *giteaTestSourceRoot
@@ -30,7 +30,9 @@ func SetupGiteaTestEnv() {
log.OsExiter = func(code int) { log.OsExiter = func(code int) {
if code != 0 { if code != 0 {
// non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens, show a full stacktrace for more details // Non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens:
// * Show a full stacktrace for more details.
// * If the "log.Fatal" is abused in tests, should fix.
panic(fmt.Errorf("non-zero exit code during testing: %d", code)) panic(fmt.Errorf("non-zero exit code during testing: %d", code))
} }
os.Exit(0) os.Exit(0)
@@ -49,12 +51,14 @@ func SetupGiteaTestEnv() {
giteaTestSourceRoot = &giteaRoot giteaTestSourceRoot = &giteaRoot
return giteaRoot return giteaRoot
} }
giteaRoot := initGiteaRoot()
initGiteaPaths := func() { initGiteaPaths := func() {
appWorkPathBuiltin = *giteaTestSourceRoot // need to load assets (options, public) from the source code directory for testing
AppWorkPath = appWorkPathBuiltin StaticRootPath = giteaRoot
AppPath = filepath.Join(AppWorkPath, "gitea") + util.Iif(IsWindows, ".exe", "") // during testing, the AppPath must point to the pre-built Gitea binary in the source root
StaticRootPath = AppWorkPath // need to load assets (options, public) from the source code directory for testing // it needs to be called by git hooks
AppPath = filepath.Join(giteaRoot, "gitea") + util.Iif(IsWindows, ".exe", "")
} }
initGiteaConf := func() string { initGiteaConf := func() string {
@@ -62,13 +66,15 @@ func SetupGiteaTestEnv() {
giteaConf := os.Getenv("GITEA_TEST_CONF") giteaConf := os.Getenv("GITEA_TEST_CONF")
if giteaConf == "" { if giteaConf == "" {
// if no GITEA_TEST_CONF, then it is in unit test, use a temp (non-existing / empty) config file // if no GITEA_TEST_CONF, then it is in unit test, use a temp (non-existing / empty) config file
// do not really use such config file, the test can run concurrently, using the same config file will cause data-race between tests
giteaConf = "custom/conf/app-test-tmp.ini" giteaConf = "custom/conf/app-test-tmp.ini"
customConfBuiltin = filepath.Join(AppWorkPath, giteaConf) customConfBuiltin = filepath.Join(AppWorkPath, giteaConf)
CustomConf = customConfBuiltin CustomConf = customConfBuiltin
_ = os.Remove(CustomConf) _ = os.Remove(CustomConf)
} else { } else {
// CustomConf must be absolute path to make tests pass, // CustomConf must be absolute path to make tests pass.
CustomConf = filepath.Join(AppWorkPath, giteaConf) // At the moment, GITEA_TEST_CONF is always in Gitea's source root
CustomConf = filepath.Join(giteaRoot, giteaConf)
} }
return giteaConf return giteaConf
} }
@@ -98,12 +104,15 @@ func SetupGiteaTestEnv() {
PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy") PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
} }
giteaRoot := initGiteaRoot()
initGiteaPaths() initGiteaPaths()
giteaConf := initGiteaConf() giteaConf := initGiteaConf()
cleanUpEnv() cleanUpEnv()
initWorkPathAndConfig() initWorkPathAndConfig()
if RepoRootPath == "" || AppDataPath == "" {
panic("SetupGiteaTestEnv failed, paths are not initialized")
}
// 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

View File

@@ -118,7 +118,7 @@ func PrintCurrentTest(t testing.TB, skip ...int) func() {
deferHasRun := false deferHasRun := false
t.Cleanup(func() { t.Cleanup(func() {
if !deferHasRun { if !deferHasRun {
stdoutPrintf("!!! %s defer function hasn't been run but Cleanup is called, usually caused by panic\n", t.Name()) stdoutPrintf("!!! %s: defer function hasn't been run but Cleanup is called, usually caused by panic\n", t.Name())
} }
}) })
stdoutPrintf("=== %s (%s:%d)\n", log.NewColoredValue(t.Name()), strings.TrimPrefix(filename, prefix), line) stdoutPrintf("=== %s (%s:%d)\n", log.NewColoredValue(t.Name()), strings.TrimPrefix(filename, prefix), line)
@@ -173,7 +173,8 @@ func Init() {
log.RegisterEventWriter("test", newTestLoggerWriter) log.RegisterEventWriter("test", newTestLoggerWriter)
} }
func Panicf(format string, args ...any) { // MainErrorf is used to report an error from TestMain and return a non-zero value to indicate the failure
// don't call os.Exit, otherwise the "defer" functions won't be executed func MainErrorf(msg string, a ...any) int {
panic(fmt.Sprintf(format, args...)) _, _ = fmt.Fprintf(os.Stderr, msg+"\n", a...)
return 1
} }

View File

@@ -5,8 +5,10 @@ package repo
import ( import (
"net/http" "net/http"
"strconv"
"testing" "testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/contexttest"
@@ -17,8 +19,17 @@ import (
func TestTestHook(t *testing.T) { func TestTestHook(t *testing.T) {
unittest.PrepareTestEnv(t) unittest.PrepareTestEnv(t)
hook := &webhook.Webhook{
RepoID: 1,
URL: "https://www.example.com/test_hook",
ContentType: webhook.ContentTypeJSON,
Events: `{"push_only":true}`,
IsActive: true,
}
assert.NoError(t, db.Insert(t.Context(), hook))
ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/wiki/_pages") ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/wiki/_pages")
ctx.SetPathParam("id", "1") ctx.SetPathParam("id", strconv.FormatInt(hook.ID, 10))
contexttest.LoadRepo(t, ctx, 1) contexttest.LoadRepo(t, ctx, 1)
contexttest.LoadRepoCommit(t, ctx) contexttest.LoadRepoCommit(t, ctx)
contexttest.LoadUser(t, ctx, 2) contexttest.LoadUser(t, ctx, 2)
@@ -26,6 +37,6 @@ func TestTestHook(t *testing.T) {
assert.Equal(t, http.StatusNoContent, ctx.Resp.WrittenStatus()) assert.Equal(t, http.StatusNoContent, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{ unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{
HookID: 1, HookID: hook.ID,
}, unittest.Cond("is_delivered=?", false)) }, unittest.Cond("is_delivered=?", false))
} }

View File

@@ -36,10 +36,7 @@ import (
func newDefaultRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (req *http.Request, body []byte, err error) { func newDefaultRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (req *http.Request, body []byte, err error) {
switch w.HTTPMethod { switch w.HTTPMethod {
case "": case "", http.MethodPost:
log.Info("HTTP Method for %s webhook %s [ID: %d] is not set, defaulting to POST", w.Type, w.URL, w.ID)
fallthrough
case http.MethodPost:
switch w.ContentType { switch w.ContentType {
case webhook_model.ContentTypeJSON: case webhook_model.ContentTypeJSON:
req, err = http.NewRequest(http.MethodPost, w.URL, strings.NewReader(t.PayloadContent)) req, err = http.NewRequest(http.MethodPost, w.URL, strings.NewReader(t.PayloadContent))

View File

@@ -6,6 +6,7 @@ package webhook
import ( import (
"testing" "testing"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@@ -21,7 +22,17 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestWebhook_GetSlackHook(t *testing.T) { func TestWebhookService(t *testing.T) {
unittest.PrepareTestEnv(t)
t.Run("GetSlackHook", testWebhookGetSlackHook)
t.Run("PrepareWebhooks", testWebhookPrepare)
t.Run("PrepareBranchFilterMatch", testWebhookPrepareBranchFilterMatch)
t.Run("PrepareBranchFilterNoMatch", testWebhookPrepareBranchFilterNoMatch)
t.Run("WebhookUserMail", testWebhookUserMail)
t.Run("CheckBranchFilter", testWebhookCheckBranchFilter)
}
func testWebhookGetSlackHook(t *testing.T) {
w := &webhook_model.Webhook{ w := &webhook_model.Webhook{
Meta: `{"channel": "foo", "username": "username", "color": "blue"}`, Meta: `{"channel": "foo", "username": "username", "color": "blue"}`,
} }
@@ -33,66 +44,69 @@ func TestWebhook_GetSlackHook(t *testing.T) {
}, *slackHook) }, *slackHook)
} }
func TestPrepareWebhooks(t *testing.T) { func testWebhookPrepare(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
hookTasks := []*webhook_model.HookTask{ hook := &webhook_model.Webhook{
{HookID: 1, EventType: webhook_module.HookEventPush}, RepoID: repo.ID,
} URL: "http://localhost/gitea-webhook-test-prepare_webhooks",
for _, hookTask := range hookTasks { ContentType: webhook_model.ContentTypeJSON,
unittest.AssertNotExistsBean(t, hookTask) Events: `{"push_only":true}`,
} IsActive: true,
assert.NoError(t, PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Commits: []*api.PayloadCommit{{}}}))
for _, hookTask := range hookTasks {
unittest.AssertExistsAndLoadBean(t, hookTask)
} }
require.NoError(t, db.Insert(t.Context(), hook))
hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush}
unittest.AssertNotExistsBean(t, hookTask)
err := PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Commits: []*api.PayloadCommit{{}}})
require.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
} }
func TestPrepareWebhooksBranchFilterMatch(t *testing.T) { func testWebhookPrepareBranchFilterMatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
hookTasks := []*webhook_model.HookTask{ hook := &webhook_model.Webhook{
{HookID: 4, EventType: webhook_module.HookEventPush}, RepoID: repo.ID,
} URL: "http://localhost/gitea-webhook-test-branch_filter_match",
for _, hookTask := range hookTasks { ContentType: webhook_model.ContentTypeJSON,
unittest.AssertNotExistsBean(t, hookTask) Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`,
IsActive: true,
} }
require.NoError(t, db.Insert(t.Context(), hook))
hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush}
unittest.AssertNotExistsBean(t, hookTask)
// this test also ensures that * doesn't handle / in any special way (like shell would) // this test also ensures that * doesn't handle / in any special way (like shell would)
assert.NoError(t, PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/feature/7791", Commits: []*api.PayloadCommit{{}}})) err := PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/feature/7791", Commits: []*api.PayloadCommit{{}}})
for _, hookTask := range hookTasks { require.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask) unittest.AssertExistsAndLoadBean(t, hookTask)
}
} }
func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) { func testWebhookPrepareBranchFilterNoMatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
hookTasks := []*webhook_model.HookTask{ hook := &webhook_model.Webhook{
{HookID: 4, EventType: webhook_module.HookEventPush}, RepoID: repo.ID,
URL: "http://localhost/gitea-webhook-test-branch_filter_no_match",
ContentType: webhook_model.ContentTypeJSON,
Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`,
IsActive: true,
} }
for _, hookTask := range hookTasks { require.NoError(t, db.Insert(t.Context(), hook))
unittest.AssertNotExistsBean(t, hookTask)
}
assert.NoError(t, PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"}))
for _, hookTask := range hookTasks { hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush}
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
} err := PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"})
require.NoError(t, err)
unittest.AssertNotExistsBean(t, hookTask)
} }
func TestWebhookUserMail(t *testing.T) { func testWebhookUserMail(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
defer test.MockVariableValue(&setting.Service.NoReplyAddress, "no-reply.com")() defer test.MockVariableValue(&setting.Service.NoReplyAddress, "no-reply.com")()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.Equal(t, user.GetPlaceholderEmail(), convert.ToUser(t.Context(), user, nil).Email) assert.Equal(t, user.GetPlaceholderEmail(), convert.ToUser(t.Context(), user, nil).Email)
assert.Equal(t, user.Email, convert.ToUser(t.Context(), user, user).Email) assert.Equal(t, user.Email, convert.ToUser(t.Context(), user, user).Email)
} }
func TestCheckBranchFilter(t *testing.T) { func testWebhookCheckBranchFilter(t *testing.T) {
cases := []struct { cases := []struct {
filter string filter string
ref git.RefName ref git.RefName

View File

@@ -87,16 +87,19 @@ func testMain(m *testing.M) int {
graceful.InitManager(managerCtx) graceful.InitManager(managerCtx)
defer cancel() defer cancel()
tests.InitTest() err := tests.InitTest()
if err != nil {
return testlogger.MainErrorf("InitTest error: %v", err)
}
testWebRoutes = routers.NormalRoutes() testWebRoutes = routers.NormalRoutes()
err := unittest.InitFixtures( err = unittest.InitFixtures(
unittest.FixturesOptions{ unittest.FixturesOptions{
Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"), Dir: filepath.Join(setting.GetGiteaTestSourceRoot(), "models/fixtures/"),
}, },
) )
if err != nil { if err != nil {
testlogger.Panicf("InitFixtures: %v", err) return testlogger.MainErrorf("InitFixtures: %v", err)
} }
// FIXME: the console logger is deleted by mistake, so if there is any `log.Fatal`, developers won't see any error message. // FIXME: the console logger is deleted by mistake, so if there is any `log.Fatal`, developers won't see any error message.

View File

@@ -40,7 +40,7 @@ func initMigrationTest(t *testing.T) func() {
setting.SetupGiteaTestEnv() 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(setting.GetGiteaTestSourceRoot(), "tests/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitFull()) assert.NoError(t, git.InitFull())
setting.LoadDBSetting() setting.LoadDBSetting()
setting.InitLoggersForTest() setting.InitLoggersForTest()

View File

@@ -17,6 +17,7 @@ import (
"time" "time"
auth_model "code.gitea.io/gitea/models/auth" auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
pull_model "code.gitea.io/gitea/models/pull" pull_model "code.gitea.io/gitea/models/pull"
@@ -92,12 +93,25 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str
return resp return resp
} }
func preparePullMergeWebhook(t *testing.T, repoID int64) {
require.NoError(t, db.TruncateBeans(t.Context(), &webhook.Webhook{}, &webhook.HookTask{}))
require.NoError(t, db.Insert(t.Context(), &webhook.Webhook{
RepoID: repoID,
URL: "http://localhost/gitea-test-webhook-pull-merge",
ContentType: webhook.ContentTypeJSON,
Events: `{"push_only":true,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":false}}`,
IsActive: true,
}))
}
func assertPullMergeWebhookTask(t *testing.T, repoID int64) {
hook := unittest.AssertExistsAndLoadBean(t, &webhook.Webhook{RepoID: repoID})
unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{HookID: hook.ID})
}
func TestPullMerge(t *testing.T) { func TestPullMerge(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number preparePullMergeWebhook(t, 1)
assert.NoError(t, err)
hookTasksLenBefore := len(hookTasks)
session := loginUser(t, "user1") // FIXME: don't use admin user for testing session := loginUser(t, "user1") // FIXME: don't use admin user for testing
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "") testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
@@ -123,18 +137,13 @@ func TestPullMerge(t *testing.T) {
assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 4, repo.NumPulls)
assert.Equal(t, 3, repo.NumOpenPulls) assert.Equal(t, 3, repo.NumOpenPulls)
hookTasks, err = webhook.HookTasks(t.Context(), 1, 1) assertPullMergeWebhookTask(t, 1)
assert.NoError(t, err)
assert.Len(t, hookTasks, hookTasksLenBefore+1)
}) })
} }
func TestPullRebase(t *testing.T) { func TestPullRebase(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number preparePullMergeWebhook(t, 1)
assert.NoError(t, err)
hookTasksLenBefore := len(hookTasks)
session := loginUser(t, "user1") // FIXME: don't use admin user for testing session := loginUser(t, "user1") // FIXME: don't use admin user for testing
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "") testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
@@ -160,18 +169,13 @@ func TestPullRebase(t *testing.T) {
assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 4, repo.NumPulls)
assert.Equal(t, 3, repo.NumOpenPulls) assert.Equal(t, 3, repo.NumOpenPulls)
hookTasks, err = webhook.HookTasks(t.Context(), 1, 1) assertPullMergeWebhookTask(t, 1)
assert.NoError(t, err)
assert.Len(t, hookTasks, hookTasksLenBefore+1)
}) })
} }
func TestPullRebaseMerge(t *testing.T) { func TestPullRebaseMerge(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number preparePullMergeWebhook(t, 1)
assert.NoError(t, err)
hookTasksLenBefore := len(hookTasks)
session := loginUser(t, "user1") // FIXME: don't use admin user for testing session := loginUser(t, "user1") // FIXME: don't use admin user for testing
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "") testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
@@ -197,18 +201,13 @@ func TestPullRebaseMerge(t *testing.T) {
assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 4, repo.NumPulls)
assert.Equal(t, 3, repo.NumOpenPulls) assert.Equal(t, 3, repo.NumOpenPulls)
hookTasks, err = webhook.HookTasks(t.Context(), 1, 1) assertPullMergeWebhookTask(t, 1)
assert.NoError(t, err)
assert.Len(t, hookTasks, hookTasksLenBefore+1)
}) })
} }
func TestPullSquash(t *testing.T) { func TestPullSquash(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number preparePullMergeWebhook(t, 1)
assert.NoError(t, err)
hookTasksLenBefore := len(hookTasks)
session := loginUser(t, "user1") // FIXME: don't use admin user for testing session := loginUser(t, "user1") // FIXME: don't use admin user for testing
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "") testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
@@ -223,18 +222,13 @@ func TestPullSquash(t *testing.T) {
DeleteBranch: false, DeleteBranch: false,
}) })
hookTasks, err = webhook.HookTasks(t.Context(), 1, 1) assertPullMergeWebhookTask(t, 1)
assert.NoError(t, err)
assert.Len(t, hookTasks, hookTasksLenBefore+1)
}) })
} }
func TestPullSquashWithHeadCommitID(t *testing.T) { func TestPullSquashWithHeadCommitID(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number preparePullMergeWebhook(t, 1)
assert.NoError(t, err)
hookTasksLenBefore := len(hookTasks)
session := loginUser(t, "user1") // FIXME: don't use admin user for testing session := loginUser(t, "user1") // FIXME: don't use admin user for testing
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "") testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
@@ -267,9 +261,7 @@ func TestPullSquashWithHeadCommitID(t *testing.T) {
assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 4, repo.NumPulls)
assert.Equal(t, 3, repo.NumOpenPulls) assert.Equal(t, 3, repo.NumOpenPulls)
hookTasks, err = webhook.HookTasks(t.Context(), 1, 1) assertPullMergeWebhookTask(t, 1)
assert.NoError(t, err)
assert.Len(t, hookTasks, hookTasksLenBefore+1)
}) })
} }
@@ -303,18 +295,14 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
// Check PR branch deletion // Check PR branch deletion
resp = testPullCleanUp(t, session, elem[1], elem[2], elem[4]) resp = testPullCleanUp(t, session, elem[1], elem[2], elem[4])
respJSON := struct { respJSON := test.ParseJSONRedirect(resp.Body.Bytes())
Redirect string require.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found")
}{}
DecodeJSON(t, resp, &respJSON)
assert.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found") elem = strings.Split(*respJSON.Redirect, "/")
elem = strings.Split(respJSON.Redirect, "/")
assert.Equal(t, "pulls", elem[3]) assert.Equal(t, "pulls", elem[3])
// Check branch deletion result // Check branch deletion result
req := NewRequest(t, "GET", respJSON.Redirect) req := NewRequest(t, "GET", *respJSON.Redirect)
resp = session.MakeRequest(t, req, http.StatusOK) resp = session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body) htmlDoc := NewHTMLParser(t, resp.Body)
@@ -767,8 +755,7 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
// search issues // search issues
searchIssuesResp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) searchIssuesResp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
var apiIssuesBefore []*api.Issue apiIssuesBefore := DecodeJSON(t, searchIssuesResp, []*api.Issue{})
DecodeJSON(t, searchIssuesResp, &apiIssuesBefore)
assert.Empty(t, apiIssuesBefore) assert.Empty(t, apiIssuesBefore)
// merge the pull request // merge the pull request
@@ -790,11 +777,9 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
// search issues again // search issues again
searchIssuesResp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) searchIssuesResp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
var apiIssuesAfter []*api.Issue apiIssuesAfter := DecodeJSON(t, searchIssuesResp, []*api.Issue{})
DecodeJSON(t, searchIssuesResp, &apiIssuesAfter) require.Len(t, apiIssuesAfter, 1)
if assert.Len(t, apiIssuesAfter, 1) { assert.Equal(t, issue.ID, apiIssuesAfter[0].ID)
assert.Equal(t, issue.ID, apiIssuesAfter[0].ID)
}
}) })
} }

View File

@@ -10,12 +10,14 @@ import (
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"path" "path"
"strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
actions_model "code.gitea.io/gitea/models/actions" actions_model "code.gitea.io/gitea/models/actions"
auth_model "code.gitea.io/gitea/models/auth" auth_model "code.gitea.io/gitea/models/auth"
db_model "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
@@ -40,26 +42,28 @@ import (
func TestNewWebHookLink(t *testing.T) { func TestNewWebHookLink(t *testing.T) {
defer tests.PrepareTestEnv(t)() defer tests.PrepareTestEnv(t)()
require.NoError(t, db_model.Insert(t.Context(), &webhook.Webhook{
RepoID: 1,
URL: "http://localhost/gitea-test-webhook-link",
ContentType: webhook.ContentTypeJSON,
Events: `{}`,
IsActive: true,
}))
hook := unittest.AssertExistsAndLoadBean(t, &webhook.Webhook{RepoID: 1})
session := loginUser(t, "user2") session := loginUser(t, "user2")
webhooksBaseHref := "/user2/repo1/settings/hooks"
baseurl := "/user2/repo1/settings/hooks" cases := []string{
tests := []string{ webhooksBaseHref,
// webhook list page webhooksBaseHref + "/gitea/new",
baseurl, webhooksBaseHref + "/" + strconv.FormatInt(hook.ID, 10), // edit webhook
// new webhook page
baseurl + "/gitea/new",
// edit webhook page
baseurl + "/1",
} }
for _, reqHref := range cases {
for _, url := range tests { resp := session.MakeRequest(t, NewRequest(t, "GET", reqHref), http.StatusOK)
resp := session.MakeRequest(t, NewRequest(t, "GET", url), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body) htmlDoc := NewHTMLParser(t, resp.Body)
menus := htmlDoc.doc.Find(".ui.top.attached.header .ui.dropdown .menu a") menus := htmlDoc.doc.Find(".ui.top.attached.header .ui.dropdown .menu a")
menus.Each(func(i int, menu *goquery.Selection) { menus.Each(func(i int, menu *goquery.Selection) {
url, exist := menu.Attr("href") foundHref := menu.AttrOr("href", "")
assert.True(t, exist) assert.True(t, strings.HasPrefix(foundHref, webhooksBaseHref))
assert.True(t, strings.HasPrefix(url, baseurl))
}) })
} }
} }

View File

@@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
@@ -15,7 +16,6 @@ import (
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/testlogger" "code.gitea.io/gitea/modules/testlogger"
@@ -25,8 +25,9 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func InitTest() { func InitTest() error {
testlogger.Init() testlogger.Init()
if os.Getenv("GITEA_TEST_CONF") == "" { if os.Getenv("GITEA_TEST_CONF") == "" {
// By default, use sqlite.ini for testing, then IDE like GoLand can start the test process with debugger. // 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. // It's easier for developers to debug bugs step by step with a debugger.
@@ -39,101 +40,102 @@ func InitTest() {
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 {
log.Fatal("git.InitOnceWithSync: %v", err) return err
} }
setting.LoadDBSetting() setting.LoadDBSetting()
if err := storage.Init(); err != nil { if err := storage.Init(); err != nil {
testlogger.Panicf("Init storage failed: %v\n", err) return err
} }
switch { switch {
case setting.Database.Type.IsMySQL(): case setting.Database.Type.IsMySQL():
connType := "tcp" {
if len(setting.Database.Host) > 0 && setting.Database.Host[0] == '/' { // looks like a unix socket connType := util.Iif(strings.HasPrefix(setting.Database.Host, "/"), "unix", "tcp")
connType = "unix" db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@%s(%s)/",
} setting.Database.User, setting.Database.Passwd, connType, setting.Database.Host))
if err != nil {
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@%s(%s)/", return err
setting.Database.User, setting.Database.Passwd, connType, setting.Database.Host)) }
defer db.Close() defer db.Close()
if err != nil { if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + setting.Database.Name); err != nil {
log.Fatal("sql.Open: %v", err) return err
}
if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + setting.Database.Name); err != nil {
log.Fatal("db.Exec: %v", err)
}
case setting.Database.Type.IsPostgreSQL():
var db *sql.DB
var err error
if setting.Database.Host[0] == '/' {
db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@/%s?sslmode=%s&host=%s",
setting.Database.User, setting.Database.Passwd, setting.Database.Name, setting.Database.SSLMode, setting.Database.Host))
} else {
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))
}
defer db.Close()
if err != nil {
log.Fatal("sql.Open: %v", err)
}
dbrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", setting.Database.Name))
if err != nil {
log.Fatal("db.Query: %v", err)
}
defer dbrows.Close()
if !dbrows.Next() {
if _, err = db.Exec("CREATE DATABASE " + setting.Database.Name); err != nil {
log.Fatal("db.Exec: CREATE DATABASE: %v", err)
} }
} }
// Check if we need to setup a specific schema case setting.Database.Type.IsPostgreSQL():
if len(setting.Database.Schema) == 0 { openPostgreSQL := func() (*sql.DB, error) {
break if strings.HasPrefix(setting.Database.Host, "/") {
} return sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@/%s?sslmode=%s&host=%s",
db.Close() setting.Database.User, setting.Database.Passwd, setting.Database.Name, setting.Database.SSLMode, setting.Database.Host))
}
if setting.Database.Host[0] == '/' { return sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@/%s?sslmode=%s&host=%s",
setting.Database.User, setting.Database.Passwd, setting.Database.Name, setting.Database.SSLMode, setting.Database.Host))
} else {
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)) setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
} }
// This is a different db object; requires a different Close()
defer db.Close()
if err != nil {
log.Fatal("sql.Open: %v", err)
}
schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema))
if err != nil {
log.Fatal("db.Query: %v", err)
}
defer schrows.Close()
if !schrows.Next() { // create database
// Create and setup a DB schema {
if _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema); err != nil { db, err := openPostgreSQL()
log.Fatal("db.Exec: CREATE SCHEMA: %v", err) if err != nil {
return err
}
defer db.Close()
dbRows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", setting.Database.Name))
if err != nil {
return err
}
defer dbRows.Close()
if !dbRows.Next() {
if _, err = db.Exec("CREATE DATABASE " + setting.Database.Name); err != nil {
return err
}
}
// Check if we need to set up a specific schema
if setting.Database.Schema == "" {
break
}
db.Close()
}
// create schema
{
db, err := openPostgreSQL()
if err != nil {
return err
}
defer db.Close()
schemaRows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema))
if err != nil {
return err
}
defer schemaRows.Close()
if !schemaRows.Next() {
// Create and set up a DB schema
if _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema); err != nil {
return err
}
} }
} }
case setting.Database.Type.IsMSSQL(): case setting.Database.Type.IsMSSQL():
host, port := setting.ParseMSSQLHostPort(setting.Database.Host) {
db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port := setting.ParseMSSQLHostPort(setting.Database.Host)
host, port, "master", setting.Database.User, setting.Database.Passwd)) db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;",
if err != nil { host, port, "master", setting.Database.User, setting.Database.Passwd))
log.Fatal("sql.Open: %v", err) if err != nil {
return err
}
defer db.Close()
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 {
return err
}
} }
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 {
log.Fatal("db.Exec: %v", err)
}
defer db.Close()
} }
routers.InitWebInstalled(graceful.GetManager().HammerContext()) routers.InitWebInstalled(graceful.GetManager().HammerContext())
return nil
} }
func PrepareAttachmentsStorage(t testing.TB) { func PrepareAttachmentsStorage(t testing.TB) {
@@ -154,7 +156,7 @@ func PrepareGitRepoDirectory(t testing.TB) {
if !assert.NotEmpty(t, setting.RepoRootPath) { if !assert.NotEmpty(t, setting.RepoRootPath) {
return return
} }
assert.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath)) assert.NoError(t, unittest.SyncDirs(filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/gitea-repositories-meta"), setting.RepoRootPath))
} }
func PrepareArtifactsStorage(t testing.TB) { func PrepareArtifactsStorage(t testing.TB) {