2
0

Выполнен рефакторинг. Добавлен workflow.

This commit is contained in:
Алексей Бадяев 2024-10-20 13:50:44 +07:00
parent f884b56a10
commit a06ee87fba
Signed by: alexey
GPG Key ID: 686FBC1363E4AFAE
16 changed files with 1004 additions and 382 deletions

View File

@ -0,0 +1,38 @@
name: test
run-name: ${{ gitea.actor }} test MouseSoft Config
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: check-out
uses: https://gitea.com/actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: set-up go
uses: https://gitea.com/actions/setup-go@v3
with:
go-version: ">=1.22"
- name: set-up dependencies
run: |
go install github.com/kisielk/errcheck@latest
go install honnef.co/go/tools/cmd/staticcheck@latest
go install github.com/sashamelentyev/usestdlibvars@latest
go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest
make vendor
- name: lint
run: make lint
# - name: golangci-lint
# uses: https://github.com/golangci/golangci-lint-action@v6
# with:
# version: v1.60
- name: test
id: build
run: make test

314
.golangci.yaml Normal file
View File

@ -0,0 +1,314 @@
# This file contains all available configuration options
# with their default values (in comments).
#
# This file is not a configuration example,
# it contains the exhaustive configuration with explanations of the options.
linters:
# Disable all linters.
# Default: false
disable-all: true
# Enable specific linter
# https://golangci-lint.run/usage/linters/#enabled-by-default
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- canonicalheader
- containedctx
- contextcheck
- copyloopvar
- cyclop
- decorder
- dogsled
- dupl
- dupword
- durationcheck
- err113
- errcheck
- errchkjson
- errname
- errorlint
- exhaustive
- fatcontext
- forcetypeassert
- funlen
- gci
- ginkgolinter
- gocheckcompilerdirectives
- gochecknoglobals
- gochecknoinits
- gochecksumtype
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- godox
- gofmt
- gofumpt
- goheader
- goimports
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- gosimple
- gosmopolitan
- govet
- grouper
- importas
- inamedparam
- ineffassign
- interfacebloat
- intrange
- ireturn
- lll
- loggercheck
- maintidx
- makezero
- mirror
- misspell
- mnd
- musttag
- nakedret
- nestif
- nilerr
- nilnil
- nlreturn
- noctx
- nolintlint
- nonamedreturns
- nosprintfhostport
- paralleltest
- perfsprint
- prealloc
- predeclared
- promlinter
- protogetter
- reassign
- revive
- rowserrcheck
- sloglint
- spancheck
- sqlclosecheck
- staticcheck
- stylecheck
- tagalign
- tagliatelle
- tenv
- testableexamples
- testifylint
- testpackage
- thelper
- tparallel
- unconvert
- unparam
- unused
- usestdlibvars
- varnamelen
- wastedassign
- whitespace
- wrapcheck
- wsl
- zerologlint
# Enable all available linters.
# Default: false
enable-all: false
# Enable only fast linters from enabled linters set (first run won't be fast)
# Default: false
fast: true
# All available settings of specific linters.
linters-settings:
cyclop:
# The maximal code complexity to report.
# Default: 10
max-complexity: 20
# Should ignore tests.
# Default: false
skip-tests: true
funlen:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
# Default: 60
lines: 80
# Checks the number of statements in a function.
# If lower than 0, disable the check.
# Default: 40
statements: 50
# Ignore comments when counting lines.
# Default false
ignore-comments: true
gocognit:
# Minimal code complexity to report.
# Default: 30 (but we recommend 10-20)
min-complexity: 20
gocyclo:
# Minimal code complexity to report.
# Default: 30 (but we recommend 10-20)
min-complexity: 20
godot:
# Comments to be checked: `declarations`, `toplevel`, or `all`.
# Default: declarations
scope: all
# List of regexps for excluding particular comment lines from check.
# Default: []
exclude:
# Exclude todo and fixme comments.
- "^fixme:"
- "^todo:"
# Check that each sentence ends with a period.
# Default: true
period: false
# Check that each sentence starts with a capital letter.
# Default: false
capital: true
goimports:
local-prefixes: git.mousesoft.ru/ms/config
gosec:
excludes:
- G115
revive:
rules:
- name: unexported-return
disabled: true
# output configuration options
output:
# The formats used to render issues.
# Formats:
# - `colored-line-number`
# - `line-number`
# - `json`
# - `colored-tab`
# - `tab`
# - `html`
# - `checkstyle`
# - `code-climate`
# - `junit-xml`
# - `junit-xml-extended`
# - `github-actions`
# - `teamcity`
# - `sarif`
# Output path can be either `stdout`, `stderr` or path to the file to write to.
#
# For the CLI flag (`--out-format`), multiple formats can be specified by separating them by comma.
# The output can be specified for each of them by separating format name and path by colon symbol.
# Example: "--out-format=checkstyle:report.xml,json:stdout,colored-line-number"
# The CLI flag (`--out-format`) override the configuration file.
#
# Default:
# formats:
# - format: colored-line-number
# path: stdout
formats:
# - format: json
# path: stderr
# - format: checkstyle
# path: report.xml
- format: colored-line-number
# Print lines of code with issue.
# Default: true
print-issued-lines: false
# Print linter name in the end of issue text.
# Default: true
print-linter-name: true
# Make issues output unique by line.
# Default: true
uniq-by-line: false
# Add a prefix to the output file references.
# Default: ""
path-prefix: ""
# Sort results by the order defined in `sort-order`.
# Default: false
sort-results: true
# Order to use when sorting results.
# Require `sort-results` to `true`.
# Possible values: `file`, `linter`, and `severity`.
#
# If the severity values are inside the following list, they are ordered in this order:
# 1. error
# 2. warning
# 3. high
# 4. medium
# 5. low
# Either they are sorted alphabetically.
#
# Default: ["file"]
sort-order:
- linter
- severity
- file # filepath, line, and column.
# Show statistics per linter.
# Default: false
show-stats: true
# Options for analysis running.
run:
# Timeout for analysis, e.g. 30s, 5m.
# Default: 1m
timeout: 5m
# Exit code when at least one issue was found.
# Default: 1
issues-exit-code: 2
# Include test files or not.
# Default: true
tests: false
# List of build tags, all linters use it.
# Default: []
build-tags:
- mytag
# If set, we pass it to "go list -mod={option}". From "go help modules":
# If invoked with -mod=readonly, the go command is disallowed from the implicit
# automatic updating of go.mod described above. Instead, it fails when any changes
# to go.mod are needed. This setting is most useful to check that go.mod does
# not need updates, such as in a continuous integration and testing system.
# If invoked with -mod=vendor, the go command assumes that the vendor
# directory holds the correct copies of dependencies and ignores
# the dependency descriptions in go.mod.
#
# Allowed values: readonly|vendor|mod
# Default: ""
modules-download-mode: readonly
# Allow multiple parallel golangci-lint instances running.
# If false, golangci-lint acquires file lock on start.
# Default: false
allow-parallel-runners: true
# Allow multiple golangci-lint instances running, but serialize them around a lock.
# If false, golangci-lint exits with an error if it fails to acquire file lock on start.
# Default: false
allow-serial-runners: true
# Define the Go version limit.
# Mainly related to generics support since go1.18.
# Default: use Go version from the go.mod file, fallback on the env var `GOVERSION`, fallback on 1.17
go: "1.22"
# Number of operating system threads (`GOMAXPROCS`) that can execute golangci-lint simultaneously.
# If it is explicitly set to 0 (i.e. not the default) then golangci-lint will automatically set the value to match Linux container CPU quota.
# Default: the number of logical CPUs in the machine
concurrency: 4

10
.vscode/settings.json vendored
View File

@ -1,8 +1,16 @@
{
"cSpell.words": [
"errcheck",
"eschao",
"gitea",
"golangci",
"gopkg",
"honnef",
"kisielk",
"sashamelentyev",
"staticcheck",
"stretchr",
"struct"
"struct",
"usestdlibvars"
]
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cli
package config
import (
"flag"
@ -22,8 +22,6 @@ import (
"strconv"
"strings"
"unsafe"
"git.mousesoft.ru/ms/config/utils"
)
// anyValue wraps a reflect.Value object and implements flag.Value interface
@ -70,25 +68,25 @@ func (av *anyValue) Set(v string) error {
case reflect.String:
av.any.SetString(v)
case reflect.Float32:
return utils.SetValueWithFloatX(av.any, v, 32)
return SetValueWithFloatX(av.any, v, 32)
case reflect.Float64:
return utils.SetValueWithFloatX(av.any, v, 64)
return SetValueWithFloatX(av.any, v, 64)
case reflect.Int8:
return utils.SetValueWithIntX(av.any, v, 8)
return SetValueWithIntX(av.any, v, 8)
case reflect.Int16:
return utils.SetValueWithIntX(av.any, v, 16)
return SetValueWithIntX(av.any, v, 16)
case reflect.Int, reflect.Int32:
return utils.SetValueWithIntX(av.any, v, 32)
return SetValueWithIntX(av.any, v, 32)
case reflect.Int64:
return utils.SetValueWithIntX(av.any, v, 64)
return SetValueWithIntX(av.any, v, 64)
case reflect.Uint8:
return utils.SetValueWithUintX(av.any, v, 8)
return SetValueWithUintX(av.any, v, 8)
case reflect.Uint16:
return utils.SetValueWithUintX(av.any, v, 16)
return SetValueWithUintX(av.any, v, 16)
case reflect.Uint, reflect.Uint32:
return utils.SetValueWithUintX(av.any, v, 32)
return SetValueWithUintX(av.any, v, 32)
case reflect.Uint64:
return utils.SetValueWithUintX(av.any, v, 64)
return SetValueWithUintX(av.any, v, 64)
default:
return fmt.Errorf("unsupported type: %s", kind.String())
}
@ -116,7 +114,7 @@ func (sv *sliceValue) Set(v string) error {
if sp == "" {
sp = ":"
}
return utils.SetValueWithSlice(sv.value, v, sp)
return SetValueWithSlice(sv.value, v, sp)
}
// errorHandling is a global flag.ErrorHandling
@ -137,9 +135,9 @@ type Command struct {
SubCommands map[string]*Command // sub-commands
}
// New creates a command with given name, the command will use default
// NewCLI creates a command with given name, the command will use default
// ErrorHandling: flag.ExitOnError and default usage function: flag.Usage
func New(name string) *Command {
func NewCLI(name string) *Command {
cmd := Command{
Name: name,
FlagSet: flag.NewFlagSet(name, errorHandling),

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cli
package config
import (
"flag"
@ -27,7 +27,7 @@ import (
func TestServiceCommand(t *testing.T) {
assert := assert.New(t)
serviceConfig := test.ServiceConfig{}
cmd := New("Service")
cmd := NewCLI("Service")
err := cmd.Init(&serviceConfig)
if err != nil {
t.Errorf("Can't init service command. %s", err.Error())
@ -65,7 +65,7 @@ func TestServiceCommand(t *testing.T) {
func TestLoginSubCommand(t *testing.T) {
assert := assert.New(t)
serviceConfig := test.ServiceConfig{Login: &test.LoginConfig{}}
cmd := New("Service")
cmd := NewCLI("Service")
assert.NoError(cmd.Init(&serviceConfig))
// assert login sub command
@ -78,7 +78,7 @@ func TestLoginSubCommand(t *testing.T) {
func TestLoginCommandWithValues(t *testing.T) {
assert := assert.New(t)
loginConfig := test.LoginConfig{}
cmd := New("Login")
cmd := NewCLI("Login")
assert.NoError(cmd.Init(&loginConfig), "Can't init login command")
username := "test-user"
@ -92,7 +92,7 @@ func TestLoginCommandWithValues(t *testing.T) {
func TestServiceCommandWithValues(t *testing.T) {
assert := assert.New(t)
serviceConfig := test.ServiceConfig{Login: &test.LoginConfig{}}
cmd := New("Service")
cmd := NewCLI("Service")
assert.NoError(cmd.Init(&serviceConfig))
serviceHost := "service-hostname"
@ -227,7 +227,7 @@ func TestVariousTypeCommand(t *testing.T) {
func TestCommandWithSlices(t *testing.T) {
assert := assert.New(t)
conf := test.SlicesConfig{}
cmd := New("Slice")
cmd := NewCLI("Slice")
assert.NoError(cmd.Init(&conf), "Can't init slice command")
paths := "/var:/home:/log"

View File

@ -23,9 +23,6 @@ import (
"path/filepath"
"reflect"
"git.mousesoft.ru/ms/config/cli"
"git.mousesoft.ru/ms/config/env"
"git.mousesoft.ru/ms/config/utils"
"gopkg.in/yaml.v3"
)
@ -89,35 +86,35 @@ func parseValue(v reflect.Value) error {
kind := valueOfField.Kind()
switch kind {
case reflect.Bool:
err = utils.SetValueWithBool(valueOfField, defValue)
err = SetValueWithBool(valueOfField, defValue)
case reflect.String:
valueOfField.SetString(defValue)
case reflect.Int8:
err = utils.SetValueWithIntX(valueOfField, defValue, 8)
err = SetValueWithIntX(valueOfField, defValue, 8)
case reflect.Int16:
err = utils.SetValueWithIntX(valueOfField, defValue, 16)
err = SetValueWithIntX(valueOfField, defValue, 16)
case reflect.Int, reflect.Int32:
err = utils.SetValueWithIntX(valueOfField, defValue, 32)
err = SetValueWithIntX(valueOfField, defValue, 32)
case reflect.Int64:
err = utils.SetValueWithIntX(valueOfField, defValue, 64)
err = SetValueWithIntX(valueOfField, defValue, 64)
case reflect.Uint8:
err = utils.SetValueWithUintX(valueOfField, defValue, 8)
err = SetValueWithUintX(valueOfField, defValue, 8)
case reflect.Uint16:
err = utils.SetValueWithUintX(valueOfField, defValue, 16)
err = SetValueWithUintX(valueOfField, defValue, 16)
case reflect.Uint, reflect.Uint32:
err = utils.SetValueWithUintX(valueOfField, defValue, 32)
err = SetValueWithUintX(valueOfField, defValue, 32)
case reflect.Uint64:
err = utils.SetValueWithUintX(valueOfField, defValue, 64)
err = SetValueWithUintX(valueOfField, defValue, 64)
case reflect.Float32:
err = utils.SetValueWithFloatX(valueOfField, defValue, 32)
err = SetValueWithFloatX(valueOfField, defValue, 32)
case reflect.Float64:
err = utils.SetValueWithFloatX(valueOfField, defValue, 64)
err = SetValueWithFloatX(valueOfField, defValue, 64)
case reflect.Slice:
sp, ok := structOfField.Tag.Lookup("separator")
if !ok {
sp = ":"
}
err = utils.SetValueWithSlice(valueOfField, defValue, sp)
err = SetValueWithSlice(valueOfField, defValue, sp)
default:
return fmt.Errorf("unsupported type: %s", kind.String())
@ -127,15 +124,9 @@ func parseValue(v reflect.Value) error {
return err
}
// ParseEnv parses given structure interface and set it with corresponding
// environment values
func ParseEnv(i interface{}) error {
return env.ParseWith(i, "")
}
// ParseCli parses given structure interface and set it with command line input
func ParseCli(i interface{}) error {
cli := cli.New(os.Args[0])
cli := NewCLI(os.Args[0])
if err := cli.Init(i); err != nil {
return err
}

View File

@ -26,21 +26,6 @@ import (
"github.com/stretchr/testify/assert"
)
const (
LOGIN_USER = "test-login-user"
LOGIN_PASSWORD = "test-login-passwd"
SERVICE_HOST = "test-service-host"
SERVICE_PORT = 8080
SERVICE_LOG_PATH = "/var/log/service"
SERVICE_LOG_LEVEL = "debug"
DB_HOST = "test-db-host"
DB_PORT = 9090
DB_USER = "test-db-user"
DB_PASSWORD = "test-db-password"
DB_LOG_PATH = "/var/log/db"
DB_LOG_LEVEL = "error"
)
func TestDefaultValueConfig(t *testing.T) {
conf := test.DefValueConfig{}
assert := assert.New(t)
@ -60,29 +45,41 @@ func TestDefaultValueConfig(t *testing.T) {
func TestEnvConfig(t *testing.T) {
dbLogPrefix := "LOG_"
os.Setenv("HOST", DB_HOST)
os.Setenv("PORT", strconv.Itoa(DB_PORT))
os.Setenv("USER", DB_USER)
os.Setenv("PASSWORD", DB_PASSWORD)
os.Setenv(dbLogPrefix+"PATH", DB_LOG_PATH)
os.Setenv(dbLogPrefix+"LEVEL", DB_LOG_LEVEL)
var err error
defer os.Unsetenv("HOST")
defer os.Unsetenv("PORT")
defer os.Unsetenv("USER")
defer os.Unsetenv("PASSWORD")
defer os.Unsetenv(dbLogPrefix + "PATH")
defer os.Unsetenv(dbLogPrefix + "LEVEL")
err = os.Setenv("HOST", DB_HOST)
assert.NoError(t, err)
err = os.Setenv("PORT", strconv.Itoa(DB_PORT))
assert.NoError(t, err)
err = os.Setenv("USER", DB_USER)
assert.NoError(t, err)
err = os.Setenv("PASSWORD", DB_PASSWORD)
assert.NoError(t, err)
err = os.Setenv(dbLogPrefix+"PATH", DB_LOG_PATH)
assert.NoError(t, err)
err = os.Setenv(dbLogPrefix+"LEVEL", DB_LOG_LEVEL)
assert.NoError(t, err)
defer func() { _ = os.Unsetenv("HOST") }()
defer func() { _ = os.Unsetenv("PORT") }()
defer func() { _ = os.Unsetenv("USER") }()
defer func() { _ = os.Unsetenv("PASSWORD") }()
defer func() { _ = os.Unsetenv(dbLogPrefix + "PATH") }()
defer func() { _ = os.Unsetenv(dbLogPrefix + "LEVEL") }()
conf := test.DBConfig{}
assert := assert.New(t)
assert.NoError(ParseEnv(&conf))
assert.Equal(DB_HOST, conf.Host)
assert.Equal(DB_PORT, conf.Port)
assert.Equal(DB_USER, conf.User)
assert.Equal(DB_PASSWORD, conf.Password)
assert.Equal(DB_LOG_PATH, conf.Log.Path)
assert.Equal(DB_LOG_LEVEL, conf.Log.Level)
assert.NoError(t, ParseEnv(&conf))
assert.Equal(t, DB_HOST, conf.Host)
assert.Equal(t, DB_PORT, conf.Port)
assert.Equal(t, DB_USER, conf.User)
assert.Equal(t, DB_PASSWORD, conf.Password)
assert.Equal(t, DB_LOG_PATH, conf.Log.Path)
assert.Equal(t, DB_LOG_LEVEL, conf.Log.Level)
}
func TestJSONConfigFile(t *testing.T) {

View File

@ -13,20 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package env
package config
import (
"fmt"
"os"
"reflect"
"git.mousesoft.ru/ms/config/utils"
)
// Parse parses given structure interface, extracts environment definitions
// from its tag and sets structure with defined environment variables
func Parse(i interface{}) error {
return ParseWith(i, "")
func ParseEnv(i interface{}) error {
return ParseEnvWith(i, "")
}
// ParseWith parses with given structure interface and environment name prefix
@ -43,7 +41,7 @@ func Parse(i interface{}) error {
//
// The Server.DB.Host will be mapped to environment variable: DB_HOST which is
// concatenated from DB tag in Server struct and Host tag in Database struct
func ParseWith(i interface{}, prefix string) error {
func ParseEnvWith(i interface{}, prefix string) error {
ptrRef := reflect.ValueOf(i)
if ptrRef.IsNil() || ptrRef.Kind() != reflect.Ptr {
@ -57,11 +55,11 @@ func ParseWith(i interface{}, prefix string) error {
valueOfStruct.Kind().String())
}
return parseValue(valueOfStruct, prefix)
return parseValueEnv(valueOfStruct, prefix)
}
// parseValue parses a reflect.Value object
func parseValue(v reflect.Value, prefix string) error {
func parseValueEnv(v reflect.Value, prefix string) error {
typeOfStruct := v.Type()
var err error
for i := 0; i < v.NumField() && err == nil; i++ {
@ -72,27 +70,27 @@ func parseValue(v reflect.Value, prefix string) error {
// recursively unmarshal if value is ptr type
if kindOfField == reflect.Ptr {
if !valueOfField.IsNil() && valueOfField.CanSet() {
err = ParseWith(valueOfField.Interface(),
err = ParseEnvWith(valueOfField.Interface(),
prefix+structOfField.Tag.Get("env"))
} else {
continue
}
} else if kindOfField == reflect.Struct {
err = parseValue(valueOfField, prefix+structOfField.Tag.Get("env"))
err = parseValueEnv(valueOfField, prefix+structOfField.Tag.Get("env"))
}
if err != nil {
return err
}
err = setFieldValue(valueOfField, structOfField, prefix)
err = setFieldValueEnv(valueOfField, structOfField, prefix)
}
return err
}
// setFieldValue sets a reflect.Value with environment value
func setFieldValue(v reflect.Value, f reflect.StructField, prefix string) error {
func setFieldValueEnv(v reflect.Value, f reflect.StructField, prefix string) error {
envName := f.Tag.Get("env")
if envName == "" {
return nil
@ -111,36 +109,36 @@ func setFieldValue(v reflect.Value, f reflect.StructField, prefix string) error
kind := v.Kind()
switch kind {
case reflect.Bool:
err = utils.SetValueWithBool(v, envValue)
err = SetValueWithBool(v, envValue)
case reflect.String:
v.SetString(envValue)
case reflect.Int8:
err = utils.SetValueWithIntX(v, envValue, 8)
err = SetValueWithIntX(v, envValue, 8)
case reflect.Int16:
err = utils.SetValueWithIntX(v, envValue, 16)
err = SetValueWithIntX(v, envValue, 16)
case reflect.Int, reflect.Int32:
err = utils.SetValueWithIntX(v, envValue, 32)
err = SetValueWithIntX(v, envValue, 32)
case reflect.Int64:
err = utils.SetValueWithIntX(v, envValue, 64)
err = SetValueWithIntX(v, envValue, 64)
case reflect.Uint8:
err = utils.SetValueWithUintX(v, envValue, 8)
err = SetValueWithUintX(v, envValue, 8)
case reflect.Uint16:
err = utils.SetValueWithUintX(v, envValue, 16)
err = SetValueWithUintX(v, envValue, 16)
case reflect.Uint, reflect.Uint32:
err = utils.SetValueWithUintX(v, envValue, 32)
err = SetValueWithUintX(v, envValue, 32)
case reflect.Uint64:
err = utils.SetValueWithUintX(v, envValue, 64)
err = SetValueWithUintX(v, envValue, 64)
case reflect.Float32:
err = utils.SetValueWithFloatX(v, envValue, 32)
err = SetValueWithFloatX(v, envValue, 32)
case reflect.Float64:
err = utils.SetValueWithFloatX(v, envValue, 64)
err = SetValueWithFloatX(v, envValue, 64)
case reflect.Slice:
sp, ok := f.Tag.Lookup("separator")
if !ok {
sp = ":"
}
err = utils.SetValueWithSlice(v, envValue, sp)
err = SetValueWithSlice(v, envValue, sp)
default:
return fmt.Errorf("unsupported type: %s", kind.String())

274
env/env_test.go vendored
View File

@ -1,274 +0,0 @@
/*
* Copyright (C) 2017 eschao <esc.chao@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package env
import (
"os"
"strconv"
"testing"
"git.mousesoft.ru/ms/config/test"
"github.com/stretchr/testify/assert"
)
const (
LOGIN_USER = "test-login-user"
LOGIN_PASSWORD = "test-login-passwd"
SERVICE_HOST = "test-service-host"
SERVICE_PORT = 8080
SERVICE_LOG_PATH = "/var/log/service"
SERVICE_LOG_LEVEL = "debug"
DB_HOST = "test-db-host"
DB_PORT = 9090
DB_USER = "test-db-user"
DB_PASSWORD = "test-db-password"
DB_LOG_PATH = "/var/log/db"
DB_LOG_LEVEL = "error"
)
func TestLoginConfigEnv(t *testing.T) {
os.Setenv("USER", LOGIN_USER)
os.Setenv("PASSWORD", LOGIN_PASSWORD)
defer os.Unsetenv("USER")
defer os.Unsetenv("PASSWORD")
assert := assert.New(t)
loginConfig := test.LoginConfig{}
assert.NoError(Parse(&loginConfig))
assert.Equal(LOGIN_USER, loginConfig.User)
assert.Equal(LOGIN_PASSWORD, loginConfig.Password)
}
func TestLoginConfigEnvWithPrefix(t *testing.T) {
os.Setenv("DB_USER", LOGIN_USER)
os.Setenv("DB_PASSWORD", LOGIN_PASSWORD)
defer os.Unsetenv("DB_USER")
defer os.Unsetenv("DB_PASSWORD")
assert := assert.New(t)
loginConfig := test.LoginConfig{}
assert.NoError(ParseWith(&loginConfig, "DB_"))
assert.Equal(LOGIN_USER, loginConfig.User)
assert.Equal(LOGIN_PASSWORD, loginConfig.Password)
}
func TestServiceConfigEnv(t *testing.T) {
servicePrefix := "CONFIG_TEST_SERVICE_"
serviceLogPrefix := servicePrefix + "LOG_"
dbPrefix := servicePrefix + "DB_"
dbLogPrefix := dbPrefix + "LOG_"
os.Setenv(servicePrefix+"HOST", SERVICE_HOST)
os.Setenv(servicePrefix+"PORT", strconv.Itoa(SERVICE_PORT))
os.Setenv(serviceLogPrefix+"PATH", SERVICE_LOG_PATH)
os.Setenv(serviceLogPrefix+"LEVEL", SERVICE_LOG_LEVEL)
os.Setenv(dbPrefix+"HOST", DB_HOST)
os.Setenv(dbPrefix+"PORT", strconv.Itoa(DB_PORT))
os.Setenv(dbPrefix+"USER", DB_USER)
os.Setenv(dbPrefix+"PASSWORD", DB_PASSWORD)
os.Setenv(dbLogPrefix+"PATH", DB_LOG_PATH)
os.Setenv(dbLogPrefix+"LEVEL", DB_LOG_LEVEL)
defer os.Unsetenv(servicePrefix + "HOST")
defer os.Unsetenv(servicePrefix + "PORT")
defer os.Unsetenv(serviceLogPrefix + "PATH")
defer os.Unsetenv(serviceLogPrefix + "LEVEL")
defer os.Unsetenv(dbPrefix + "HOST")
defer os.Unsetenv(dbPrefix + "PORT")
defer os.Unsetenv(dbPrefix + "USER")
defer os.Unsetenv(dbPrefix + "PASSWORD")
defer os.Unsetenv(dbLogPrefix + "PATH")
defer os.Unsetenv(dbLogPrefix + "LEVEL")
assert := assert.New(t)
serviceConfig := test.ServiceConfig{}
assert.NoError(Parse(&serviceConfig))
assert.Equal(SERVICE_HOST, serviceConfig.Host)
assert.Equal(SERVICE_PORT, serviceConfig.Port)
assert.Equal(SERVICE_LOG_PATH, serviceConfig.Log.Path)
assert.Equal(SERVICE_LOG_LEVEL, serviceConfig.Log.Level)
assert.Equal(DB_HOST, serviceConfig.DBConfig.Host)
assert.Equal(DB_PORT, serviceConfig.DBConfig.Port)
assert.Equal(DB_USER, serviceConfig.DBConfig.User)
assert.Equal(DB_PASSWORD, serviceConfig.DBConfig.Password)
assert.Equal(DB_LOG_PATH, serviceConfig.DBConfig.Log.Path)
assert.Equal(DB_LOG_LEVEL, serviceConfig.DBConfig.Log.Level)
}
func TestServiceLoginConfigEnv(t *testing.T) {
serviceLoginPrefix := "CONFIG_TEST_SERVICE_LOGIN_"
os.Setenv(serviceLoginPrefix+"USER", LOGIN_USER)
os.Setenv(serviceLoginPrefix+"PASSWORD", LOGIN_PASSWORD)
defer os.Unsetenv(serviceLoginPrefix + "USER")
defer os.Unsetenv(serviceLoginPrefix + "PASSWORD")
assert := assert.New(t)
serviceConfig := test.ServiceConfig{Login: &test.LoginConfig{}}
assert.NoError(Parse(&serviceConfig))
assert.Equal(LOGIN_USER, serviceConfig.Login.User)
assert.Equal(LOGIN_PASSWORD, serviceConfig.Login.Password)
}
func TestTypesConfigEnv(t *testing.T) {
typesPrefix := "CONFIG_TEST_"
os.Setenv(typesPrefix+"BOOL", "true")
os.Setenv(typesPrefix+"STR", "test-string")
os.Setenv(typesPrefix+"INT8", "100")
os.Setenv(typesPrefix+"INT16", "1000")
os.Setenv(typesPrefix+"INT", "10000")
os.Setenv(typesPrefix+"INT32", "100000")
os.Setenv(typesPrefix+"INT64", "1000000")
os.Setenv(typesPrefix+"UINT8", "200")
os.Setenv(typesPrefix+"UINT16", "2000")
os.Setenv(typesPrefix+"UINT", "20000")
os.Setenv(typesPrefix+"UINT32", "200000")
os.Setenv(typesPrefix+"UINT64", "2000000")
os.Setenv(typesPrefix+"FLOAT32", "1.234")
os.Setenv(typesPrefix+"FLOAT64", "2222.33333")
defer os.Unsetenv(typesPrefix + "BOOL")
defer os.Unsetenv(typesPrefix + "STR")
defer os.Unsetenv(typesPrefix + "INT8")
defer os.Unsetenv(typesPrefix + "INT16")
defer os.Unsetenv(typesPrefix + "INT")
defer os.Unsetenv(typesPrefix + "INT32")
defer os.Unsetenv(typesPrefix + "INT64")
defer os.Unsetenv(typesPrefix + "UINT8")
defer os.Unsetenv(typesPrefix + "UINT16")
defer os.Unsetenv(typesPrefix + "UINT")
defer os.Unsetenv(typesPrefix + "UINT32")
defer os.Unsetenv(typesPrefix + "UINT64")
defer os.Unsetenv(typesPrefix + "FLOAT32")
defer os.Unsetenv(typesPrefix + "FLOAT64")
assert := assert.New(t)
typesConfig := test.TypesConfig{}
assert.NoError(Parse(&typesConfig))
assert.Equal(true, typesConfig.BoolValue)
assert.Equal("test-string", typesConfig.StrValue)
assert.Equal(int8(100), typesConfig.Int8Value)
assert.Equal(int16(1000), typesConfig.Int16Value)
assert.Equal(10000, typesConfig.IntValue)
assert.Equal(int32(100000), typesConfig.Int32Value)
assert.Equal(int64(1000000), typesConfig.Int64Value)
assert.Equal(uint8(200), typesConfig.Uint8Value)
assert.Equal(uint16(2000), typesConfig.Uint16Value)
assert.Equal(uint(20000), typesConfig.UintValue)
assert.Equal(uint32(200000), typesConfig.Uint32Value)
assert.Equal(uint64(2000000), typesConfig.Uint64Value)
assert.Equal(float32(1.234), typesConfig.Float32Value)
assert.Equal(float64(2222.33333), typesConfig.Float64Value)
}
func TestTypesConfigWithErrorEnv(t *testing.T) {
assert := assert.New(t)
typesConfig := test.TypesConfig{}
typesPrefix := "CONFIG_TEST_"
os.Setenv(typesPrefix+"BOOL", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "BOOL")
os.Setenv(typesPrefix+"INT8", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "INT8")
os.Setenv(typesPrefix+"INT16", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "INT16")
os.Setenv(typesPrefix+"INT", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "INT")
os.Setenv(typesPrefix+"INT32", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "INT32")
os.Setenv(typesPrefix+"INT64", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "INT64")
os.Setenv(typesPrefix+"UINT8", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "UINT8")
os.Setenv(typesPrefix+"UINT16", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "UINT16")
os.Setenv(typesPrefix+"UINT", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "UINT")
os.Setenv(typesPrefix+"UINT32", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "UINT32")
os.Setenv(typesPrefix+"UINT64", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "UINT64")
os.Setenv(typesPrefix+"FLOAT32", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "FLOAT32")
os.Setenv(typesPrefix+"FLOAT64", "xxx")
assert.Error(Parse(&typesConfig))
os.Unsetenv(typesPrefix + "FLOAT64")
defer os.Unsetenv(typesPrefix + "BOOL")
defer os.Unsetenv(typesPrefix + "INT8")
defer os.Unsetenv(typesPrefix + "INT16")
defer os.Unsetenv(typesPrefix + "INT")
defer os.Unsetenv(typesPrefix + "INT32")
defer os.Unsetenv(typesPrefix + "INT64")
defer os.Unsetenv(typesPrefix + "UINT8")
defer os.Unsetenv(typesPrefix + "UINT16")
defer os.Unsetenv(typesPrefix + "UINT")
defer os.Unsetenv(typesPrefix + "UINT32")
defer os.Unsetenv(typesPrefix + "UINT64")
defer os.Unsetenv(typesPrefix + "FLOAT32")
defer os.Unsetenv(typesPrefix + "FLOAT64")
}
func TestSlicesConfigEnv(t *testing.T) {
prefix := "CONFIG_TEST_SLICES_"
os.Setenv(prefix+"PATHS", "/var:/usr:/home")
os.Setenv(prefix+"DEBUG", "/root;/log;/opt")
os.Setenv(prefix+"VALUES", "1,2,4,5")
defer os.Unsetenv(prefix + "PATHS")
defer os.Unsetenv(prefix + "DEBUG")
defer os.Unsetenv(prefix + "VALUES")
assert := assert.New(t)
conf := test.SlicesConfig{}
assert.NoError(Parse(&conf))
assert.Equal(3, len(conf.Paths))
assert.Equal("/var", conf.Paths[0])
assert.Equal("/usr", conf.Paths[1])
assert.Equal("/home", conf.Paths[2])
assert.Equal(3, len(conf.Debugs))
assert.Equal("/root", conf.Debugs[0])
assert.Equal("/log", conf.Debugs[1])
assert.Equal("/opt", conf.Debugs[2])
assert.Equal(4, len(conf.Values))
assert.Equal(1, conf.Values[0])
assert.Equal(2, conf.Values[1])
assert.Equal(4, conf.Values[2])
assert.Equal(5, conf.Values[3])
}

401
env_test.go Normal file
View File

@ -0,0 +1,401 @@
/*
* Copyright (C) 2017 eschao <esc.chao@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package config
import (
"os"
"strconv"
"testing"
"git.mousesoft.ru/ms/config/test"
"github.com/stretchr/testify/assert"
)
const (
LOGIN_USER = "test-login-user"
LOGIN_PASSWORD = "test-login-passwd"
SERVICE_HOST = "test-service-host"
SERVICE_PORT = 8080
SERVICE_LOG_PATH = "/var/log/service"
SERVICE_LOG_LEVEL = "debug"
DB_HOST = "test-db-host"
DB_PORT = 9090
DB_USER = "test-db-user"
DB_PASSWORD = "test-db-password"
DB_LOG_PATH = "/var/log/db"
DB_LOG_LEVEL = "error"
)
func TestLoginConfigEnv(t *testing.T) {
var err error
err = os.Setenv("USER", LOGIN_USER)
assert.NoError(t, err)
err = os.Setenv("PASSWORD", LOGIN_PASSWORD)
assert.NoError(t, err)
defer func() { _ = os.Unsetenv("USER") }()
defer func() { _ = os.Unsetenv("PASSWORD") }()
loginConfig := test.LoginConfig{}
assert.NoError(t, ParseEnv(&loginConfig))
assert.Equal(t, LOGIN_USER, loginConfig.User)
assert.Equal(t, LOGIN_PASSWORD, loginConfig.Password)
}
func TestLoginConfigEnvWithPrefix(t *testing.T) {
var err error
err = os.Setenv("DB_USER", LOGIN_USER)
assert.NoError(t, err)
err = os.Setenv("DB_PASSWORD", LOGIN_PASSWORD)
assert.NoError(t, err)
defer func() { _ = os.Unsetenv("DB_USER") }()
defer func() { _ = os.Unsetenv("DB_PASSWORD") }()
loginConfig := test.LoginConfig{}
assert.NoError(t, ParseEnvWith(&loginConfig, "DB_"))
assert.Equal(t, LOGIN_USER, loginConfig.User)
assert.Equal(t, LOGIN_PASSWORD, loginConfig.Password)
}
func TestServiceConfigEnv(t *testing.T) {
servicePrefix := "CONFIG_TEST_SERVICE_"
serviceLogPrefix := servicePrefix + "LOG_"
dbPrefix := servicePrefix + "DB_"
dbLogPrefix := dbPrefix + "LOG_"
err := os.Setenv(servicePrefix+"HOST", SERVICE_HOST)
assert.NoError(t, err)
err = os.Setenv(servicePrefix+"PORT", strconv.Itoa(SERVICE_PORT))
assert.NoError(t, err)
err = os.Setenv(serviceLogPrefix+"PATH", SERVICE_LOG_PATH)
assert.NoError(t, err)
err = os.Setenv(serviceLogPrefix+"LEVEL", SERVICE_LOG_LEVEL)
assert.NoError(t, err)
err = os.Setenv(dbPrefix+"HOST", DB_HOST)
assert.NoError(t, err)
err = os.Setenv(dbPrefix+"PORT", strconv.Itoa(DB_PORT))
assert.NoError(t, err)
err = os.Setenv(dbPrefix+"USER", DB_USER)
assert.NoError(t, err)
err = os.Setenv(dbPrefix+"PASSWORD", DB_PASSWORD)
assert.NoError(t, err)
err = os.Setenv(dbLogPrefix+"PATH", DB_LOG_PATH)
assert.NoError(t, err)
err = os.Setenv(dbLogPrefix+"LEVEL", DB_LOG_LEVEL)
assert.NoError(t, err)
defer func() { _ = os.Unsetenv(servicePrefix + "HOST") }()
defer func() { _ = os.Unsetenv(servicePrefix + "PORT") }()
defer func() { _ = os.Unsetenv(serviceLogPrefix + "PATH") }()
defer func() { _ = os.Unsetenv(serviceLogPrefix + "LEVEL") }()
defer func() { _ = os.Unsetenv(dbPrefix + "HOST") }()
defer func() { _ = os.Unsetenv(dbPrefix + "PORT") }()
defer func() { _ = os.Unsetenv(dbPrefix + "USER") }()
defer func() { _ = os.Unsetenv(dbPrefix + "PASSWORD") }()
defer func() { _ = os.Unsetenv(dbLogPrefix + "PATH") }()
defer func() { _ = os.Unsetenv(dbLogPrefix + "LEVEL") }()
serviceConfig := test.ServiceConfig{}
assert.NoError(t, ParseEnv(&serviceConfig))
assert.Equal(t, SERVICE_HOST, serviceConfig.Host)
assert.Equal(t, SERVICE_PORT, serviceConfig.Port)
assert.Equal(t, SERVICE_LOG_PATH, serviceConfig.Log.Path)
assert.Equal(t, SERVICE_LOG_LEVEL, serviceConfig.Log.Level)
assert.Equal(t, DB_HOST, serviceConfig.DBConfig.Host)
assert.Equal(t, DB_PORT, serviceConfig.DBConfig.Port)
assert.Equal(t, DB_USER, serviceConfig.DBConfig.User)
assert.Equal(t, DB_PASSWORD, serviceConfig.DBConfig.Password)
assert.Equal(t, DB_LOG_PATH, serviceConfig.DBConfig.Log.Path)
assert.Equal(t, DB_LOG_LEVEL, serviceConfig.DBConfig.Log.Level)
}
func TestServiceLoginConfigEnv(t *testing.T) {
var err error
serviceLoginPrefix := "CONFIG_TEST_SERVICE_LOGIN_"
err = os.Setenv(serviceLoginPrefix+"USER", LOGIN_USER)
assert.NoError(t, err)
err = os.Setenv(serviceLoginPrefix+"PASSWORD", LOGIN_PASSWORD)
assert.NoError(t, err)
defer func() { _ = os.Unsetenv(serviceLoginPrefix + "USER") }()
defer func() { _ = os.Unsetenv(serviceLoginPrefix + "PASSWORD") }()
serviceConfig := test.ServiceConfig{Login: &test.LoginConfig{}}
assert.NoError(t, ParseEnv(&serviceConfig))
assert.Equal(t, LOGIN_USER, serviceConfig.Login.User)
assert.Equal(t, LOGIN_PASSWORD, serviceConfig.Login.Password)
}
func TestTypesConfigEnv(t *testing.T) {
var err error
typesPrefix := "CONFIG_TEST_"
err = os.Setenv(typesPrefix+"BOOL", "true")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"STR", "test-string")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"INT8", "100")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"INT16", "1000")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"INT", "10000")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"INT32", "100000")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"INT64", "1000000")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"UINT8", "200")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"UINT16", "2000")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"UINT", "20000")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"UINT32", "200000")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"UINT64", "2000000")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"FLOAT32", "1.234")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"FLOAT64", "2222.33333")
assert.NoError(t, err)
defer func() { _ = os.Unsetenv(typesPrefix + "BOOL") }()
defer func() { _ = os.Unsetenv(typesPrefix + "STR") }()
defer func() { _ = os.Unsetenv(typesPrefix + "INT8") }()
defer func() { _ = os.Unsetenv(typesPrefix + "INT16") }()
defer func() { _ = os.Unsetenv(typesPrefix + "INT") }()
defer func() { _ = os.Unsetenv(typesPrefix + "INT32") }()
defer func() { _ = os.Unsetenv(typesPrefix + "INT64") }()
defer func() { _ = os.Unsetenv(typesPrefix + "UINT8") }()
defer func() { _ = os.Unsetenv(typesPrefix + "UINT16") }()
defer func() { _ = os.Unsetenv(typesPrefix + "UINT") }()
defer func() { _ = os.Unsetenv(typesPrefix + "UINT32") }()
defer func() { _ = os.Unsetenv(typesPrefix + "UINT64") }()
defer func() { _ = os.Unsetenv(typesPrefix + "FLOAT32") }()
defer func() { _ = os.Unsetenv(typesPrefix + "FLOAT64") }()
typesConfig := test.TypesConfig{}
err = ParseEnv(&typesConfig)
assert.NoError(t, err)
assert.True(t, typesConfig.BoolValue)
assert.Equal(t, "test-string", typesConfig.StrValue)
assert.Equal(t, int8(100), typesConfig.Int8Value)
assert.Equal(t, int16(1000), typesConfig.Int16Value)
assert.Equal(t, 10000, typesConfig.IntValue)
assert.Equal(t, int32(100000), typesConfig.Int32Value)
assert.Equal(t, int64(1000000), typesConfig.Int64Value)
assert.Equal(t, uint8(200), typesConfig.Uint8Value)
assert.Equal(t, uint16(2000), typesConfig.Uint16Value)
assert.Equal(t, uint(20000), typesConfig.UintValue)
assert.Equal(t, uint32(200000), typesConfig.Uint32Value)
assert.Equal(t, uint64(2000000), typesConfig.Uint64Value)
assert.Equal(t, float32(1.234), typesConfig.Float32Value)
assert.Equal(t, float64(2222.33333), typesConfig.Float64Value)
}
func TestTypesConfigWithErrorEnv(t *testing.T) {
var err error
typesConfig := test.TypesConfig{}
typesPrefix := "CONFIG_TEST_"
err = os.Setenv(typesPrefix+"BOOL", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "BOOL")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"INT8", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "INT8")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"INT16", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "INT16")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"INT", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "INT")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"INT32", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "INT32")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"INT64", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "INT64")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"UINT8", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "UINT8")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"UINT16", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "UINT16")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"UINT", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "UINT")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"UINT32", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "UINT32")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"UINT64", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "UINT64")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"FLOAT32", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "FLOAT32")
assert.NoError(t, err)
err = os.Setenv(typesPrefix+"FLOAT64", "xxx")
assert.NoError(t, err)
assert.Error(t, ParseEnv(&typesConfig))
err = os.Unsetenv(typesPrefix + "FLOAT64")
assert.NoError(t, err)
defer func() { _ = os.Unsetenv(typesPrefix + "BOOL") }()
defer func() { _ = os.Unsetenv(typesPrefix + "INT8") }()
defer func() { _ = os.Unsetenv(typesPrefix + "INT16") }()
defer func() { _ = os.Unsetenv(typesPrefix + "INT") }()
defer func() { _ = os.Unsetenv(typesPrefix + "INT32") }()
defer func() { _ = os.Unsetenv(typesPrefix + "INT64") }()
defer func() { _ = os.Unsetenv(typesPrefix + "UINT8") }()
defer func() { _ = os.Unsetenv(typesPrefix + "UINT16") }()
defer func() { _ = os.Unsetenv(typesPrefix + "UINT") }()
defer func() { _ = os.Unsetenv(typesPrefix + "UINT32") }()
defer func() { _ = os.Unsetenv(typesPrefix + "UINT64") }()
defer func() { _ = os.Unsetenv(typesPrefix + "FLOAT32") }()
defer func() { _ = os.Unsetenv(typesPrefix + "FLOAT64") }()
}
func TestSlicesConfigEnv(t *testing.T) {
var err error
prefix := "CONFIG_TEST_SLICES_"
err = os.Setenv(prefix+"PATHS", "/var:/usr:/home")
assert.NoError(t, err)
err = os.Setenv(prefix+"DEBUG", "/root;/log;/opt")
assert.NoError(t, err)
err = os.Setenv(prefix+"VALUES", "1,2,4,5")
assert.NoError(t, err)
defer func() { _ = os.Unsetenv(prefix + "PATHS") }()
defer func() { _ = os.Unsetenv(prefix + "DEBUG") }()
defer func() { _ = os.Unsetenv(prefix + "VALUES") }()
conf := test.SlicesConfig{}
assert.NoError(t, ParseEnv(&conf))
assert.Equal(t, 3, len(conf.Paths))
assert.Equal(t, "/var", conf.Paths[0])
assert.Equal(t, "/usr", conf.Paths[1])
assert.Equal(t, "/home", conf.Paths[2])
assert.Equal(t, 3, len(conf.Debugs))
assert.Equal(t, "/root", conf.Debugs[0])
assert.Equal(t, "/log", conf.Debugs[1])
assert.Equal(t, "/opt", conf.Debugs[2])
assert.Equal(t, 4, len(conf.Values))
assert.Equal(t, 1, conf.Values[0])
assert.Equal(t, 2, conf.Values[1])
assert.Equal(t, 4, conf.Values[2])
assert.Equal(t, 5, conf.Values[3])
}

110
makefile Normal file
View File

@ -0,0 +1,110 @@
# chess-record makefile
# =====================
SHELL := /usr/bin/env bash
PROJECT_ID := config
PROJECT_NAME ?= MouseSoft Config
BIN_SUFFIX :=
TMPDIR ?= $(CURDIR)/tmp
OUTDIR ?= $(CURDIR)/out
BINDIR ?= $(OUTDIR)/bin
VERSION ?= $(strip $(shell ./scripts/version.sh))
VERSION_NUMBER := $(strip $(shell ./scripts/version.sh number))
BUILD_ARCH ?= $(shell go env GOARCH)
BUILD_OS ?= $(shell go env GOOS)
GOCMD := go
GOTEST := $(GOCMD) test
GOVET := $(GOCMD) vet
ECHO_CMD := echo -e
GREEN := $(shell tput -Txterm setaf 2)
YELLOW := $(shell tput -Txterm setaf 3)
WHITE := $(shell tput -Txterm setaf 7)
CYAN := $(shell tput -Txterm setaf 6)
RESET := $(shell tput -Txterm sgr0)
.DEFAULT_GOAL := all
version: ## Version of the project to be built
@echo $(VERSION)
.PHONY:version
version-number: ## Version number of the project to be built
@echo $(VERSION_NUMBER)
.PHONY:version-number
## Build
all: clean vendor test ## Build binary
.PHONY:all
vendor: ## Copy of all packages needed to support builds and tests in the vendor directory
$(GOCMD) mod vendor
@$(ECHO_CMD) "Vendor\t\t${GREEN}[OK]${RESET}"
.PHONY:vendor
clean: ## Remove build related files
@rm -fr $(TMPDIR)
@rm -fr $(OUTDIR)
@$(ECHO_CMD) "Clean\t\t${GREEN}[OK]${RESET}"
.PHONY:clean
## Test
test: ## Run the tests of the project
ifeq ($(EXPORT_RESULT), true)
@mkdir -p $(OUTDIR)
$(eval OUTPUT_OPTIONS = | go-junit-report -set-exit-code > $(OUTDIR)/junit-report.xml)
endif
$(GOTEST) -v $(GO_OPT) ./... $(OUTPUT_OPTIONS)
@$(ECHO_CMD) "Test\t\t${GREEN}[OK]${RESET}"
.PHONY:test
coverage: ## Run the tests of the project and export the coverage report.
@mkdir -p out
$(GOTEST) -cover -covermode=count -coverprofile=$(OUTDIR)/profile.cov ./...
$(GOCMD) tool cover -func $(OUTDIR)/profile.cov
ifeq ($(EXPORT_RESULT), true)
ifeq ($(COVERAGE_FORMAT), html)
gocov convert $(OUTDIR)/profile.cov | gocov-html > $(OUTDIR)/coverage.html
else
gocov convert $(OUTDIR)/profile.cov | gocov-xml > $(OUTDIR)/coverage.xml
endif
endif
@$(ECHO_CMD) "Coverage\t${GREEN}[OK]${RESET}"
.PHONY:coverage
## Lint
lint: ## Run all available linters.
go vet ./...
errcheck ./...
staticcheck ./...
usestdlibvars ./...
shadow ./...
@$(ECHO_CMD) "Lint\t\t${GREEN}[OK]${RESET}"
.PHONY:lint
golangci-lint: ## Run golangci-lint linter
@golangci-lint run
@$(ECHO_CMD) "GolangCI Lint\t${GREEN}[OK]${RESET}"
.PHONY:golangci-lint
## Help
help: ## Show this help.
@$(ECHO_CMD) ''
@$(ECHO_CMD) 'Usage:'
@$(ECHO_CMD) ' ${YELLOW}make${RESET} ${GREEN}<target>${RESET}'
@$(ECHO_CMD) ''
@$(ECHO_CMD) 'Targets:'
@awk 'BEGIN {FS = ":.*?## "} { \
if (/^[a-zA-Z_-]+:.*?##.*$$/) {printf " ${YELLOW}%-20s${GREEN}%s${RESET}\n", $$1, $$2} \
else if (/^## .*$$/) {printf " ${CYAN}%s${RESET}\n", substr($$1,4)} \
}' $(MAKEFILE_LIST)
.PHONY:help

20
scripts/changes.awk Normal file
View File

@ -0,0 +1,20 @@
# Get changes of given version number.
{
while (index($0, "## [" version "]") <= 0) {
if (getline <= 0) {
exit
}
}
if (getline <= 0 ) {
exit
}
if (getline <= 0 ) {
exit
}
while (index($0, "## [") <= 0) {
print $0
if (getline <= 0) {
exit
}
}
}

22
scripts/version.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
if [ -z ${TAG_NAME+x} ]; then
if [ -z ${BRANCH_NAME+x} ]; then
BRANCH_NAME=$(echo $(git branch --show-current) || \
echo $(git name-rev --name-only HEAD))
fi
GIT_VERSION=$(echo ${BRANCH_NAME} | grep -q 'release/' \
&& echo ${BRANCH_NAME} | sed -e 's|release/|v|' -e 's/$/-RC/' || \
echo $(git describe --always --tags --dirty 2>/dev/null) || echo v0)
else
GIT_VERSION=${TAG_NAME}
fi
if [ -z ${VERSION+x} ]; then
VERSION=$(echo ${GIT_VERSION} | sed -e 's|^origin/||')
fi
if [ -z $1 ]; then
echo "${VERSION}"
else
echo ${VERSION} | sed -e 's/^v//'
fi

View File

@ -5,4 +5,3 @@ dbPassword: test-db-password
log:
path: /var/log/db
level: error

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utils
package config
import (
"fmt"
@ -97,7 +97,7 @@ func SetValueWithSlice(v reflect.Value, slice string, separator string) error {
case reflect.Float64:
err = SetValueWithFloatX(ele, data[i], 64)
default:
return fmt.Errorf("Can't support type: %s", kind.String())
return fmt.Errorf("unsupported type: %s", kind.String())
}
if err != nil {

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utils
package config
import (
"reflect"