Linting, CI, some small logic fixes.

This commit is contained in:
Stanislav Nikitin 2020-11-21 21:25:16 +05:00
parent 94234816c5
commit 3d8d3e478c
No known key found for this signature in database
GPG Key ID: 106900B32F8192EE
11 changed files with 252 additions and 91 deletions

10
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,10 @@
stages:
- test
lint:
stage: test
tags:
- docker
image: golangci/golangci-lint:v1.32.2
script:
- golangci-lint run

20
.golangci.yml Normal file
View File

@ -0,0 +1,20 @@
run:
deadline: 5m
linters:
enable-all: true
disable:
- dupl
- gochecknoglobals
- exhaustive
- testpackage
linters-settings:
lll:
line-length: 120
gocyclo:
min-complexity: 80
gocognit:
min-complexity: 100
funlen:
lines: 200
statements: 100

View File

@ -2,5 +2,6 @@
"go.testFlags": [ "go.testFlags": [
"-test.v", "-test.v",
"-cover" "-cover"
] ],
"go.inferGopath": false
} }

View File

@ -1,7 +1,6 @@
package sec package sec
import ( import (
// stdlib
"reflect" "reflect"
"strings" "strings"
) )
@ -30,6 +29,14 @@ func composeTree(value reflect.Value, prefix string) {
} }
if value.Kind() != reflect.Struct { if value.Kind() != reflect.Struct {
if value.Kind() == reflect.Map {
newElementPrefix := curPrefix
mapIter := value.MapRange()
for mapIter.Next() {
composeTree(mapIter.Value().Elem(), newElementPrefix+"_"+strings.ToUpper(mapIter.Key().String()))
}
} else {
f := &field{ f := &field{
Name: typeOf.Name(), Name: typeOf.Name(),
EnvVar: curPrefix + strings.ToUpper(typeOf.Name()), EnvVar: curPrefix + strings.ToUpper(typeOf.Name()),
@ -39,7 +46,8 @@ func composeTree(value reflect.Value, prefix string) {
parsedTree = append(parsedTree, f) parsedTree = append(parsedTree, f)
printDebug("Field data constructed: %+v", f) printDebug("Field data constructed (start): %+v", f)
}
return return
} }
@ -72,25 +80,35 @@ func composeTree(value reflect.Value, prefix string) {
fieldToProcess.Set(reflect.New(fieldToProcess.Type().Elem())) fieldToProcess.Set(reflect.New(fieldToProcess.Type().Elem()))
} else { } else {
printDebug("Field '%s' is unexported and will be ignored", fieldToProcessType.Name) printDebug("Field '%s' is unexported and will be ignored", fieldToProcessType.Name)
continue continue
} }
fieldToProcess = fieldToProcess.Elem() fieldToProcess = fieldToProcess.Elem()
} }
} }
printDebug("Field: '%s', type: %s (anonymous or embedded: %t)", fieldToProcessType.Name, fieldToProcess.Type().Kind().String(), fieldToProcessType.Anonymous) printDebug("Field: '%s', type: %s (anonymous or embedded: %t)",
fieldToProcessType.Name,
fieldToProcess.Type().Kind().String(),
fieldToProcessType.Anonymous,
)
// Dealing with embedded things. // Dealing with embedded things.
if fieldToProcessType.Anonymous { if fieldToProcessType.Anonymous {
// We should not allow anything other than struct. // We should not allow anything other than struct.
if fieldToProcess.Kind() != reflect.Struct { if fieldToProcess.Kind() != reflect.Struct {
printDebug("Field is embedded, but not a struct (%s), which cannot be used", fieldToProcess.Kind().String()) printDebug("Field is embedded, but not a struct (%s), which cannot be used", fieldToProcess.Kind().String())
continue continue
} }
} }
if fieldToProcess.Kind() != reflect.Struct && !fieldToProcess.CanSet() { if fieldToProcess.Kind() != reflect.Struct && !fieldToProcess.CanSet() {
printDebug("Field '%s' of type '%s' can't be set, skipping", fieldToProcessType.Name, fieldToProcess.Type().Kind().String()) printDebug("Field '%s' of type '%s' can't be set, skipping",
fieldToProcessType.Name,
fieldToProcess.Type().Kind().String())
continue continue
} }
@ -98,13 +116,15 @@ func composeTree(value reflect.Value, prefix string) {
// Hello, I'm recursion and I'm here to make you happy. // Hello, I'm recursion and I'm here to make you happy.
// I'll be launched only for structures to get their fields. // I'll be launched only for structures to get their fields.
if fieldToProcess.Kind() == reflect.Struct { switch fieldToProcess.Kind() {
case reflect.Struct:
newElementPrefix := curPrefix newElementPrefix := curPrefix
if !fieldToProcessType.Anonymous { if !fieldToProcessType.Anonymous {
newElementPrefix = strings.ToUpper(newElementPrefix + typeOf.Field(i).Name) newElementPrefix = strings.ToUpper(newElementPrefix + typeOf.Field(i).Name)
} }
composeTree(fieldToProcess, newElementPrefix) composeTree(fieldToProcess, newElementPrefix)
} else if fieldToProcess.Kind() == reflect.Map { case reflect.Map:
newElementPrefix := curPrefix newElementPrefix := curPrefix
if !fieldToProcessType.Anonymous { if !fieldToProcessType.Anonymous {
newElementPrefix = strings.ToUpper(newElementPrefix + typeOf.Field(i).Name) newElementPrefix = strings.ToUpper(newElementPrefix + typeOf.Field(i).Name)
@ -114,7 +134,7 @@ func composeTree(value reflect.Value, prefix string) {
for mapIter.Next() { for mapIter.Next() {
composeTree(mapIter.Value().Elem(), newElementPrefix+"_"+strings.ToUpper(mapIter.Key().String())) composeTree(mapIter.Value().Elem(), newElementPrefix+"_"+strings.ToUpper(mapIter.Key().String()))
} }
} else { default:
f := &field{ f := &field{
Name: typeOf.Field(i).Name, Name: typeOf.Field(i).Name,
EnvVar: curPrefix + strings.ToUpper(typeOf.Field(i).Name), EnvVar: curPrefix + strings.ToUpper(typeOf.Field(i).Name),
@ -124,7 +144,7 @@ func composeTree(value reflect.Value, prefix string) {
parsedTree = append(parsedTree, f) parsedTree = append(parsedTree, f)
printDebug("Field data constructed: %+v", f) printDebug("Field data constructed (end): %+v", f)
} }
} }
} }

View File

@ -1,7 +1,6 @@
package sec package sec
import ( import (
// stdlib
"reflect" "reflect"
) )
@ -18,5 +17,5 @@ type field struct {
Kind reflect.Kind Kind reflect.Kind
// Next variables are tag-related. // Next variables are tag-related.
optional bool // optional bool
} }

View File

@ -1,7 +1,6 @@
package sec package sec
import ( import (
// stdlib
"errors" "errors"
"reflect" "reflect"
"strconv" "strconv"
@ -15,10 +14,12 @@ func fillValue(element *field, data string) error {
val, err := strconv.ParseBool(data) val, err := strconv.ParseBool(data)
if err != nil { if err != nil {
printDebug("Error occurred while parsing boolean: %s", err.Error()) printDebug("Error occurred while parsing boolean: %s", err.Error())
if options.ErrorsAreCritical { if options.ErrorsAreCritical {
return errNotBool return errNotBool
} }
} }
element.Pointer.SetBool(val) element.Pointer.SetBool(val)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
// Bitsize 64 here specified for a reason - actual ints // Bitsize 64 here specified for a reason - actual ints
@ -27,10 +28,12 @@ func fillValue(element *field, data string) error {
val, err := strconv.ParseInt(data, 10, 64) val, err := strconv.ParseInt(data, 10, 64)
if err != nil { if err != nil {
printDebug("Error occurred while parsing int: %s", err.Error()) printDebug("Error occurred while parsing int: %s", err.Error())
if options.ErrorsAreCritical { if options.ErrorsAreCritical {
return errNotInt return errNotInt
} }
} }
switch element.Kind { switch element.Kind {
case reflect.Int8: case reflect.Int8:
// int8 is an integer in [-128...127] range. // int8 is an integer in [-128...127] range.
@ -75,14 +78,16 @@ func fillValue(element *field, data string) error {
val, err := strconv.ParseUint(data, 10, 64) val, err := strconv.ParseUint(data, 10, 64)
if err != nil { if err != nil {
printDebug("Error occurred while parsing unsigned integer: %s", err.Error()) printDebug("Error occurred while parsing unsigned integer: %s", err.Error())
if options.ErrorsAreCritical { if options.ErrorsAreCritical {
return errNotUint return errNotUint
} }
} }
switch element.Kind { switch element.Kind {
case reflect.Uint8: case reflect.Uint8:
// uint8 is an integer in [0...255] range. // uint8 is an integer in [0...255] range.
if val <= 255 { if val <= uint64(^uint8(0)) {
element.Pointer.SetUint(val) element.Pointer.SetUint(val)
} else { } else {
printDebug("Data in environment variable '%s' isn't uint8", element.EnvVar) printDebug("Data in environment variable '%s' isn't uint8", element.EnvVar)
@ -93,7 +98,7 @@ func fillValue(element *field, data string) error {
} }
case reflect.Uint16: case reflect.Uint16:
// uint16 is an integer in [0...65535] range. // uint16 is an integer in [0...65535] range.
if val <= 65535 { if val <= uint64(^uint16(0)) {
element.Pointer.SetUint(val) element.Pointer.SetUint(val)
} else { } else {
printDebug("Data in environment variable '%s' isn't uint16", element.EnvVar) printDebug("Data in environment variable '%s' isn't uint16", element.EnvVar)
@ -104,7 +109,7 @@ func fillValue(element *field, data string) error {
} }
case reflect.Uint32: case reflect.Uint32:
// uint32 is an integer in [0...4294967295] range. // uint32 is an integer in [0...4294967295] range.
if val <= 4294967295 { if val <= uint64(^uint32(0)) {
element.Pointer.SetUint(val) element.Pointer.SetUint(val)
} else { } else {
printDebug("Data in environment variable '%s' isn't uint32", element.EnvVar) printDebug("Data in environment variable '%s' isn't uint32", element.EnvVar)
@ -123,19 +128,25 @@ func fillValue(element *field, data string) error {
val, err := strconv.ParseFloat(data, 64) val, err := strconv.ParseFloat(data, 64)
if err != nil { if err != nil {
printDebug("Error occurred while parsing float: %s", err.Error()) printDebug("Error occurred while parsing float: %s", err.Error())
if options.ErrorsAreCritical { if options.ErrorsAreCritical {
return errNotFloat return errNotFloat
} }
} }
element.Pointer.SetFloat(val) element.Pointer.SetFloat(val)
case reflect.Interface: case reflect.Interface:
// We should not attempt to work with data in interface{} // We should not attempt to work with data in interface{}
// unless it is a pointer to value. // unless it is a pointer to value.
if element.Pointer.Elem().Kind() != reflect.Ptr { if element.Pointer.Elem().Kind() != reflect.Ptr {
printDebug("Element for environment variable '%s' isn't a pointer and put into interface{}. Nothing will be done with this element.", element.EnvVar) printDebug("Element for environment variable '%s' isn't a pointer and put "+
"into interface{}. Nothing will be done with this element.",
element.EnvVar)
if options.ErrorsAreCritical { if options.ErrorsAreCritical {
return errors.New("element for environment variable '" + element.EnvVar + "' isn't a pointer and put into interface") return errors.New("element for environment variable '" + element.EnvVar + "' isn't a pointer and put into interface")
} }
return nil return nil
} }
@ -143,7 +154,9 @@ func fillValue(element *field, data string) error {
// It goes interface{} -> ptr -> real element. // It goes interface{} -> ptr -> real element.
element.Pointer = element.Pointer.Elem().Elem() element.Pointer = element.Pointer.Elem().Elem()
element.Kind = element.Pointer.Kind() element.Kind = element.Pointer.Kind()
return fillValue(element, data) return fillValue(element, data)
} }
return nil return nil
} }

View File

@ -11,8 +11,6 @@ type Options struct {
ErrorsAreCritical bool ErrorsAreCritical bool
} }
var ( var defaultOptions = &Options{
defaultOptions = &Options{
ErrorsAreCritical: false, ErrorsAreCritical: false,
} }
)

View File

@ -1,7 +1,6 @@
package sec package sec
import ( import (
// stdlib
"errors" "errors"
"os" "os"
) )
@ -32,9 +31,11 @@ func parseEnv() error {
for _, element := range parsedTree { for _, element := range parsedTree {
printDebug("Processing element '%s'", element.EnvVar) printDebug("Processing element '%s'", element.EnvVar)
data, found := os.LookupEnv(element.EnvVar) data, found := os.LookupEnv(element.EnvVar)
if !found { if !found {
printDebug("Value for '%s' environment variable wasn't found", element.EnvVar) printDebug("Value for '%s' environment variable wasn't found", element.EnvVar)
continue continue
} else { } else {
printDebug("Value for '%s' will be: %s", element.EnvVar, data) printDebug("Value for '%s' will be: %s", element.EnvVar, data)

View File

@ -1,12 +1,10 @@
package sec package sec
import ( import (
// stdlib
"os" "os"
"strconv" "strconv"
"testing" "testing"
// other
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -18,10 +16,12 @@ func TestParseString(t *testing.T) {
os.Setenv("STRINGDATA", "test") os.Setenv("STRINGDATA", "test")
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
} }
@ -56,10 +56,12 @@ func TestParseBoolean(t *testing.T) {
// If ErrorsAreCritical == false, then we should check only // If ErrorsAreCritical == false, then we should check only
// equality of parsed data and valid data. // equality of parsed data and valid data.
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, testCase.ValidData, s.BoolData) require.Equal(t, testCase.ValidData, s.BoolData)
@ -69,6 +71,7 @@ func TestParseBoolean(t *testing.T) {
err1 := Parse(s1, &Options{ErrorsAreCritical: true}) err1 := Parse(s1, &Options{ErrorsAreCritical: true})
var checkNotBoolError bool var checkNotBoolError bool
_, err2 := strconv.ParseBool(testCase.TestData) _, err2 := strconv.ParseBool(testCase.TestData)
if err2 != nil { if err2 != nil {
checkNotBoolError = true checkNotBoolError = true
@ -78,6 +81,7 @@ func TestParseBoolean(t *testing.T) {
if err1 == nil { if err1 == nil {
t.Log("No error returned!") t.Log("No error returned!")
} }
require.NotNil(t, err1) require.NotNil(t, err1)
require.Equal(t, errNotBool, err1) require.Equal(t, errNotBool, err1)
} }
@ -112,10 +116,12 @@ func TestParseInt8(t *testing.T) {
// If ErrorsAreCritical == false, then we should check only // If ErrorsAreCritical == false, then we should check only
// equality of parsed data and valid data. // equality of parsed data and valid data.
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, testCase.ValidData, s.IntData) require.Equal(t, testCase.ValidData, s.IntData)
@ -124,12 +130,16 @@ func TestParseInt8(t *testing.T) {
s1 := &testStruct{} s1 := &testStruct{}
err1 := Parse(s1, &Options{ErrorsAreCritical: true}) err1 := Parse(s1, &Options{ErrorsAreCritical: true})
var checkNotIntError bool var (
var checkRangeError bool checkNotIntError bool
checkRangeError bool
)
passedData, err2 := strconv.ParseInt(testCase.TestData, 10, 64) passedData, err2 := strconv.ParseInt(testCase.TestData, 10, 64)
if err2 != nil { if err2 != nil {
checkNotIntError = true checkNotIntError = true
} }
if passedData != int64(testCase.ValidData) { if passedData != int64(testCase.ValidData) {
checkRangeError = true checkRangeError = true
} }
@ -138,11 +148,13 @@ func TestParseInt8(t *testing.T) {
if err1 == nil { if err1 == nil {
t.Log("No error returned!") t.Log("No error returned!")
} }
require.NotNil(t, err1) require.NotNil(t, err1)
if checkNotIntError { if checkNotIntError {
require.Equal(t, errNotInt, err1) require.Equal(t, errNotInt, err1)
} }
if checkRangeError { if checkRangeError {
require.Equal(t, errNotInt8, err1) require.Equal(t, errNotInt8, err1)
} }
@ -178,10 +190,12 @@ func TestParseInt16(t *testing.T) {
// If ErrorsAreCritical == false, then we should check only // If ErrorsAreCritical == false, then we should check only
// equality of parsed data and valid data. // equality of parsed data and valid data.
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, testCase.ValidData, s.IntData) require.Equal(t, testCase.ValidData, s.IntData)
@ -190,12 +204,16 @@ func TestParseInt16(t *testing.T) {
s1 := &testStruct{} s1 := &testStruct{}
err1 := Parse(s1, &Options{ErrorsAreCritical: true}) err1 := Parse(s1, &Options{ErrorsAreCritical: true})
var checkNotIntError bool var (
var checkRangeError bool checkNotIntError bool
checkRangeError bool
)
passedData, err2 := strconv.ParseInt(testCase.TestData, 10, 64) passedData, err2 := strconv.ParseInt(testCase.TestData, 10, 64)
if err2 != nil { if err2 != nil {
checkNotIntError = true checkNotIntError = true
} }
if passedData != int64(testCase.ValidData) { if passedData != int64(testCase.ValidData) {
checkRangeError = true checkRangeError = true
} }
@ -204,11 +222,13 @@ func TestParseInt16(t *testing.T) {
if err1 == nil { if err1 == nil {
t.Log("No error returned!") t.Log("No error returned!")
} }
require.NotNil(t, err1) require.NotNil(t, err1)
if checkNotIntError { if checkNotIntError {
require.Equal(t, errNotInt, err1) require.Equal(t, errNotInt, err1)
} }
if checkRangeError { if checkRangeError {
require.Equal(t, errNotInt16, err1) require.Equal(t, errNotInt16, err1)
} }
@ -244,10 +264,12 @@ func TestParseInt32(t *testing.T) {
// If ErrorsAreCritical == false, then we should check only // If ErrorsAreCritical == false, then we should check only
// equality of parsed data and valid data. // equality of parsed data and valid data.
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, testCase.ValidData, s.IntData) require.Equal(t, testCase.ValidData, s.IntData)
@ -256,12 +278,16 @@ func TestParseInt32(t *testing.T) {
s1 := &testStruct{} s1 := &testStruct{}
err1 := Parse(s1, &Options{ErrorsAreCritical: true}) err1 := Parse(s1, &Options{ErrorsAreCritical: true})
var checkNotIntError bool var (
var checkRangeError bool checkNotIntError bool
checkRangeError bool
)
passedData, err2 := strconv.ParseInt(testCase.TestData, 10, 64) passedData, err2 := strconv.ParseInt(testCase.TestData, 10, 64)
if err2 != nil { if err2 != nil {
checkNotIntError = true checkNotIntError = true
} }
if passedData != int64(testCase.ValidData) { if passedData != int64(testCase.ValidData) {
checkRangeError = true checkRangeError = true
} }
@ -270,11 +296,13 @@ func TestParseInt32(t *testing.T) {
if err1 == nil { if err1 == nil {
t.Log("No error returned!") t.Log("No error returned!")
} }
require.NotNil(t, err1) require.NotNil(t, err1)
if checkNotIntError { if checkNotIntError {
require.Equal(t, errNotInt, err1) require.Equal(t, errNotInt, err1)
} }
if checkRangeError { if checkRangeError {
require.Equal(t, errNotInt32, err1) require.Equal(t, errNotInt32, err1)
} }
@ -310,10 +338,12 @@ func TestParseInt64(t *testing.T) {
// If ErrorsAreCritical == false, then we should check only // If ErrorsAreCritical == false, then we should check only
// equality of parsed data and valid data. // equality of parsed data and valid data.
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, testCase.ValidData, s.IntData) require.Equal(t, testCase.ValidData, s.IntData)
@ -322,12 +352,16 @@ func TestParseInt64(t *testing.T) {
s1 := &testStruct{} s1 := &testStruct{}
err1 := Parse(s1, &Options{ErrorsAreCritical: true}) err1 := Parse(s1, &Options{ErrorsAreCritical: true})
var checkNotIntError bool var (
var checkRangeError bool checkNotIntError bool
checkRangeError bool
)
passedData, err2 := strconv.ParseInt(testCase.TestData, 10, 64) passedData, err2 := strconv.ParseInt(testCase.TestData, 10, 64)
if err2 != nil { if err2 != nil {
checkNotIntError = true checkNotIntError = true
} }
if passedData != testCase.ValidData { if passedData != testCase.ValidData {
checkRangeError = true checkRangeError = true
} }
@ -336,11 +370,13 @@ func TestParseInt64(t *testing.T) {
if err1 == nil { if err1 == nil {
t.Log("No error returned!") t.Log("No error returned!")
} }
require.NotNil(t, err1) require.NotNil(t, err1)
if checkNotIntError { if checkNotIntError {
require.Equal(t, errNotInt, err1) require.Equal(t, errNotInt, err1)
} }
if checkRangeError { if checkRangeError {
require.Equal(t, errNotInt64, err1) require.Equal(t, errNotInt64, err1)
} }
@ -375,10 +411,12 @@ func TestParseUint8(t *testing.T) {
// If ErrorsAreCritical == false, then we should check only // If ErrorsAreCritical == false, then we should check only
// equality of parsed data and valid data. // equality of parsed data and valid data.
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, testCase.ValidData, s.UintData) require.Equal(t, testCase.ValidData, s.UintData)
@ -387,12 +425,16 @@ func TestParseUint8(t *testing.T) {
s1 := &testStruct{} s1 := &testStruct{}
err1 := Parse(s1, &Options{ErrorsAreCritical: true}) err1 := Parse(s1, &Options{ErrorsAreCritical: true})
var checkNotIntError bool var (
var checkRangeError bool checkNotIntError bool
checkRangeError bool
)
passedData, err2 := strconv.ParseUint(testCase.TestData, 10, 64) passedData, err2 := strconv.ParseUint(testCase.TestData, 10, 64)
if err2 != nil { if err2 != nil {
checkNotIntError = true checkNotIntError = true
} }
if passedData != uint64(testCase.ValidData) { if passedData != uint64(testCase.ValidData) {
checkRangeError = true checkRangeError = true
} }
@ -401,11 +443,13 @@ func TestParseUint8(t *testing.T) {
if err1 == nil { if err1 == nil {
t.Log("No error returned!") t.Log("No error returned!")
} }
require.NotNil(t, err1) require.NotNil(t, err1)
if checkNotIntError { if checkNotIntError {
require.Equal(t, errNotUint, err1) require.Equal(t, errNotUint, err1)
} }
if checkRangeError { if checkRangeError {
require.Equal(t, errNotUint8, err1) require.Equal(t, errNotUint8, err1)
} }
@ -440,10 +484,12 @@ func TestParseUint16(t *testing.T) {
// If ErrorsAreCritical == false, then we should check only // If ErrorsAreCritical == false, then we should check only
// equality of parsed data and valid data. // equality of parsed data and valid data.
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, testCase.ValidData, s.UintData) require.Equal(t, testCase.ValidData, s.UintData)
@ -452,12 +498,16 @@ func TestParseUint16(t *testing.T) {
s1 := &testStruct{} s1 := &testStruct{}
err1 := Parse(s1, &Options{ErrorsAreCritical: true}) err1 := Parse(s1, &Options{ErrorsAreCritical: true})
var checkNotIntError bool var (
var checkRangeError bool checkNotIntError bool
checkRangeError bool
)
passedData, err2 := strconv.ParseUint(testCase.TestData, 10, 64) passedData, err2 := strconv.ParseUint(testCase.TestData, 10, 64)
if err2 != nil { if err2 != nil {
checkNotIntError = true checkNotIntError = true
} }
if passedData != uint64(testCase.ValidData) { if passedData != uint64(testCase.ValidData) {
checkRangeError = true checkRangeError = true
} }
@ -466,11 +516,13 @@ func TestParseUint16(t *testing.T) {
if err1 == nil { if err1 == nil {
t.Log("No error returned!") t.Log("No error returned!")
} }
require.NotNil(t, err1) require.NotNil(t, err1)
if checkNotIntError { if checkNotIntError {
require.Equal(t, errNotUint, err1) require.Equal(t, errNotUint, err1)
} }
if checkRangeError { if checkRangeError {
require.Equal(t, errNotUint16, err1) require.Equal(t, errNotUint16, err1)
} }
@ -505,10 +557,12 @@ func TestParseUint32(t *testing.T) {
// If ErrorsAreCritical == false, then we should check only // If ErrorsAreCritical == false, then we should check only
// equality of parsed data and valid data. // equality of parsed data and valid data.
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, testCase.ValidData, s.UintData) require.Equal(t, testCase.ValidData, s.UintData)
@ -517,12 +571,16 @@ func TestParseUint32(t *testing.T) {
s1 := &testStruct{} s1 := &testStruct{}
err1 := Parse(s1, &Options{ErrorsAreCritical: true}) err1 := Parse(s1, &Options{ErrorsAreCritical: true})
var checkNotIntError bool var (
var checkRangeError bool checkNotIntError bool
checkRangeError bool
)
passedData, err2 := strconv.ParseUint(testCase.TestData, 10, 64) passedData, err2 := strconv.ParseUint(testCase.TestData, 10, 64)
if err2 != nil { if err2 != nil {
checkNotIntError = true checkNotIntError = true
} }
if passedData != uint64(testCase.ValidData) { if passedData != uint64(testCase.ValidData) {
checkRangeError = true checkRangeError = true
} }
@ -531,11 +589,13 @@ func TestParseUint32(t *testing.T) {
if err1 == nil { if err1 == nil {
t.Log("No error returned!") t.Log("No error returned!")
} }
require.NotNil(t, err1) require.NotNil(t, err1)
if checkNotIntError { if checkNotIntError {
require.Equal(t, errNotUint, err1) require.Equal(t, errNotUint, err1)
} }
if checkRangeError { if checkRangeError {
require.Equal(t, errNotUint32, err1) require.Equal(t, errNotUint32, err1)
} }
@ -570,10 +630,12 @@ func TestParseUint64(t *testing.T) {
// If ErrorsAreCritical == false, then we should check only // If ErrorsAreCritical == false, then we should check only
// equality of parsed data and valid data. // equality of parsed data and valid data.
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, testCase.ValidData, s.UintData) require.Equal(t, testCase.ValidData, s.UintData)
@ -582,12 +644,16 @@ func TestParseUint64(t *testing.T) {
s1 := &testStruct{} s1 := &testStruct{}
err1 := Parse(s1, &Options{ErrorsAreCritical: true}) err1 := Parse(s1, &Options{ErrorsAreCritical: true})
var checkNotIntError bool var (
var checkRangeError bool checkNotIntError bool
checkRangeError bool
)
passedData, err2 := strconv.ParseUint(testCase.TestData, 10, 64) passedData, err2 := strconv.ParseUint(testCase.TestData, 10, 64)
if err2 != nil { if err2 != nil {
checkNotIntError = true checkNotIntError = true
} }
if passedData != testCase.ValidData { if passedData != testCase.ValidData {
checkRangeError = true checkRangeError = true
} }
@ -596,11 +662,13 @@ func TestParseUint64(t *testing.T) {
if err1 == nil { if err1 == nil {
t.Log("No error returned!") t.Log("No error returned!")
} }
require.NotNil(t, err1) require.NotNil(t, err1)
if checkNotIntError { if checkNotIntError {
require.Equal(t, errNotUint, err1) require.Equal(t, errNotUint, err1)
} }
if checkRangeError { if checkRangeError {
require.Equal(t, errNotUint64, err1) require.Equal(t, errNotUint64, err1)
} }
@ -634,10 +702,12 @@ func TestParseFloat32(t *testing.T) {
// If ErrorsAreCritical == false, then we should check only // If ErrorsAreCritical == false, then we should check only
// equality of parsed data and valid data. // equality of parsed data and valid data.
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, testCase.ValidData, s.FloatData) require.Equal(t, testCase.ValidData, s.FloatData)
@ -646,12 +716,16 @@ func TestParseFloat32(t *testing.T) {
s1 := &testStruct{} s1 := &testStruct{}
err1 := Parse(s1, &Options{ErrorsAreCritical: true}) err1 := Parse(s1, &Options{ErrorsAreCritical: true})
var checkNotIntError bool var (
var checkRangeError bool checkNotIntError bool
checkRangeError bool
)
passedData, err2 := strconv.ParseFloat(testCase.TestData, 64) passedData, err2 := strconv.ParseFloat(testCase.TestData, 64)
if err2 != nil { if err2 != nil {
checkNotIntError = true checkNotIntError = true
} }
if passedData != float64(testCase.ValidData) { if passedData != float64(testCase.ValidData) {
checkRangeError = true checkRangeError = true
} }
@ -660,11 +734,13 @@ func TestParseFloat32(t *testing.T) {
if err1 == nil { if err1 == nil {
t.Log("No error returned!") t.Log("No error returned!")
} }
require.NotNil(t, err1) require.NotNil(t, err1)
if checkNotIntError { if checkNotIntError {
require.Equal(t, errNotFloat, err1) require.Equal(t, errNotFloat, err1)
} }
if checkRangeError { if checkRangeError {
require.Equal(t, errNotFloat32, err1) require.Equal(t, errNotFloat32, err1)
} }
@ -697,10 +773,12 @@ func TestParseFloat64(t *testing.T) {
// If ErrorsAreCritical == false, then we should check only // If ErrorsAreCritical == false, then we should check only
// equality of parsed data and valid data. // equality of parsed data and valid data.
s := &testStruct{} s := &testStruct{}
err := Parse(s, nil) err := Parse(s, nil)
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, testCase.ValidData, s.FloatData) require.Equal(t, testCase.ValidData, s.FloatData)
@ -709,12 +787,16 @@ func TestParseFloat64(t *testing.T) {
s1 := &testStruct{} s1 := &testStruct{}
err1 := Parse(s1, &Options{ErrorsAreCritical: true}) err1 := Parse(s1, &Options{ErrorsAreCritical: true})
var checkNotIntError bool var (
var checkRangeError bool checkNotIntError bool
checkRangeError bool
)
passedData, err2 := strconv.ParseFloat(testCase.TestData, 64) passedData, err2 := strconv.ParseFloat(testCase.TestData, 64)
if err2 != nil { if err2 != nil {
checkNotIntError = true checkNotIntError = true
} }
if passedData != testCase.ValidData { if passedData != testCase.ValidData {
checkRangeError = true checkRangeError = true
} }
@ -723,11 +805,13 @@ func TestParseFloat64(t *testing.T) {
if err1 == nil { if err1 == nil {
t.Log("No error returned!") t.Log("No error returned!")
} }
require.NotNil(t, err1) require.NotNil(t, err1)
if checkNotIntError { if checkNotIntError {
require.Equal(t, errNotFloat, err1) require.Equal(t, errNotFloat, err1)
} }
if checkRangeError { if checkRangeError {
require.Equal(t, errNotFloat64, err1) require.Equal(t, errNotFloat64, err1)
} }
@ -772,6 +856,7 @@ func TestParseStructWitStructAsInterface(t *testing.T) {
Data interface{} Data interface{}
Int int Int int
} }
type testUnderlyingStruct struct { type testUnderlyingStruct struct {
Data string Data string
} }

5
sec.go
View File

@ -1,7 +1,6 @@
package sec package sec
import ( import (
// stdlib
"errors" "errors"
"log" "log"
"os" "os"
@ -38,9 +37,11 @@ func Parse(structure interface{}, config *Options) error {
debugFlagRaw, found := os.LookupEnv(debugFlagEnvName) debugFlagRaw, found := os.LookupEnv(debugFlagEnvName)
if found { if found {
var err error var err error
debug, err = strconv.ParseBool(debugFlagRaw) debug, err = strconv.ParseBool(debugFlagRaw)
if err != nil { if err != nil {
log.Println("Invalid '" + debugFlagEnvName + "' environment variable data: '" + debugFlagRaw + "'. Error: " + err.Error()) log.Printf("Invalid '%s' environment variable data: '%s'. Error: %s", debugFlagEnvName, debugFlagRaw, err.Error())
if options.ErrorsAreCritical { if options.ErrorsAreCritical {
return err return err
} }

View File

@ -1,12 +1,10 @@
package sec package sec
import ( import (
// stdlib
"os" "os"
"strconv" "strconv"
"testing" "testing"
// other
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -26,18 +24,23 @@ var (
) )
type testDatas struct { type testDatas struct {
TestString string
TestInt8 int8
TestInt16 int16
TestInt32 int32
TestInt64 int64
TestUint8 uint8
TestUint16 uint16
TestUint32 uint32
TestUint64 uint64
TestFloat32 float32
TestFloat64 float64 TestFloat64 float64
TestUint64 uint64
TestInt64 int64
TestFloat32 float32
TestUint32 uint32
TestInt32 int32
TestUint16 uint16
TestInt16 int16
TestUint8 uint8
TestInt8 int8
TestBool bool TestBool bool
TestString string
} }
type testStringType string type testStringType string
@ -46,32 +49,42 @@ type testStruct1 struct {
testDatas testDatas
testStringType testStringType
TestNestAnonymous struct { TestNestAnonymous struct {
TestString string
TestInt8 int8
TestInt16 int16
TestInt32 int32
TestInt64 int64
TestUint8 uint8
TestUint16 uint16
TestUint32 uint32
TestUint64 uint64
TestFloat32 float32
TestFloat64 float64 TestFloat64 float64
TestUint64 uint64
TestInt64 int64
TestFloat32 float32
TestUint32 uint32
TestInt32 int32
TestUint16 uint16
TestInt16 int16
TestUint8 uint8
TestInt8 int8
TestBool bool TestBool bool
TestString string
} }
TestNestAnonymousPointer *struct { TestNestAnonymousPointer *struct {
TestString string
TestInt8 int8
TestInt16 int16
TestInt32 int32
TestInt64 int64
TestUint8 uint8
TestUint16 uint16
TestUint32 uint32
TestUint64 uint64
TestFloat32 float32
TestFloat64 float64 TestFloat64 float64
TestUint64 uint64
TestInt64 int64
TestFloat32 float32
TestUint32 uint32
TestInt32 int32
TestUint16 uint16
TestInt16 int16
TestUint8 uint8
TestInt8 int8
TestBool bool TestBool bool
TestString string
} }
TestNestPointer *testDatas TestNestPointer *testDatas
TestNest testDatas TestNest testDatas
@ -90,7 +103,7 @@ func setenv(prefix string) {
os.Setenv(prefix+"TESTINT8", strconv.FormatInt(int64(testInt8), 10)) os.Setenv(prefix+"TESTINT8", strconv.FormatInt(int64(testInt8), 10))
os.Setenv(prefix+"TESTINT16", strconv.FormatInt(int64(testInt16), 10)) os.Setenv(prefix+"TESTINT16", strconv.FormatInt(int64(testInt16), 10))
os.Setenv(prefix+"TESTINT32", strconv.FormatInt(int64(testInt32), 10)) os.Setenv(prefix+"TESTINT32", strconv.FormatInt(int64(testInt32), 10))
os.Setenv(prefix+"TESTINT64", strconv.FormatInt(int64(testInt64), 10)) os.Setenv(prefix+"TESTINT64", strconv.FormatInt(testInt64, 10))
os.Setenv(prefix+"TESTUINT8", strconv.FormatInt(int64(testUint8), 10)) os.Setenv(prefix+"TESTUINT8", strconv.FormatInt(int64(testUint8), 10))
os.Setenv(prefix+"TESTUINT16", strconv.FormatInt(int64(testUint16), 10)) os.Setenv(prefix+"TESTUINT16", strconv.FormatInt(int64(testUint16), 10))
os.Setenv(prefix+"TESTUINT32", strconv.FormatInt(int64(testUint32), 10)) os.Setenv(prefix+"TESTUINT32", strconv.FormatInt(int64(testUint32), 10))
@ -209,8 +222,8 @@ func TestInvalidDebugFlagValue(t *testing.T) {
func TestInvalidDebugFlagValueWithErrorsAreCritical(t *testing.T) { func TestInvalidDebugFlagValueWithErrorsAreCritical(t *testing.T) {
_ = os.Setenv(debugFlagEnvName, "INVALID") _ = os.Setenv(debugFlagEnvName, "INVALID")
c := &testStruct1{} c := &testStruct1{}
err := Parse(c, &Options{ErrorsAreCritical: true})
err := Parse(c, &Options{ErrorsAreCritical: true})
if err != nil { if err != nil {
t.Log(err.Error()) t.Log(err.Error())
} }