diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml new file mode 100644 index 0000000..f059431 --- /dev/null +++ b/.gitea/workflows/test.yaml @@ -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 diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..a3c0985 --- /dev/null +++ b/.golangci.yaml @@ -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 diff --git a/.vscode/settings.json b/.vscode/settings.json index 248c1c5..ebf5a86 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,16 @@ { "cSpell.words": [ + "errcheck", "eschao", + "gitea", + "golangci", "gopkg", + "honnef", + "kisielk", + "sashamelentyev", + "staticcheck", "stretchr", - "struct" + "struct", + "usestdlibvars" ] } \ No newline at end of file diff --git a/cli/cli.go b/cli.go similarity index 91% rename from cli/cli.go rename to cli.go index 8fdb971..bcd3d96 100644 --- a/cli/cli.go +++ b/cli.go @@ -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), diff --git a/cli/cli_test.go b/cli_test.go similarity index 98% rename from cli/cli_test.go rename to cli_test.go index d5388d8..4b7ed01 100644 --- a/cli/cli_test.go +++ b/cli_test.go @@ -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" diff --git a/config.go b/config.go index 62f5700..e43bdbc 100644 --- a/config.go +++ b/config.go @@ -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 } diff --git a/config_test.go b/config_test.go index 28d1c7c..498b361 100644 --- a/config_test.go +++ b/config_test.go @@ -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) { diff --git a/env/env.go b/env.go similarity index 73% rename from env/env.go rename to env.go index af5b095..35d0423 100644 --- a/env/env.go +++ b/env.go @@ -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()) diff --git a/env/env_test.go b/env/env_test.go deleted file mode 100644 index 7e21772..0000000 --- a/env/env_test.go +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (C) 2017 eschao - * - * 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]) -} diff --git a/env_test.go b/env_test.go new file mode 100644 index 0000000..541ba3b --- /dev/null +++ b/env_test.go @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2017 eschao + * + * 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]) +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..76b032b --- /dev/null +++ b/makefile @@ -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}${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 diff --git a/scripts/changes.awk b/scripts/changes.awk new file mode 100644 index 0000000..4c8e834 --- /dev/null +++ b/scripts/changes.awk @@ -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 + } + } +} \ No newline at end of file diff --git a/scripts/version.sh b/scripts/version.sh new file mode 100755 index 0000000..7f922b2 --- /dev/null +++ b/scripts/version.sh @@ -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 diff --git a/test/config.yaml b/test/config.yaml index ca2d56f..31588d2 100644 --- a/test/config.yaml +++ b/test/config.yaml @@ -5,4 +5,3 @@ dbPassword: test-db-password log: path: /var/log/db level: error - diff --git a/utils/utils.go b/utils.go similarity index 97% rename from utils/utils.go rename to utils.go index bb6c490..e206eb6 100644 --- a/utils/utils.go +++ b/utils.go @@ -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 { diff --git a/utils/utils_test.go b/utils_test.go similarity index 99% rename from utils/utils_test.go rename to utils_test.go index c9d711c..5395107 100644 --- a/utils/utils_test.go +++ b/utils_test.go @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package utils +package config import ( "reflect"