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
pull/5955/head
ᴜɴᴋɴᴡᴏɴ 2020-02-29 22:24:20 +08:00 committed by GitHub
parent 17ae0ed3ee
commit 8796df8218
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 974 additions and 65 deletions

View File

@ -11,6 +11,10 @@ build: false
deploy: false
install:
- set PATH=C:\msys64\mingw64\bin;%PATH% # Fix "gcc" not found: https://github.com/appveyor/ci/issues/2613
- go version
- go env
- go build -tags "minwinsvc" -v
test_script:
- go test -v -race -cover ./...

View File

@ -99,8 +99,8 @@ var (
// string when environment variables are not set.
func HomeDir() string {
homeDirOnce.Do(func() {
if !IsWindowsRuntime() {
homeDir = os.Getenv("HOME")
homeDir = os.Getenv("HOME")
if homeDir != "" {
return
}

View 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)
})
}
}

View File

@ -353,7 +353,7 @@ func Init(customConf string) error {
// ----- I18n settings -----
// *************************
I18n = new(i18n)
I18n = new(i18nConf)
if err = File.Section("i18n").MapTo(I18n); err != nil {
return errors.Wrap(err, "mapping [i18n] section")
}

View 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())
}

View File

@ -9,28 +9,39 @@ import (
"path/filepath"
"strings"
"github.com/pkg/errors"
"gopkg.in/ini.v1"
log "unknwon.dev/clog/v2"
)
// Log settings
var Log struct {
RootPath string
Modes []string
Configs []interface{}
type loggerConf struct {
Buffer int64
Config interface{}
}
// InitLogging initializes the logging service of the application.
func InitLogging() {
Log.RootPath = File.Section("log").Key("ROOT_PATH").MustString(filepath.Join(WorkDir(), "log"))
type logConf struct {
RootPath string
Modes []string
Configs []*loggerConf
}
// 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.
hasConsole := false
// Log settings
var Log *logConf
// 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.
Log.Modes = strings.Split(File.Section("log").Key("MODE").MustString("console"), ",")
Log.Configs = make([]interface{}, 0, len(Log.Modes))
levelMappings := map[string]log.Level{
"trace": log.LevelTrace,
"info": log.LevelInfo,
@ -39,43 +50,30 @@ func InitLogging() {
"fatal": log.LevelFatal,
}
type config struct {
Buffer int64
Config interface{}
}
for _, mode := range Log.Modes {
mode = strings.ToLower(strings.TrimSpace(mode))
secName := "log." + mode
sec, err := File.GetSection(secName)
for i := range modes {
modes[i] = strings.ToLower(strings.TrimSpace(modes[i]))
secName := "log." + modes[i]
sec, err := cfg.GetSection(secName)
if err != nil {
log.Fatal("Missing configuration section [%s] for %q logger", secName, mode)
return
return nil, hasConsole, errors.Errorf("missing configuration section [%s] for %q logger", secName, modes[i])
}
level := levelMappings[strings.ToLower(sec.Key("LEVEL").MustString("trace"))]
buffer := sec.Key("BUFFER_LEN").MustInt64(100)
var c *config
switch mode {
var c *loggerConf
switch modes[i] {
case log.DefaultConsoleName:
hasConsole = true
c = &config{
c = &loggerConf{
Buffer: buffer,
Config: log.ConsoleConfig{
Level: level,
},
}
err = log.NewConsole(c.Buffer, c.Config)
case log.DefaultFileName:
logPath := filepath.Join(Log.RootPath, "gogs.log")
logDir := filepath.Dir(logPath)
err = os.MkdirAll(logDir, os.ModePerm)
if err != nil {
log.Fatal("Failed to create log directory %q: %v", logDir, err)
return
}
c = &config{
logPath := filepath.Join(lc.RootPath, "gogs.log")
c = &loggerConf{
Buffer: buffer,
Config: log.FileConfig{
Level: level,
@ -89,20 +87,18 @@ func InitLogging() {
},
},
}
err = log.NewFile(c.Buffer, c.Config)
case log.DefaultSlackName:
c = &config{
c = &loggerConf{
Buffer: buffer,
Config: log.SlackConfig{
Level: level,
URL: sec.Key("URL").String(),
},
}
err = log.NewSlack(c.Buffer, c.Config)
case log.DefaultDiscordName:
c = &config{
c = &loggerConf{
Buffer: buffer,
Config: log.DiscordConfig{
Level: level,
@ -110,22 +106,62 @@ func InitLogging() {
Username: sec.Key("USERNAME").String(),
},
}
err = log.NewDiscord(c.Buffer, c.Config)
default:
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 {
log.Fatal("Failed to init %s logger: %v", mode, err)
return
}
Log.Configs = append(Log.Configs, c)
log.Trace("Log mode: %s (%s)", strings.Title(mode), strings.Title(strings.ToLower(level.String())))
}
if !hasConsole {
log.Remove(log.DefaultConsoleName)
}
Log = logConf
}

134
internal/conf/log_test.go Normal file
View 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)
}

View File

@ -16,12 +16,12 @@ import (
// 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
// Build time and commit information.
//
// ⚠️ WARNING: should only be set by -ldflags.
// ⚠️ WARNING: should only be set by "-ldflags".
var (
BuildTime string
BuildCommit string
@ -35,7 +35,7 @@ var CustomConf string
var (
// Application settings
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:"-"`
BrandName string
@ -288,7 +288,7 @@ var (
}
// I18n settings
I18n *i18n
I18n *i18nConf
// Webhook settings
Webhook struct {
@ -349,7 +349,9 @@ var (
// Git settings
Git struct {
Version string `ini:"-"`
// ⚠️ WARNING: Should only be set by "internal/db/repo.go".
Version string `ini:"-"`
DisableDiffHighlight bool
MaxGitDiffLines int
MaxGitDiffLineCharacters int
@ -408,15 +410,15 @@ var (
HasRobotsTxt bool
)
type i18n struct {
Langs []string `delim:","`
Names []string `delim:","`
dateLangs map[string]string
type i18nConf struct {
Langs []string `delim:","`
Names []string `delim:","`
dateLangs map[string]string `ini:"-"`
}
// DateLang transforms standard language locale name to corresponding value in datetime plugin.
func (i *i18n) DateLang(lang string) string {
name, ok := i.dateLangs[lang]
func (c *i18nConf) DateLang(lang string) string {
name, ok := c.dateLangs[lang]
if ok {
return name
}

View 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))
})
}
}

View 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
View 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

View File

@ -14,6 +14,13 @@ import (
"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".
func openSSHVersion() (string, error) {
// NOTE: Somehow the version is printed to stderr.
@ -22,10 +29,7 @@ func openSSHVersion() (string, error) {
return "", errors.Wrap(err, stderr)
}
// Trim unused information, see https://github.com/gogs/gogs/issues/4507#issuecomment-305150441.
v := strings.TrimRight(strings.Fields(stderr)[0], ",1234567890")
v = strings.TrimSuffix(strings.TrimPrefix(v, "OpenSSH_"), "p")
return v, nil
return cleanUpOpenSSHVersion(stderr), nil
}
// ensureAbs prepends the WorkDir to the given path if it is not an absolute path.

View 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))
})
}
}

View File

@ -25,9 +25,9 @@ func IsExist(path string) bool {
// CurrentUsername returns the current system user via environment variables.
func CurrentUsername() string {
curUserName := os.Getenv("USER")
if len(curUserName) > 0 {
return curUserName
username := os.Getenv("USER")
if len(username) > 0 {
return username
}
return os.Getenv("USERNAME")

View File

@ -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
View 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"
}

View 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)
})
}
}

View 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
}
}

View 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
View File

@ -0,0 +1,3 @@
{
"Message": "This is a golden file."
}