WIP
parent
8f6a3a13a2
commit
1e3f9d2ca8
4
Makefile
4
Makefile
|
@ -1,8 +1,8 @@
|
|||
lint: install-lint-deps
|
||||
golangci-lint run ./pkg/...
|
||||
golangci-lint run ./core/...
|
||||
|
||||
test:
|
||||
go test -race -count 100 -timeout 30s ./pkg/...
|
||||
go test -race -timeout 30s ./core/...
|
||||
|
||||
install-lint-deps:
|
||||
(which golangci-lint > /dev/null) || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# modules
|
||||
Usable go modules for Go apps
|
||||
|
||||
* [/config - universal configuration module](./pkg/config/README.md)
|
||||
* [/logger - universal logging module](./pkg/logger/README.md)
|
||||
* [/config - universal configuration module](core/config/README.md)
|
||||
* [/logger - universal logging module](core/logger/README.md)
|
||||
|
|
|
@ -43,13 +43,17 @@ func (s Interface) Combine(c Config) error {
|
|||
if c.EnvPrefix != "" {
|
||||
fmt.Printf("try to apply config from environment...\n")
|
||||
if err := s.SetFromEnv(c.EnvPrefix); err != nil {
|
||||
return fmt.Errorf("can't apply envvars to config :%w", err)
|
||||
return fmt.Errorf("can't apply envvars to config:%w", err)
|
||||
}
|
||||
}
|
||||
if c.DSN != "" {
|
||||
fmt.Printf("try to apply config from DSN %s...\n", c.DSN)
|
||||
if err := s.SetFromDB(c.DSN); err != nil {
|
||||
return fmt.Errorf("can't apply db lines to config :%w", err)
|
||||
db, dbname, err := DialDSN(c.DSN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't dial DB:%w", err)
|
||||
}
|
||||
if err := s.SetFromDB(db, dbname); err != nil {
|
||||
return fmt.Errorf("can't apply db lines to config:%w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -78,12 +82,11 @@ func (s Interface) SetFromEnv(prefix string) error {
|
|||
return getEnvVar(reflect.ValueOf(s.str), reflect.TypeOf(s.str), -1, prefix)
|
||||
}
|
||||
|
||||
// Method adds and replace config fields from db.
|
||||
func (s Interface) SetFromDB(dsn string) error {
|
||||
func DialDSN(dsn string) (db *sql.DB, dbname string, err error) {
|
||||
m := strings.FieldsFunc(dsn, func(r rune) bool { return r == ':' || r == '@' || r == '/' })
|
||||
dbName := m[len(m)-1]
|
||||
if dbName == "" {
|
||||
return fmt.Errorf("DSN not contains database name: %s", dsn)
|
||||
return nil, "", fmt.Errorf("DSN not contains database name: %s", dsn)
|
||||
}
|
||||
|
||||
var driver string
|
||||
|
@ -96,15 +99,19 @@ func (s Interface) SetFromDB(dsn string) error {
|
|||
driver = "postgresql"
|
||||
}
|
||||
|
||||
db, err := sql.Open(driver, dsn)
|
||||
db, err = sql.Open(driver, dsn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't connect to DB: %w", err)
|
||||
return nil, "", fmt.Errorf("can't connect to DB: %w", err)
|
||||
}
|
||||
defer db.Close()
|
||||
return db, dbName, nil
|
||||
}
|
||||
|
||||
// Method adds and replace config fields from db.
|
||||
func (s Interface) SetFromDB(db *sql.DB, dbname string) error {
|
||||
defer db.Close()
|
||||
res := make(map[string]string)
|
||||
var key, val string
|
||||
results, err := db.Query(`SELECT key, value FROM $1`, dbName)
|
||||
results, err := db.Query("SELECT key, value FROM ?", dbname)
|
||||
if err != nil || results.Err() != nil {
|
||||
return fmt.Errorf("can't get key-value pairs from DB: %w", err)
|
||||
}
|
|
@ -0,0 +1,338 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type TestConf struct {
|
||||
Section1 struct {
|
||||
VarInt1 int
|
||||
VarString1 string
|
||||
VarBool1 bool
|
||||
}
|
||||
Section2 struct {
|
||||
VarInt2 int
|
||||
VarString2 string
|
||||
VarBool2 bool
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetFromFilePositive(t *testing.T) {
|
||||
|
||||
goodfile, err := ioutil.TempFile("", "conf.")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.Remove(goodfile.Name())
|
||||
goodfile.WriteString(
|
||||
`[section1]
|
||||
varint1 = 11
|
||||
varstring1 = "first string"
|
||||
varbool1 = true
|
||||
[section2]
|
||||
varint2 = 22
|
||||
varstring2 = "second string"
|
||||
varbool2 = true`)
|
||||
goodfile.Sync()
|
||||
|
||||
partfile, err := ioutil.TempFile("", "conf.")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.Remove(partfile.Name())
|
||||
partfile.WriteString(
|
||||
`[section1]
|
||||
varint1 = 11
|
||||
[section2]
|
||||
varbool2 = true`)
|
||||
partfile.Sync()
|
||||
|
||||
zerofile, err := ioutil.TempFile("", "conf.")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.Remove(zerofile.Name())
|
||||
zerofile.WriteString(
|
||||
`[sdgsergergse]
|
||||
argargragffg = "sdgfhdtn"
|
||||
[sdbgjuykb]
|
||||
fbfjmuinuyhg = 134134`)
|
||||
zerofile.Sync()
|
||||
|
||||
// Если файл валидный TOML, метод вернет заполненный конфиг и nil.
|
||||
t.Run("Successful parsing the file", func(t *testing.T) {
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromFile(goodfile.Name())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 11, c.Section1.VarInt1)
|
||||
require.Equal(t, "first string", c.Section1.VarString1)
|
||||
require.Equal(t, true, c.Section1.VarBool1)
|
||||
require.Equal(t, 22, c.Section2.VarInt2)
|
||||
require.Equal(t, "second string", c.Section2.VarString2)
|
||||
require.Equal(t, true, c.Section2.VarBool2)
|
||||
})
|
||||
|
||||
// Если некоторых переменных нет в файле, метод вернет конфиг заполненный насколько возможно и nil.
|
||||
t.Run("Partial config applying from file", func(t *testing.T) {
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromFile(partfile.Name())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 11, c.Section1.VarInt1)
|
||||
require.Equal(t, "", c.Section1.VarString1)
|
||||
require.Equal(t, false, c.Section1.VarBool1)
|
||||
require.Equal(t, 0, c.Section2.VarInt2)
|
||||
require.Equal(t, "", c.Section2.VarString2)
|
||||
require.Equal(t, true, c.Section2.VarBool2)
|
||||
})
|
||||
|
||||
// Если переменных нет в файле, метод вернет zerovalue конфиг и nil.
|
||||
t.Run("No vars in file", func(t *testing.T) {
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromFile(zerofile.Name())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, TestConf{}, c)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetFromFileNegative(t *testing.T) {
|
||||
|
||||
badfile, err := ioutil.TempFile("", "conf.")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.Remove(badfile.Name())
|
||||
badfile.WriteString(`aefSD
|
||||
sadfg
|
||||
RFABND FYGUMG
|
||||
V`)
|
||||
badfile.Sync()
|
||||
|
||||
typesfile, err := ioutil.TempFile("", "conf.")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.Remove(typesfile.Name())
|
||||
typesfile.WriteString(
|
||||
`[section1]
|
||||
varint1 = "first string"
|
||||
varstring1 = true
|
||||
varbool1 = 11
|
||||
[section2]
|
||||
varint2 = 22
|
||||
varstring2 = "second string"
|
||||
varbool2 = true`)
|
||||
typesfile.Sync()
|
||||
|
||||
// Если файла не существует, метод вернет пустой конфиг и ошибку.
|
||||
t.Run("File doesn't exist", func(t *testing.T) {
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromFile("dfsdfgsdfds")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, TestConf{}, c)
|
||||
})
|
||||
|
||||
// Если файл не является TOML-readable, метод вернет пустой конфиг и ошибку.
|
||||
t.Run("Try to use corrupted file", func(t *testing.T) {
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromFile(badfile.Name())
|
||||
require.Error(t, err)
|
||||
require.Equal(t, TestConf{}, c)
|
||||
})
|
||||
|
||||
// Если некоторые типы перепутаны, метод вернет zerovalue конфиг и ошибку.
|
||||
t.Run("Unexpected types", func(t *testing.T) {
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromFile(typesfile.Name())
|
||||
require.Error(t, err)
|
||||
require.Equal(t, TestConf{}, c)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetFromEnvPositive(t *testing.T) {
|
||||
|
||||
// Если все соответствующие переменные есть в окружении, метод вернет полностью заполненный конфиг и nil.
|
||||
t.Run("Fulfill config applying from env", func(t *testing.T) {
|
||||
for k, v := range map[string]string{"APP_SECTION1_VARINT1": "11", "APP_SECTION1_VARSTRING1": "first string", "APP_SECTION1_VARBOOL1": "true", "APP_SECTION2_VARINT2": "22", "APP_SECTION2_VARSTRING2": "second string", "APP_SECTION2_VARBOOL2": "true"} {
|
||||
require.NoError(t, os.Setenv(k, v))
|
||||
}
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromEnv("APP")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 11, c.Section1.VarInt1)
|
||||
require.Equal(t, "first string", c.Section1.VarString1)
|
||||
require.Equal(t, true, c.Section1.VarBool1)
|
||||
require.Equal(t, 22, c.Section2.VarInt2)
|
||||
require.Equal(t, "second string", c.Section2.VarString2)
|
||||
require.Equal(t, true, c.Section2.VarBool2)
|
||||
})
|
||||
|
||||
// Если некоторых переменных нет в окружении, метод вернет конфиг заполненный насколько возможно и nil.
|
||||
t.Run("Partial config applying from env", func(t *testing.T) {
|
||||
for k, v := range map[string]string{"APP_SECTION1_VARINT1": "11", "APP_SECTION1_VARSTRING1": "first string", "APP_SECTION1_VARBOOL1": "true"} {
|
||||
require.NoError(t, os.Setenv(k, v))
|
||||
}
|
||||
for k, _ := range map[string]string{"APP_SECTION2_VARINT2": "22", "APP_SECTION2_VARSTRING2": "second string", "APP_SECTION2_VARBOOL2": "true"} {
|
||||
require.NoError(t, os.Unsetenv(k))
|
||||
}
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromEnv("APP")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 11, c.Section1.VarInt1)
|
||||
require.Equal(t, "first string", c.Section1.VarString1)
|
||||
require.Equal(t, true, c.Section1.VarBool1)
|
||||
require.Equal(t, 0, c.Section2.VarInt2)
|
||||
require.Equal(t, "", c.Section2.VarString2)
|
||||
require.Equal(t, false, c.Section2.VarBool2)
|
||||
})
|
||||
|
||||
// Если переменных нет в окружении, метод вернет zerovalue конфиг и nil.
|
||||
t.Run("No env vars", func(t *testing.T) {
|
||||
for k, _ := range map[string]string{"APP_SECTION1_VARINT1": "11", "APP_SECTION1_VARSTRING1": "first string", "APP_SECTION1_VARBOOL1": "true", "APP_SECTION2_VARINT2": "22", "APP_SECTION2_VARSTRING2": "second string", "APP_SECTION2_VARBOOL2": "true"} {
|
||||
require.NoError(t, os.Unsetenv(k))
|
||||
}
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromEnv("APP")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, TestConf{}, c)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetFromEnvNegative(t *testing.T) {
|
||||
|
||||
// Если некоторые типы перепутаны, метод вернет zerovalue конфиг и ошибку.
|
||||
t.Run("Unexpected types", func(t *testing.T) {
|
||||
for k, v := range map[string]string{"APP_SECTION1_VARINT1": "first string", "APP_SECTION1_VARSTRING1": "false", "APP_SECTION1_VARBOOL1": "shstrjerthgccw", "APP_SECTION2_VARINT2": "22", "APP_SECTION2_VARSTRING2": "second string", "APP_SECTION2_VARBOOL2": "true"} {
|
||||
require.NoError(t, os.Setenv(k, v))
|
||||
}
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromEnv("APP")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, TestConf{}, c)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetFromDBPositive(t *testing.T) {
|
||||
|
||||
// Если в базе есть все переменные конфига, метод вернет заполненный конфиг и nil.
|
||||
t.Run("Successful reading from DB", func(t *testing.T) {
|
||||
db, mock := newMock()
|
||||
defer db.Close()
|
||||
|
||||
rows := sqlmock.NewRows([]string{"key", "value"})
|
||||
rows.AddRow("SECTION1.VARINT1", "11")
|
||||
rows.AddRow("SECTION1.VARSTRING1", "first string")
|
||||
rows.AddRow("SECTION1.VARBOOL1", "true")
|
||||
rows.AddRow("SECTION2.VARINT2", "22")
|
||||
rows.AddRow("SECTION2.VARSTRING2", "second string")
|
||||
rows.AddRow("SECTION2.VARBOOL2", "true")
|
||||
|
||||
mock.ExpectQuery("SELECT key, value FROM").WithArgs("config").WillReturnRows(rows)
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromDB(db, "config")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 11, c.Section1.VarInt1)
|
||||
require.Equal(t, "first string", c.Section1.VarString1)
|
||||
require.Equal(t, true, c.Section1.VarBool1)
|
||||
require.Equal(t, 22, c.Section2.VarInt2)
|
||||
require.Equal(t, "second string", c.Section2.VarString2)
|
||||
require.Equal(t, true, c.Section2.VarBool2)
|
||||
})
|
||||
|
||||
// Если в базе нет некоторых переменных, метод вернет конфиг заполненный насколько возможно и nil.
|
||||
t.Run("Partial reading from DB", func(t *testing.T) {
|
||||
db, mock := newMock()
|
||||
defer db.Close()
|
||||
|
||||
rows := sqlmock.NewRows([]string{"key", "value"})
|
||||
rows.AddRow("SECTION2.VARINT2", "22")
|
||||
rows.AddRow("SECTION2.VARSTRING2", "second string")
|
||||
rows.AddRow("SECTION2.VARBOOL2", "true")
|
||||
|
||||
mock.ExpectQuery("SELECT key, value FROM").WithArgs("config").WillReturnRows(rows)
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromDB(db, "config")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, c.Section1.VarInt1)
|
||||
require.Equal(t, "", c.Section1.VarString1)
|
||||
require.Equal(t, false, c.Section1.VarBool1)
|
||||
require.Equal(t, 22, c.Section2.VarInt2)
|
||||
require.Equal(t, "second string", c.Section2.VarString2)
|
||||
require.Equal(t, true, c.Section2.VarBool2)
|
||||
})
|
||||
|
||||
// Если в базе нет переменных, метод вернет zerovalue конфиг и nil.
|
||||
t.Run("No vars in DB", func(t *testing.T) {
|
||||
db, mock := newMock()
|
||||
defer db.Close()
|
||||
|
||||
rows := sqlmock.NewRows([]string{"key", "value"})
|
||||
|
||||
mock.ExpectQuery("SELECT key, value FROM").WithArgs("config").WillReturnRows(rows)
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromDB(db, "config")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, TestConf{}, c)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetFromDBNegative(t *testing.T) {
|
||||
|
||||
// Если некоторые типы перепутаны, метод вернет zerovalue конфиг и ошибку.
|
||||
t.Run("Unexpected types", func(t *testing.T) {
|
||||
db, mock := newMock()
|
||||
defer db.Close()
|
||||
|
||||
rows := sqlmock.NewRows([]string{"key", "value"})
|
||||
rows.AddRow("SECTION1.VARINT1", "first string")
|
||||
rows.AddRow("SECTION1.VARSTRING1", "true")
|
||||
rows.AddRow("SECTION1.VARBOOL1", "affwefefasdasfdsda")
|
||||
rows.AddRow("SECTION2.VARINT2", "22")
|
||||
rows.AddRow("SECTION2.VARSTRING2", "second string")
|
||||
rows.AddRow("SECTION2.VARBOOL2", "true")
|
||||
|
||||
mock.ExpectQuery("SELECT key, value FROM").WithArgs("config").WillReturnRows(rows)
|
||||
var c TestConf
|
||||
i := New(&c)
|
||||
err := i.SetFromDB(db, "config")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, TestConf{}, c)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCombinePositive(t *testing.T) {
|
||||
// Успешное чтение из файла, окружения и базы
|
||||
// Считанные из базы переписывают считанные из окружения, которые переписывают считанные из файла
|
||||
}
|
||||
|
||||
func TestCombineNegative(t *testing.T) {
|
||||
// Если файла не существует, метод вернет пустой конфиг и ошибку.
|
||||
// Если файл не является TOML-readable, метод вернет пустой конфиг и ошибку.
|
||||
// Если некоторые типы перепутаны, метод вернет zerovalue конфиг и ошибку.
|
||||
}
|
||||
|
||||
func newMock() (*sql.DB, sqlmock.Sqlmock) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
log.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
}
|
||||
return db, mock
|
||||
}
|
|
@ -18,25 +18,13 @@ func getEnvVar(v reflect.Value, t reflect.Type, counter int, prefix string) erro
|
|||
}
|
||||
v = reflect.Indirect(v)
|
||||
fName := strings.ToUpper(f.Name)
|
||||
if prefix != "" {
|
||||
prefix = strings.TrimLeft(prefix, "_")
|
||||
}
|
||||
|
||||
env := os.Getenv(prefix + fName)
|
||||
if env != "" {
|
||||
switch v.Kind() {
|
||||
case reflect.Int:
|
||||
envI, err := strconv.Atoi(env)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse to int: %w", err)
|
||||
}
|
||||
v.SetInt(int64(envI))
|
||||
case reflect.String:
|
||||
v.SetString(env)
|
||||
case reflect.Bool:
|
||||
envB, err := strconv.ParseBool(env)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse bool: %w", err)
|
||||
}
|
||||
v.SetBool(envB)
|
||||
case reflect.Array, reflect.Chan, reflect.Complex128, reflect.Complex64, reflect.Float32, reflect.Float64, reflect.Func, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Interface, reflect.Invalid, reflect.Map, reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8, reflect.Uintptr, reflect.UnsafePointer:
|
||||
}
|
||||
if err := selector(env, &v); err != nil {
|
||||
return fmt.Errorf("could set value: %w", err)
|
||||
}
|
||||
if v.Kind() == reflect.Struct {
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
|
@ -58,9 +46,13 @@ func parseToStruct(v reflect.Value, t reflect.Type, counter int, prefix string,
|
|||
}
|
||||
v = reflect.Indirect(v)
|
||||
fName := strings.ToUpper(f.Name)
|
||||
if prefix != "" {
|
||||
prefix = strings.TrimLeft(prefix, ".")
|
||||
}
|
||||
|
||||
env := kv[prefix+fName]
|
||||
if env != "" {
|
||||
v.SetString(env)
|
||||
if err := selector(env, &v); err != nil {
|
||||
return fmt.Errorf("could set value: %w", err)
|
||||
}
|
||||
if v.Kind() == reflect.Struct {
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
|
@ -71,3 +63,26 @@ func parseToStruct(v reflect.Value, t reflect.Type, counter int, prefix string,
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func selector(env string, v *reflect.Value) error {
|
||||
if env != "" {
|
||||
switch v.Kind() {
|
||||
case reflect.Int:
|
||||
envI, err := strconv.Atoi(env)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse to int: %w", err)
|
||||
}
|
||||
v.SetInt(int64(envI))
|
||||
case reflect.String:
|
||||
v.SetString(env)
|
||||
case reflect.Bool:
|
||||
envB, err := strconv.ParseBool(env)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse bool: %w", err)
|
||||
}
|
||||
v.SetBool(envB)
|
||||
case reflect.Array, reflect.Chan, reflect.Complex128, reflect.Complex64, reflect.Float32, reflect.Float64, reflect.Func, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Interface, reflect.Invalid, reflect.Map, reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8, reflect.Uintptr, reflect.UnsafePointer:
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
6
go.mod
6
go.mod
|
@ -4,11 +4,15 @@ go 1.14
|
|||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/amitrai48/logger v0.0.0-20190214092904-448001c055ec
|
||||
github.com/daixiang0/gci v0.2.6 // indirect
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/lib/pq v1.9.0
|
||||
github.com/moemoe89/go-unit-test-sql v0.0.0-20200619083906-541278f731fe
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/stretchr/testify v1.6.1
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
|
28
go.sum
28
go.sum
|
@ -1,18 +1,25 @@
|
|||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/amitrai48/logger v0.0.0-20190214092904-448001c055ec h1:tDOPo9NAXCjvoK35HgZyzQSNLmb3chZqN2tnO273Bro=
|
||||
github.com/amitrai48/logger v0.0.0-20190214092904-448001c055ec/go.mod h1:RZEHP3cxXvQlMuMjkpdh6qXA4b0CpjxnUBNxOpR0r30=
|
||||
github.com/daixiang0/gci v0.2.6 h1:qMHUJVvI308H4MFNfHNWvoA+KvgpRdmcPnPYER3q+vM=
|
||||
github.com/daixiang0/gci v0.2.6/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
|
||||
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/moemoe89/go-unit-test-sql v0.0.0-20200619083906-541278f731fe h1:5c+Tvt9nrBI5gJffKjRzIohivy48xGRUpStX7JH6ToA=
|
||||
github.com/moemoe89/go-unit-test-sql v0.0.0-20200619083906-541278f731fe/go.mod h1:C84oY/GEInmc4bfx+P0ZE7Vi+pFGUSvPRvdQPkSP/TA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -24,7 +31,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
|
@ -34,16 +40,9 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -51,15 +50,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394 h1:O3VD5Fds21mB1WVRTbkiz/HTXESx6Jql5ucPZi69oiM=
|
||||
golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewConfig(t *testing.T) {
|
||||
|
||||
badfile, err := ioutil.TempFile("", "conf.")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.Remove(badfile.Name())
|
||||
badfile.WriteString(`aefSD
|
||||
sadfg
|
||||
RFABND FYGUMG
|
||||
V`)
|
||||
badfile.Sync()
|
||||
|
||||
goodfile, err := ioutil.TempFile("", "conf.")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.Remove(goodfile.Name())
|
||||
goodfile.WriteString(`[storage]
|
||||
inMemory = true
|
||||
SQLHost = "localhost"`)
|
||||
goodfile.Sync()
|
||||
|
||||
t.Run("No such file", func(t *testing.T) {
|
||||
var c Calendar
|
||||
e := New("adfergdth", &c)
|
||||
require.Equal(t, Calendar{}, c)
|
||||
require.Error(t, e)
|
||||
})
|
||||
|
||||
t.Run("Bad file", func(t *testing.T) {
|
||||
var c Calendar
|
||||
e := New(badfile.Name(), &c)
|
||||
require.Equal(t, Calendar{}, c)
|
||||
require.Error(t, e)
|
||||
})
|
||||
|
||||
t.Run("TOML reading", func(t *testing.T) {
|
||||
var c Calendar
|
||||
e := New(goodfile.Name(), &c)
|
||||
require.Equal(t, true, c.Storage.InMemory)
|
||||
require.Equal(t, "localhost", c.Storage.SQLHost)
|
||||
require.NoError(t, e)
|
||||
})
|
||||
|
||||
t.Run("ENV reading", func(t *testing.T) {
|
||||
for k, v := range map[string]string{"APP_STRUCT1_VAR1": "val1", "APP_STRUCT1_VAR2": "val2", "APP_STRUCT2_VAR1": "val3", "APP_STRUCT2_VAR2": "val4", "APP_STRUCT3_VAR1": "val5", "APP_STRUCT3_VAR2": "val6"} {
|
||||
require.NoError(t, os.Setenv(k, v))
|
||||
}
|
||||
var str struct {
|
||||
Struct1 struct {
|
||||
Var1 string
|
||||
Var2 string
|
||||
}
|
||||
Struct2 struct {
|
||||
Var1 string
|
||||
Var2 string
|
||||
}
|
||||
Struct3 struct {
|
||||
Var1 string
|
||||
Var2 string
|
||||
}
|
||||
}
|
||||
|
||||
err := New("", &str)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "val1", str.Struct1.Var1)
|
||||
require.Equal(t, "val2", str.Struct1.Var2)
|
||||
require.Equal(t, "val3", str.Struct2.Var1)
|
||||
require.Equal(t, "val4", str.Struct2.Var2)
|
||||
require.Equal(t, "val5", str.Struct3.Var1)
|
||||
require.Equal(t, "val6", str.Struct3.Var2)
|
||||
})
|
||||
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package config
|
||||
|
||||
type Calendar struct {
|
||||
GRPC Server
|
||||
HTTP Server
|
||||
API Server
|
||||
Logger Logger
|
||||
Storage Storage
|
||||
}
|
||||
|
||||
type Scheduler struct {
|
||||
Rabbitmq Rabbit
|
||||
Storage Storage
|
||||
Logger Logger
|
||||
}
|
||||
|
||||
type Sender struct {
|
||||
Rabbitmq Rabbit
|
||||
Logger Logger
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Address string
|
||||
Port string
|
||||
}
|
||||
|
||||
type Rabbit struct {
|
||||
Login string
|
||||
Pass string
|
||||
Address string
|
||||
Port string
|
||||
Exchange string
|
||||
Queue string
|
||||
Key string
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
File string
|
||||
Level string
|
||||
MuteStdout bool
|
||||
}
|
||||
|
||||
type Storage struct {
|
||||
InMemory bool
|
||||
SQLHost string
|
||||
SQLPort string
|
||||
SQLDbase string
|
||||
SQLUser string
|
||||
SQLPass string
|
||||
}
|
Loading…
Reference in New Issue