mirror of
https://github.com/gogs/gogs.git
synced 2025-12-23 08:40:01 +01:00
conf: add unit tests (#5954)
* conf: add tests for utils.go * conf: add tests for static.go * mock os/exec * Run tests on Windows * appveyor: fix gcc not found * computed: add unit tests * log: add unit tests * log: fix tests on Windows * conf: add some tests * Finish adding tests * Cover more cases * Add tests for testutil * Add more tests
This commit is contained in:
@@ -11,6 +11,10 @@ build: false
|
|||||||
deploy: false
|
deploy: false
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
- set PATH=C:\msys64\mingw64\bin;%PATH% # Fix "gcc" not found: https://github.com/appveyor/ci/issues/2613
|
||||||
- go version
|
- go version
|
||||||
- go env
|
- go env
|
||||||
- go build -tags "minwinsvc" -v
|
- go build -tags "minwinsvc" -v
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- go test -v -race -cover ./...
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ var (
|
|||||||
// string when environment variables are not set.
|
// string when environment variables are not set.
|
||||||
func HomeDir() string {
|
func HomeDir() string {
|
||||||
homeDirOnce.Do(func() {
|
homeDirOnce.Do(func() {
|
||||||
if !IsWindowsRuntime() {
|
|
||||||
homeDir = os.Getenv("HOME")
|
homeDir = os.Getenv("HOME")
|
||||||
|
if homeDir != "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
126
internal/conf/computed_test.go
Normal file
126
internal/conf/computed_test.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2020 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package conf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"gogs.io/gogs/internal/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsProdMode(t *testing.T) {
|
||||||
|
before := App.RunMode
|
||||||
|
defer func() {
|
||||||
|
App.RunMode = before
|
||||||
|
}()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
mode string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{mode: "dev", want: false},
|
||||||
|
{mode: "test", want: false},
|
||||||
|
|
||||||
|
{mode: "prod", want: true},
|
||||||
|
{mode: "Prod", want: true},
|
||||||
|
{mode: "PROD", want: true},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
App.RunMode = test.mode
|
||||||
|
assert.Equal(t, test.want, IsProdMode())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWorkDirHelper(t *testing.T) {
|
||||||
|
if !testutil.WantHelperProcess() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stdout, WorkDir())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWorkDir(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
env string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{env: "GOGS_WORK_DIR=/tmp", want: "/tmp"},
|
||||||
|
{env: "", want: WorkDir()},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
out, err := testutil.Exec("TestWorkDirHelper", test.env)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, test.want, out)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomDirHelper(t *testing.T) {
|
||||||
|
if !testutil.WantHelperProcess() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stdout, CustomDir())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomDir(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
env string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{env: "GOGS_CUSTOM=/tmp", want: "/tmp"},
|
||||||
|
{env: "", want: CustomDir()},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
out, err := testutil.Exec("TestCustomDirHelper", test.env)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, test.want, out)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHomeDirHelper(t *testing.T) {
|
||||||
|
if !testutil.WantHelperProcess() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stdout, HomeDir())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHomeDir(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
envs []string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{envs: []string{"HOME=/tmp"}, want: "/tmp"},
|
||||||
|
{envs: []string{`USERPROFILE=C:\Users\Joe`}, want: `C:\Users\Joe`},
|
||||||
|
{envs: []string{`HOMEDRIVE=C:`, `HOMEPATH=\Users\Joe`}, want: `C:\Users\Joe`},
|
||||||
|
{envs: nil, want: ""},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
out, err := testutil.Exec("TestHomeDirHelper", test.envs...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, test.want, out)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -353,7 +353,7 @@ func Init(customConf string) error {
|
|||||||
// ----- I18n settings -----
|
// ----- I18n settings -----
|
||||||
// *************************
|
// *************************
|
||||||
|
|
||||||
I18n = new(i18n)
|
I18n = new(i18nConf)
|
||||||
if err = File.Section("i18n").MapTo(I18n); err != nil {
|
if err = File.Section("i18n").MapTo(I18n); err != nil {
|
||||||
return errors.Wrap(err, "mapping [i18n] section")
|
return errors.Wrap(err, "mapping [i18n] section")
|
||||||
}
|
}
|
||||||
|
|||||||
88
internal/conf/conf_test.go
Normal file
88
internal/conf/conf_test.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2020 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package conf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
|
||||||
|
"gogs.io/gogs/internal/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAsset(t *testing.T) {
|
||||||
|
// Make sure it does not blow up
|
||||||
|
_, err := Asset("conf/app.ini")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssetDir(t *testing.T) {
|
||||||
|
// Make sure it does not blow up
|
||||||
|
_, err := AssetDir("conf")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMustAsset(t *testing.T) {
|
||||||
|
// Make sure it does not blow up
|
||||||
|
MustAsset("conf/app.ini")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInit(t *testing.T) {
|
||||||
|
if IsWindowsRuntime() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ini.PrettyFormat = false
|
||||||
|
defer func() {
|
||||||
|
MustInit("")
|
||||||
|
ini.PrettyFormat = true
|
||||||
|
}()
|
||||||
|
|
||||||
|
assert.Nil(t, Init(filepath.Join("testdata", "custom.ini")))
|
||||||
|
|
||||||
|
cfg := ini.Empty()
|
||||||
|
cfg.NameMapper = ini.SnackCase
|
||||||
|
|
||||||
|
for _, v := range []struct {
|
||||||
|
section string
|
||||||
|
config interface{}
|
||||||
|
}{
|
||||||
|
{"", &App},
|
||||||
|
{"server", &Server},
|
||||||
|
{"server", &SSH},
|
||||||
|
{"repository", &Repository},
|
||||||
|
{"database", &Database},
|
||||||
|
{"security", &Security},
|
||||||
|
{"email", &Email},
|
||||||
|
{"auth", &Auth},
|
||||||
|
{"user", &User},
|
||||||
|
{"session", &Session},
|
||||||
|
{"attachment", &Attachment},
|
||||||
|
{"time", &Time},
|
||||||
|
{"picture", &Picture},
|
||||||
|
{"mirror", &Mirror},
|
||||||
|
{"i18n", &I18n},
|
||||||
|
} {
|
||||||
|
err := cfg.Section(v.section).ReflectFrom(v.config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: %v", v.section, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err := cfg.WriteTo(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testutil.AssertGolden(t, filepath.Join("testdata", "TestInit.golden.ini"), testutil.Update("TestInit"), buf.String())
|
||||||
|
}
|
||||||
@@ -9,28 +9,39 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Log settings
|
type loggerConf struct {
|
||||||
var Log struct {
|
Buffer int64
|
||||||
RootPath string
|
Config interface{}
|
||||||
Modes []string
|
|
||||||
Configs []interface{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitLogging initializes the logging service of the application.
|
type logConf struct {
|
||||||
func InitLogging() {
|
RootPath string
|
||||||
Log.RootPath = File.Section("log").Key("ROOT_PATH").MustString(filepath.Join(WorkDir(), "log"))
|
Modes []string
|
||||||
|
Configs []*loggerConf
|
||||||
|
}
|
||||||
|
|
||||||
// Because we always create a console logger as the primary logger at init time,
|
// Log settings
|
||||||
// we need to remove it in case the user doesn't configure to use it after the
|
var Log *logConf
|
||||||
// logging service is initalized.
|
|
||||||
hasConsole := false
|
// initLogConf returns parsed logging configuration from given INI file.
|
||||||
|
// NOTE: Because we always create a console logger as the primary logger at init time,
|
||||||
|
// we need to remove it in case the user doesn't configure to use it after the logging
|
||||||
|
// service is initalized.
|
||||||
|
func initLogConf(cfg *ini.File) (_ *logConf, hasConsole bool, _ error) {
|
||||||
|
rootPath := cfg.Section("log").Key("ROOT_PATH").MustString(filepath.Join(WorkDir(), "log"))
|
||||||
|
modes := strings.Split(cfg.Section("log").Key("MODE").MustString("console"), ",")
|
||||||
|
lc := &logConf{
|
||||||
|
RootPath: ensureAbs(rootPath),
|
||||||
|
Modes: make([]string, 0, len(modes)),
|
||||||
|
Configs: make([]*loggerConf, 0, len(modes)),
|
||||||
|
}
|
||||||
|
|
||||||
// Iterate over [log.*] sections to initialize individual logger.
|
// Iterate over [log.*] sections to initialize individual logger.
|
||||||
Log.Modes = strings.Split(File.Section("log").Key("MODE").MustString("console"), ",")
|
|
||||||
Log.Configs = make([]interface{}, 0, len(Log.Modes))
|
|
||||||
levelMappings := map[string]log.Level{
|
levelMappings := map[string]log.Level{
|
||||||
"trace": log.LevelTrace,
|
"trace": log.LevelTrace,
|
||||||
"info": log.LevelInfo,
|
"info": log.LevelInfo,
|
||||||
@@ -39,43 +50,30 @@ func InitLogging() {
|
|||||||
"fatal": log.LevelFatal,
|
"fatal": log.LevelFatal,
|
||||||
}
|
}
|
||||||
|
|
||||||
type config struct {
|
for i := range modes {
|
||||||
Buffer int64
|
modes[i] = strings.ToLower(strings.TrimSpace(modes[i]))
|
||||||
Config interface{}
|
secName := "log." + modes[i]
|
||||||
}
|
sec, err := cfg.GetSection(secName)
|
||||||
for _, mode := range Log.Modes {
|
|
||||||
mode = strings.ToLower(strings.TrimSpace(mode))
|
|
||||||
secName := "log." + mode
|
|
||||||
sec, err := File.GetSection(secName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Missing configuration section [%s] for %q logger", secName, mode)
|
return nil, hasConsole, errors.Errorf("missing configuration section [%s] for %q logger", secName, modes[i])
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
level := levelMappings[strings.ToLower(sec.Key("LEVEL").MustString("trace"))]
|
level := levelMappings[strings.ToLower(sec.Key("LEVEL").MustString("trace"))]
|
||||||
buffer := sec.Key("BUFFER_LEN").MustInt64(100)
|
buffer := sec.Key("BUFFER_LEN").MustInt64(100)
|
||||||
var c *config
|
var c *loggerConf
|
||||||
switch mode {
|
switch modes[i] {
|
||||||
case log.DefaultConsoleName:
|
case log.DefaultConsoleName:
|
||||||
hasConsole = true
|
hasConsole = true
|
||||||
c = &config{
|
c = &loggerConf{
|
||||||
Buffer: buffer,
|
Buffer: buffer,
|
||||||
Config: log.ConsoleConfig{
|
Config: log.ConsoleConfig{
|
||||||
Level: level,
|
Level: level,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = log.NewConsole(c.Buffer, c.Config)
|
|
||||||
|
|
||||||
case log.DefaultFileName:
|
case log.DefaultFileName:
|
||||||
logPath := filepath.Join(Log.RootPath, "gogs.log")
|
logPath := filepath.Join(lc.RootPath, "gogs.log")
|
||||||
logDir := filepath.Dir(logPath)
|
c = &loggerConf{
|
||||||
err = os.MkdirAll(logDir, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to create log directory %q: %v", logDir, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c = &config{
|
|
||||||
Buffer: buffer,
|
Buffer: buffer,
|
||||||
Config: log.FileConfig{
|
Config: log.FileConfig{
|
||||||
Level: level,
|
Level: level,
|
||||||
@@ -89,20 +87,18 @@ func InitLogging() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = log.NewFile(c.Buffer, c.Config)
|
|
||||||
|
|
||||||
case log.DefaultSlackName:
|
case log.DefaultSlackName:
|
||||||
c = &config{
|
c = &loggerConf{
|
||||||
Buffer: buffer,
|
Buffer: buffer,
|
||||||
Config: log.SlackConfig{
|
Config: log.SlackConfig{
|
||||||
Level: level,
|
Level: level,
|
||||||
URL: sec.Key("URL").String(),
|
URL: sec.Key("URL").String(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = log.NewSlack(c.Buffer, c.Config)
|
|
||||||
|
|
||||||
case log.DefaultDiscordName:
|
case log.DefaultDiscordName:
|
||||||
c = &config{
|
c = &loggerConf{
|
||||||
Buffer: buffer,
|
Buffer: buffer,
|
||||||
Config: log.DiscordConfig{
|
Config: log.DiscordConfig{
|
||||||
Level: level,
|
Level: level,
|
||||||
@@ -110,22 +106,62 @@ func InitLogging() {
|
|||||||
Username: sec.Key("USERNAME").String(),
|
Username: sec.Key("USERNAME").String(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = log.NewDiscord(c.Buffer, c.Config)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lc.Modes = append(lc.Modes, modes[i])
|
||||||
|
lc.Configs = append(lc.Configs, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lc, hasConsole, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitLogging initializes the logging service of the application.
|
||||||
|
func InitLogging() {
|
||||||
|
logConf, hasConsole, err := initLogConf(File)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to init logging configuration: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.MkdirAll(logConf.RootPath, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to create log directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, mode := range logConf.Modes {
|
||||||
|
c := logConf.Configs[i]
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var level log.Level
|
||||||
|
switch mode {
|
||||||
|
case log.DefaultConsoleName:
|
||||||
|
level = c.Config.(log.ConsoleConfig).Level
|
||||||
|
err = log.NewConsole(c.Buffer, c.Config)
|
||||||
|
case log.DefaultFileName:
|
||||||
|
level = c.Config.(log.FileConfig).Level
|
||||||
|
err = log.NewFile(c.Buffer, c.Config)
|
||||||
|
case log.DefaultSlackName:
|
||||||
|
level = c.Config.(log.SlackConfig).Level
|
||||||
|
err = log.NewSlack(c.Buffer, c.Config)
|
||||||
|
case log.DefaultDiscordName:
|
||||||
|
level = c.Config.(log.DiscordConfig).Level
|
||||||
|
err = log.NewDiscord(c.Buffer, c.Config)
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to init %s logger: %v", mode, err)
|
log.Fatal("Failed to init %s logger: %v", mode, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Configs = append(Log.Configs, c)
|
|
||||||
log.Trace("Log mode: %s (%s)", strings.Title(mode), strings.Title(strings.ToLower(level.String())))
|
log.Trace("Log mode: %s (%s)", strings.Title(mode), strings.Title(strings.ToLower(level.String())))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hasConsole {
|
if !hasConsole {
|
||||||
log.Remove(log.DefaultConsoleName)
|
log.Remove(log.DefaultConsoleName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log = logConf
|
||||||
}
|
}
|
||||||
|
|||||||
134
internal/conf/log_test.go
Normal file
134
internal/conf/log_test.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
// Copyright 2020 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package conf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
log "unknwon.dev/clog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_initLogConf(t *testing.T) {
|
||||||
|
t.Run("missing configuration section", func(t *testing.T) {
|
||||||
|
f, err := ini.Load([]byte(`
|
||||||
|
[log]
|
||||||
|
MODE = console
|
||||||
|
`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got, hasConsole, err := initLogConf(f)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
assert.Equal(t, `missing configuration section [log.console] for "console" logger`, err.Error())
|
||||||
|
assert.False(t, hasConsole)
|
||||||
|
assert.Nil(t, got)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("no console logger", func(t *testing.T) {
|
||||||
|
f, err := ini.Load([]byte(`
|
||||||
|
[log]
|
||||||
|
MODE = file
|
||||||
|
|
||||||
|
[log.file]
|
||||||
|
`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got, hasConsole, err := initLogConf(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.False(t, hasConsole)
|
||||||
|
assert.NotNil(t, got)
|
||||||
|
})
|
||||||
|
|
||||||
|
f, err := ini.Load([]byte(`
|
||||||
|
[log]
|
||||||
|
ROOT_PATH = log
|
||||||
|
MODE = console, file, slack, discord
|
||||||
|
BUFFER_LEN = 50
|
||||||
|
LEVEL = trace
|
||||||
|
|
||||||
|
[log.console]
|
||||||
|
BUFFER_LEN = 10
|
||||||
|
|
||||||
|
[log.file]
|
||||||
|
LEVEL = INFO
|
||||||
|
LOG_ROTATE = true
|
||||||
|
DAILY_ROTATE = true
|
||||||
|
MAX_SIZE_SHIFT = 20
|
||||||
|
MAX_LINES = 1000
|
||||||
|
MAX_DAYS = 3
|
||||||
|
|
||||||
|
[log.slack]
|
||||||
|
LEVEL = Warn
|
||||||
|
URL = https://slack.com
|
||||||
|
|
||||||
|
[log.discord]
|
||||||
|
LEVEL = error
|
||||||
|
URL = https://discordapp.com
|
||||||
|
USERNAME = yoyo
|
||||||
|
`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got, hasConsole, err := initLogConf(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logConf := &logConf{
|
||||||
|
RootPath: filepath.Join(WorkDir(), "log"),
|
||||||
|
Modes: []string{
|
||||||
|
log.DefaultConsoleName,
|
||||||
|
log.DefaultFileName,
|
||||||
|
log.DefaultSlackName,
|
||||||
|
log.DefaultDiscordName,
|
||||||
|
},
|
||||||
|
Configs: []*loggerConf{
|
||||||
|
{
|
||||||
|
Buffer: 10,
|
||||||
|
Config: log.ConsoleConfig{
|
||||||
|
Level: log.LevelTrace,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
Buffer: 50,
|
||||||
|
Config: log.FileConfig{
|
||||||
|
Level: log.LevelInfo,
|
||||||
|
Filename: filepath.Join(WorkDir(), "log", "gogs.log"),
|
||||||
|
FileRotationConfig: log.FileRotationConfig{
|
||||||
|
Rotate: true,
|
||||||
|
Daily: true,
|
||||||
|
MaxSize: 1 << 20,
|
||||||
|
MaxLines: 1000,
|
||||||
|
MaxDays: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
Buffer: 50,
|
||||||
|
Config: log.SlackConfig{
|
||||||
|
Level: log.LevelWarn,
|
||||||
|
URL: "https://slack.com",
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
Buffer: 50,
|
||||||
|
Config: log.DiscordConfig{
|
||||||
|
Level: log.LevelError,
|
||||||
|
URL: "https://discordapp.com",
|
||||||
|
Username: "yoyo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.True(t, hasConsole)
|
||||||
|
assert.Equal(t, logConf, got)
|
||||||
|
}
|
||||||
@@ -16,12 +16,12 @@ import (
|
|||||||
|
|
||||||
// HasMinWinSvc is whether the application is built with Windows Service support.
|
// HasMinWinSvc is whether the application is built with Windows Service support.
|
||||||
//
|
//
|
||||||
// ⚠️ WARNING: should only be set by "static_minwinsvc.go".
|
// ⚠️ WARNING: should only be set by "internal/conf/static_minwinsvc.go".
|
||||||
var HasMinWinSvc bool
|
var HasMinWinSvc bool
|
||||||
|
|
||||||
// Build time and commit information.
|
// Build time and commit information.
|
||||||
//
|
//
|
||||||
// ⚠️ WARNING: should only be set by -ldflags.
|
// ⚠️ WARNING: should only be set by "-ldflags".
|
||||||
var (
|
var (
|
||||||
BuildTime string
|
BuildTime string
|
||||||
BuildCommit string
|
BuildCommit string
|
||||||
@@ -35,7 +35,7 @@ var CustomConf string
|
|||||||
var (
|
var (
|
||||||
// Application settings
|
// Application settings
|
||||||
App struct {
|
App struct {
|
||||||
// ⚠️ WARNING: Should only be set by main package (i.e. "gogs.go").
|
// ⚠️ WARNING: Should only be set by the main package (i.e. "gogs.go").
|
||||||
Version string `ini:"-"`
|
Version string `ini:"-"`
|
||||||
|
|
||||||
BrandName string
|
BrandName string
|
||||||
@@ -288,7 +288,7 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// I18n settings
|
// I18n settings
|
||||||
I18n *i18n
|
I18n *i18nConf
|
||||||
|
|
||||||
// Webhook settings
|
// Webhook settings
|
||||||
Webhook struct {
|
Webhook struct {
|
||||||
@@ -349,7 +349,9 @@ var (
|
|||||||
|
|
||||||
// Git settings
|
// Git settings
|
||||||
Git struct {
|
Git struct {
|
||||||
|
// ⚠️ WARNING: Should only be set by "internal/db/repo.go".
|
||||||
Version string `ini:"-"`
|
Version string `ini:"-"`
|
||||||
|
|
||||||
DisableDiffHighlight bool
|
DisableDiffHighlight bool
|
||||||
MaxGitDiffLines int
|
MaxGitDiffLines int
|
||||||
MaxGitDiffLineCharacters int
|
MaxGitDiffLineCharacters int
|
||||||
@@ -408,15 +410,15 @@ var (
|
|||||||
HasRobotsTxt bool
|
HasRobotsTxt bool
|
||||||
)
|
)
|
||||||
|
|
||||||
type i18n struct {
|
type i18nConf struct {
|
||||||
Langs []string `delim:","`
|
Langs []string `delim:","`
|
||||||
Names []string `delim:","`
|
Names []string `delim:","`
|
||||||
dateLangs map[string]string
|
dateLangs map[string]string `ini:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DateLang transforms standard language locale name to corresponding value in datetime plugin.
|
// DateLang transforms standard language locale name to corresponding value in datetime plugin.
|
||||||
func (i *i18n) DateLang(lang string) string {
|
func (c *i18nConf) DateLang(lang string) string {
|
||||||
name, ok := i.dateLangs[lang]
|
name, ok := c.dateLangs[lang]
|
||||||
if ok {
|
if ok {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|||||||
35
internal/conf/static_test.go
Normal file
35
internal/conf/static_test.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2020 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package conf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_i18n_DateLang(t *testing.T) {
|
||||||
|
c := &i18nConf{
|
||||||
|
dateLangs: map[string]string{
|
||||||
|
"en-US": "en",
|
||||||
|
"zh-CN": "zh",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
lang string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{lang: "en-US", want: "en"},
|
||||||
|
{lang: "zh-CN", want: "zh"},
|
||||||
|
|
||||||
|
{lang: "jp-JP", want: "en"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.want, c.DateLang(test.lang))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
153
internal/conf/testdata/TestInit.golden.ini
vendored
Normal file
153
internal/conf/testdata/TestInit.golden.ini
vendored
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
BRAND_NAME=Testing
|
||||||
|
RUN_USER=git
|
||||||
|
RUN_MODE=test
|
||||||
|
APP_NAME=
|
||||||
|
|
||||||
|
[server]
|
||||||
|
EXTERNAL_URL=http://localhost:3080/
|
||||||
|
DOMAIN=localhost
|
||||||
|
PROTOCOL=http
|
||||||
|
HTTP_ADDR=0.0.0.0
|
||||||
|
HTTP_PORT=3000
|
||||||
|
CERT_FILE=custom/https/cert.pem
|
||||||
|
KEY_FILE=custom/https/key.pem
|
||||||
|
TLS_MIN_VERSION=TLS12
|
||||||
|
UNIX_SOCKET_PERMISSION=666
|
||||||
|
LOCAL_ROOT_URL=http://0.0.0.0:3000/
|
||||||
|
OFFLINE_MODE=false
|
||||||
|
DISABLE_ROUTER_LOG=true
|
||||||
|
ENABLE_GZIP=false
|
||||||
|
APP_DATA_PATH=/tmp/data
|
||||||
|
LOAD_ASSETS_FROM_DISK=false
|
||||||
|
LANDING_URL=/explore
|
||||||
|
ROOT_URL=
|
||||||
|
LANDING_PAGE=
|
||||||
|
DISABLE_SSH=false
|
||||||
|
SSH_DOMAIN=localhost
|
||||||
|
SSH_PORT=22
|
||||||
|
SSH_ROOT_PATH=/tmp
|
||||||
|
SSH_KEYGEN_PATH=ssh-keygen
|
||||||
|
SSH_KEY_TEST_PATH=/tmp/ssh-key-test
|
||||||
|
MINIMUM_KEY_SIZE_CHECK=true
|
||||||
|
REWRITE_AUTHORIZED_KEYS_AT_START=false
|
||||||
|
START_SSH_SERVER=false
|
||||||
|
SSH_LISTEN_HOST=0.0.0.0
|
||||||
|
SSH_LISTEN_PORT=22
|
||||||
|
SSH_SERVER_CIPHERS=aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,arcfour256,arcfour128
|
||||||
|
|
||||||
|
[repository]
|
||||||
|
ROOT=/tmp/gogs-repositories
|
||||||
|
SCRIPT_TYPE=bash
|
||||||
|
ANSI_CHARSET=
|
||||||
|
FORCE_PRIVATE=false
|
||||||
|
MAX_CREATION_LIMIT=-1
|
||||||
|
PREFERRED_LICENSES=Apache License 2.0,MIT License
|
||||||
|
DISABLE_HTTP_GIT=false
|
||||||
|
ENABLE_LOCAL_PATH_MIGRATION=false
|
||||||
|
ENABLE_RAW_FILE_RENDER_MODE=false
|
||||||
|
COMMITS_FETCH_CONCURRENCY=0
|
||||||
|
|
||||||
|
[repository.editor]
|
||||||
|
LINE_WRAP_EXTENSIONS=.txt,.md,.markdown,.mdown,.mkd
|
||||||
|
PREVIEWABLE_FILE_MODES=markdown
|
||||||
|
|
||||||
|
[repository.upload]
|
||||||
|
ENABLED=true
|
||||||
|
TEMP_PATH=/tmp/uploads
|
||||||
|
ALLOWED_TYPES=
|
||||||
|
FILE_MAX_SIZE=3
|
||||||
|
MAX_FILES=5
|
||||||
|
|
||||||
|
[database]
|
||||||
|
TYPE=sqlite
|
||||||
|
HOST=127.0.0.1:5432
|
||||||
|
NAME=gogs
|
||||||
|
USER=gogs
|
||||||
|
PASSWORD=12345678
|
||||||
|
SSL_MODE=disable
|
||||||
|
PATH=/tmp/gogs.db
|
||||||
|
DB_TYPE=
|
||||||
|
PASSWD=
|
||||||
|
|
||||||
|
[security]
|
||||||
|
INSTALL_LOCK=false
|
||||||
|
SECRET_KEY=`!#@FDEWREWR&*(`
|
||||||
|
LOGIN_REMEMBER_DAYS=7
|
||||||
|
COOKIE_REMEMBER_NAME=gogs_incredible
|
||||||
|
COOKIE_USERNAME=gogs_awesome
|
||||||
|
COOKIE_SECURE=false
|
||||||
|
ENABLE_LOGIN_STATUS_COOKIE=false
|
||||||
|
LOGIN_STATUS_COOKIE_NAME=login_status
|
||||||
|
REVERSE_PROXY_AUTHENTICATION_USER=
|
||||||
|
|
||||||
|
[email]
|
||||||
|
ENABLED=true
|
||||||
|
SUBJECT_PREFIX=[Gogs]
|
||||||
|
HOST=smtp.mailgun.org:587
|
||||||
|
FROM=noreply@gogs.localhost
|
||||||
|
USER=noreply@gogs.localhost
|
||||||
|
PASSWORD=87654321
|
||||||
|
DISABLE_HELO=false
|
||||||
|
HELO_HOSTNAME=
|
||||||
|
SKIP_VERIFY=false
|
||||||
|
USE_CERTIFICATE=false
|
||||||
|
CERT_FILE=custom/email/cert.pem
|
||||||
|
KEY_FILE=custom/email/key.pem
|
||||||
|
USE_PLAIN_TEXT=false
|
||||||
|
ADD_PLAIN_TEXT_ALT=false
|
||||||
|
PASSWD=
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
ACTIVATE_CODE_LIVES=10
|
||||||
|
RESET_PASSWORD_CODE_LIVES=10
|
||||||
|
REQUIRE_EMAIL_CONFIRMATION=true
|
||||||
|
REQUIRE_SIGNIN_VIEW=false
|
||||||
|
DISABLE_REGISTRATION=false
|
||||||
|
ENABLE_REGISTRATION_CAPTCHA=true
|
||||||
|
ENABLE_REVERSE_PROXY_AUTHENTICATION=false
|
||||||
|
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION=false
|
||||||
|
REVERSE_PROXY_AUTHENTICATION_HEADER=X-FORWARDED-FOR
|
||||||
|
ACTIVE_CODE_LIVE_MINUTES=0
|
||||||
|
RESET_PASSWD_CODE_LIVE_MINUTES=0
|
||||||
|
REGISTER_EMAIL_CONFIRM=false
|
||||||
|
ENABLE_CAPTCHA=false
|
||||||
|
ENABLE_NOTIFY_MAIL=false
|
||||||
|
|
||||||
|
[user]
|
||||||
|
ENABLE_EMAIL_NOTIFICATION=true
|
||||||
|
|
||||||
|
[session]
|
||||||
|
PROVIDER=memory
|
||||||
|
PROVIDER_CONFIG=data/sessions
|
||||||
|
COOKIE_NAME=i_like_gogs
|
||||||
|
COOKIE_SECURE=false
|
||||||
|
GC_INTERVAL=10
|
||||||
|
MAX_LIFE_TIME=10
|
||||||
|
CSRF_COOKIE_NAME=_csrf
|
||||||
|
GC_INTERVAL_TIME=0
|
||||||
|
SESSION_LIFE_TIME=0
|
||||||
|
|
||||||
|
[attachment]
|
||||||
|
ENABLED=true
|
||||||
|
PATH=/tmp/attachments
|
||||||
|
ALLOWED_TYPES=image/jpeg|image/png
|
||||||
|
MAX_SIZE=4
|
||||||
|
MAX_FILES=5
|
||||||
|
|
||||||
|
[time]
|
||||||
|
FORMAT=RFC1123
|
||||||
|
|
||||||
|
[picture]
|
||||||
|
AVATAR_UPLOAD_PATH=/tmp/avatars
|
||||||
|
REPOSITORY_AVATAR_UPLOAD_PATH=/tmp/repo-avatars
|
||||||
|
GRAVATAR_SOURCE=https://secure.gravatar.com/avatar/
|
||||||
|
DISABLE_GRAVATAR=false
|
||||||
|
ENABLE_FEDERATED_AVATAR=false
|
||||||
|
|
||||||
|
[mirror]
|
||||||
|
DEFAULT_INTERVAL=8
|
||||||
|
|
||||||
|
[i18n]
|
||||||
|
LANGS=en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR,gl-ES,uk-UA,en-GB,hu-HU,sk-SK,id-ID,fa-IR,vi-VN,pt-PT
|
||||||
|
NAMES=English,简体中文,繁體中文(香港),繁體中文(臺灣),Deutsch,français,Nederlands,latviešu,русский,日本語,español,português do Brasil,polski,български,italiano,suomi,Türkçe,čeština,српски,svenska,한국어,galego,українська,English (United Kingdom),Magyar,Slovenčina,Indonesian,Persian,Vietnamese,Português
|
||||||
|
|
||||||
46
internal/conf/testdata/custom.ini
vendored
Normal file
46
internal/conf/testdata/custom.ini
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
APP_NAME = Testing
|
||||||
|
RUN_MODE = test
|
||||||
|
|
||||||
|
[server]
|
||||||
|
ROOT_URL = http://localhost:3080/
|
||||||
|
APP_DATA_PATH = /tmp/data
|
||||||
|
SSH_ROOT_PATH = /tmp
|
||||||
|
SSH_KEY_TEST_PATH = /tmp/ssh-key-test
|
||||||
|
MINIMUM_KEY_SIZE_CHECK = true
|
||||||
|
LANDING_PAGE = explore
|
||||||
|
|
||||||
|
[repository]
|
||||||
|
ROOT = /tmp/gogs-repositories
|
||||||
|
|
||||||
|
[repository.upload]
|
||||||
|
TEMP_PATH = /tmp/uploads
|
||||||
|
|
||||||
|
[database]
|
||||||
|
DB_TYPE = sqlite
|
||||||
|
PASSWD = 12345678
|
||||||
|
PATH = /tmp/gogs.db
|
||||||
|
|
||||||
|
[security]
|
||||||
|
REVERSE_PROXY_AUTHENTICATION_USER=X-FORWARDED-FOR
|
||||||
|
|
||||||
|
[email]
|
||||||
|
ENABLED = true
|
||||||
|
PASSWD = 87654321
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
ACTIVE_CODE_LIVE_MINUTES = 10
|
||||||
|
RESET_PASSWD_CODE_LIVE_MINUTES = 10
|
||||||
|
REGISTER_EMAIL_CONFIRM = true
|
||||||
|
ENABLE_CAPTCHA = true
|
||||||
|
ENABLE_NOTIFY_MAIL = true
|
||||||
|
|
||||||
|
[session]
|
||||||
|
GC_INTERVAL_TIME = 10
|
||||||
|
SESSION_LIFE_TIME = 10
|
||||||
|
|
||||||
|
[attachment]
|
||||||
|
PATH = /tmp/attachments
|
||||||
|
|
||||||
|
[picture]
|
||||||
|
AVATAR_UPLOAD_PATH = /tmp/avatars
|
||||||
|
REPOSITORY_AVATAR_UPLOAD_PATH = /tmp/repo-avatars
|
||||||
@@ -14,6 +14,13 @@ import (
|
|||||||
"gogs.io/gogs/internal/process"
|
"gogs.io/gogs/internal/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// cleanUpOpenSSHVersion cleans up the raw output of "ssh -V" and returns a clean version string.
|
||||||
|
func cleanUpOpenSSHVersion(raw string) string {
|
||||||
|
v := strings.TrimRight(strings.Fields(raw)[0], ",1234567890")
|
||||||
|
v = strings.TrimSuffix(strings.TrimPrefix(v, "OpenSSH_"), "p")
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
// openSSHVersion returns string representation of OpenSSH version via command "ssh -V".
|
// openSSHVersion returns string representation of OpenSSH version via command "ssh -V".
|
||||||
func openSSHVersion() (string, error) {
|
func openSSHVersion() (string, error) {
|
||||||
// NOTE: Somehow the version is printed to stderr.
|
// NOTE: Somehow the version is printed to stderr.
|
||||||
@@ -22,10 +29,7 @@ func openSSHVersion() (string, error) {
|
|||||||
return "", errors.Wrap(err, stderr)
|
return "", errors.Wrap(err, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim unused information, see https://github.com/gogs/gogs/issues/4507#issuecomment-305150441.
|
return cleanUpOpenSSHVersion(stderr), nil
|
||||||
v := strings.TrimRight(strings.Fields(stderr)[0], ",1234567890")
|
|
||||||
v = strings.TrimSuffix(strings.TrimPrefix(v, "OpenSSH_"), "p")
|
|
||||||
return v, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensureAbs prepends the WorkDir to the given path if it is not an absolute path.
|
// ensureAbs prepends the WorkDir to the given path if it is not an absolute path.
|
||||||
|
|||||||
57
internal/conf/utils_test.go
Normal file
57
internal/conf/utils_test.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2020 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package conf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_cleanUpOpenSSHVersion(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
raw string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
raw: "OpenSSH_7.4p1 Ubuntu-10, OpenSSL 1.0.2g 1 Mar 2016",
|
||||||
|
want: "7.4",
|
||||||
|
}, {
|
||||||
|
raw: "OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013",
|
||||||
|
want: "5.3",
|
||||||
|
}, {
|
||||||
|
raw: "OpenSSH_4.3p2, OpenSSL 0.9.8e-fips-rhel5 01 Jul 2008",
|
||||||
|
want: "4.3",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.want, cleanUpOpenSSHVersion(test.raw))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ensureAbs(t *testing.T) {
|
||||||
|
wd := WorkDir()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
path string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
path: "data/avatars",
|
||||||
|
want: filepath.Join(wd, "data", "avatars"),
|
||||||
|
}, {
|
||||||
|
path: wd,
|
||||||
|
want: wd,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.want, ensureAbs(test.path))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,9 +25,9 @@ func IsExist(path string) bool {
|
|||||||
|
|
||||||
// CurrentUsername returns the current system user via environment variables.
|
// CurrentUsername returns the current system user via environment variables.
|
||||||
func CurrentUsername() string {
|
func CurrentUsername() string {
|
||||||
curUserName := os.Getenv("USER")
|
username := os.Getenv("USER")
|
||||||
if len(curUserName) > 0 {
|
if len(username) > 0 {
|
||||||
return curUserName
|
return username
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.Getenv("USERNAME")
|
return os.Getenv("USERNAME")
|
||||||
|
|||||||
@@ -55,3 +55,8 @@ func TestIsExist(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCurrentUsername(t *testing.T) {
|
||||||
|
// Make sure it does not blow up
|
||||||
|
CurrentUsername()
|
||||||
|
}
|
||||||
|
|||||||
46
internal/testutil/exec.go
Normal file
46
internal/testutil/exec.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2020 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package testutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exec executes "go test" on given helper with supplied environment variables.
|
||||||
|
// It is useful to mock "os/exec" functions in tests. When succeeded, it returns
|
||||||
|
// the result produced by the test helper.
|
||||||
|
// The test helper should:
|
||||||
|
// 1. Use WantHelperProcess function to determine if it is being called in helper mode.
|
||||||
|
// 2. Call fmt.Fprintln(os.Stdout, ...) to print results for the main test to collect.
|
||||||
|
func Exec(helper string, envs ...string) (string, error) {
|
||||||
|
cmd := exec.Command(os.Args[0], "-test.run="+helper, "--")
|
||||||
|
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
|
||||||
|
cmd.Env = append(cmd.Env, envs...)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
str := string(out)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("%v - %s", err, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(str, "no tests to run") {
|
||||||
|
return "", errors.New("no tests to run")
|
||||||
|
} else if !strings.Contains(str, "PASS") {
|
||||||
|
return "", errors.New(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect helper result
|
||||||
|
result := str[:strings.Index(str, "PASS")]
|
||||||
|
result = strings.TrimSpace(result)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WantHelperProcess returns true if current process is in helper mode.
|
||||||
|
func WantHelperProcess() bool {
|
||||||
|
return os.Getenv("GO_WANT_HELPER_PROCESS") == "1"
|
||||||
|
}
|
||||||
55
internal/testutil/exec_test.go
Normal file
55
internal/testutil/exec_test.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2020 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package testutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExecHelper(t *testing.T) {
|
||||||
|
if !WantHelperProcess() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("PASS") != "1" {
|
||||||
|
fmt.Fprintln(os.Stdout, "tests failed")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stdout, "tests succeed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExec(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
helper string
|
||||||
|
env string
|
||||||
|
expOut string
|
||||||
|
expErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
helper: "NoTestsToRun",
|
||||||
|
expErr: errors.New("no tests to run"),
|
||||||
|
}, {
|
||||||
|
helper: "TestExecHelper",
|
||||||
|
expErr: errors.New("exit status 1 - tests failed\n"),
|
||||||
|
}, {
|
||||||
|
helper: "TestExecHelper",
|
||||||
|
env: "PASS=1",
|
||||||
|
expOut: "tests succeed",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
out, err := Exec(test.helper, test.env)
|
||||||
|
assert.Equal(t, test.expErr, err)
|
||||||
|
assert.Equal(t, test.expOut, out)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
63
internal/testutil/golden.go
Normal file
63
internal/testutil/golden.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2020 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package testutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"io/ioutil"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var updateRegex = flag.String("update", "", "Update testdata of tests matching the given regex")
|
||||||
|
|
||||||
|
// Update returns true if update regex mathces given test name.
|
||||||
|
func Update(name string) bool {
|
||||||
|
if updateRegex == nil || *updateRegex == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return regexp.MustCompile(*updateRegex).MatchString(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertGolden compares what's got and what's in the golden file. It updates
|
||||||
|
// the golden file on-demand.
|
||||||
|
func AssertGolden(t testing.TB, path string, update bool, got interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
data := marshal(t, got)
|
||||||
|
|
||||||
|
if update {
|
||||||
|
if err := ioutil.WriteFile(path, data, 0640); err != nil {
|
||||||
|
t.Fatalf("update golden file %q: %s", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
golden, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("read golden file %q: %s", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, string(golden), string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshal(t testing.TB, v interface{}) []byte {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
switch v2 := v.(type) {
|
||||||
|
case string:
|
||||||
|
return []byte(v2)
|
||||||
|
case []byte:
|
||||||
|
return v2
|
||||||
|
default:
|
||||||
|
data, err := json.MarshalIndent(v, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
52
internal/testutil/golden_test.go
Normal file
52
internal/testutil/golden_test.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2020 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package testutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUpdate(t *testing.T) {
|
||||||
|
before := updateRegex
|
||||||
|
defer func() {
|
||||||
|
updateRegex = before
|
||||||
|
}()
|
||||||
|
|
||||||
|
t.Run("no flag", func(t *testing.T) {
|
||||||
|
updateRegex = nil
|
||||||
|
assert.False(t, Update("TestUpdate"))
|
||||||
|
})
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
regex string
|
||||||
|
name string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{regex: "", name: "TestUpdate", want: false},
|
||||||
|
{regex: "TestNotFound", name: "TestUpdate", want: false},
|
||||||
|
|
||||||
|
{regex: ".*", name: "TestUpdate", want: true},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
updateRegex = &test.regex
|
||||||
|
assert.Equal(t, test.want, Update(test.name))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssertGolden(t *testing.T) {
|
||||||
|
// Make sure it does not blow up
|
||||||
|
AssertGolden(t, filepath.Join("testdata", "golden"), false, "{\n \"Message\": \"This is a golden file.\"\n}")
|
||||||
|
AssertGolden(t, filepath.Join("testdata", "golden"), false, []byte("{\n \"Message\": \"This is a golden file.\"\n}"))
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
AssertGolden(t, filepath.Join("testdata", "golden"), false, T{"This is a golden file."})
|
||||||
|
}
|
||||||
3
internal/testutil/testdata/golden
vendored
Normal file
3
internal/testutil/testdata/golden
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"Message": "This is a golden file."
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user