diff --git a/internal/config/config.go b/internal/config/config.go index 5c524e1525..5740cd5f1d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -7,31 +7,21 @@ import ( "path/filepath" "github.com/BurntSushi/toml" - "github.com/ghodss/yaml" + "gopkg.in/yaml.v2" "github.com/caos/zitadel/internal/errors" ) -type Reader interface { - Unmarshal(data []byte, o interface{}) error -} - type ValidatableConfiguration interface { Validate() error } type ReaderFunc func(data []byte, o interface{}) error -func (c ReaderFunc) Unmarshal(data []byte, o interface{}) error { - return c(data, o) -} - var ( - JSONReader = ReaderFunc(json.Unmarshal) - TOMLReader = ReaderFunc(toml.Unmarshal) - YAMLReader = ReaderFunc(func(y []byte, o interface{}) error { - return yaml.Unmarshal(y, o) - }) + JSONReader = json.Unmarshal + TOMLReader = toml.Unmarshal + YAMLReader = yaml.Unmarshal ) // Read deserializes each config file to the target obj @@ -39,11 +29,11 @@ var ( // env vars are replaced in the config file as well as the file path func Read(obj interface{}, configFiles ...string) error { for _, cf := range configFiles { - configReader, err := configReaderForFile(cf) + readerFunc, err := readerFuncForFile(cf) if err != nil { return err } - if err := readConfigFile(configReader, cf, obj); err != nil { + if err := readConfigFile(readerFunc, cf, obj); err != nil { return err } } @@ -57,13 +47,9 @@ func Read(obj interface{}, configFiles ...string) error { return nil } -func readConfigFile(configReader Reader, configFile string, obj interface{}) error { +func readConfigFile(readerFunc ReaderFunc, configFile string, obj interface{}) error { configFile = os.ExpandEnv(configFile) - if _, err := os.Stat(configFile); err != nil { - return errors.ThrowNotFoundf(err, "CONFI-Hs93M", "config file %s does not exist", configFile) - } - configStr, err := ioutil.ReadFile(configFile) if err != nil { return errors.ThrowInternalf(err, "CONFI-nJk2a", "failed to read config file %s", configFile) @@ -71,14 +57,14 @@ func readConfigFile(configReader Reader, configFile string, obj interface{}) err configStr = []byte(os.ExpandEnv(string(configStr))) - if err := configReader.Unmarshal(configStr, obj); err != nil { + if err := readerFunc(configStr, obj); err != nil { return errors.ThrowInternalf(err, "CONFI-2Mc3c", "error parse config file %s", configFile) } return nil } -func configReaderForFile(configFile string) (Reader, error) { +func readerFuncForFile(configFile string) (ReaderFunc, error) { ext := filepath.Ext(configFile) switch ext { case ".yaml", ".yml": diff --git a/internal/config/config_test.go b/internal/config/config_test.go new file mode 100644 index 0000000000..ba517b6061 --- /dev/null +++ b/internal/config/config_test.go @@ -0,0 +1,198 @@ +package config + +import ( + "errors" + "reflect" + "runtime" + "testing" + + "github.com/stretchr/testify/assert" +) + +type test struct { + Test bool +} + +type validatable struct { + Test bool +} + +func (v *validatable) Validate() error { + if v.Test { + return nil + } + return errors.New("invalid") +} + +func TestRead(t *testing.T) { + type args struct { + obj interface{} + configFiles []string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + "not supoorted config file error", + args{ + configFiles: []string{"notsupported.unknown"}, + obj: nil, + }, + true, + }, + { + "non existing config file error", + args{ + configFiles: []string{"nonexisting.yaml"}, + obj: nil, + }, + true, + }, + { + "non parsable config file error", + args{ + configFiles: []string{"./testdata/non_parsable.json"}, + obj: &test{}, + }, + true, + }, + { + "invalid parsable config file error", + args{ + configFiles: []string{"./testdata/invalid.json"}, + obj: &validatable{}, + }, + true, + }, + { + "parsable config file ok", + args{ + configFiles: []string{"./testdata/valid.json"}, + obj: &test{}, + }, + false, + }, + { + "valid parsable config file ok", + args{ + configFiles: []string{"./testdata/valid.json"}, + obj: &validatable{}, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Read(tt.args.obj, tt.args.configFiles...); (err != nil) != tt.wantErr { + t.Errorf("Read() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_readerFuncForFile(t *testing.T) { + type args struct { + configFile string + } + tests := []struct { + name string + args args + want ReaderFunc + wantErr bool + }{ + { + "unknown extension error", + args{configFile: "test.unknown"}, + nil, + true, + }, + { + "toml", + args{configFile: "test.toml"}, + TOMLReader, + false, + }, + { + "json", + args{configFile: "test.json"}, + JSONReader, + false, + }, + { + "yaml", + args{configFile: "test.yaml"}, + YAMLReader, + false, + }, + { + "yml", + args{configFile: "test.yml"}, + YAMLReader, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := readerFuncForFile(tt.args.configFile) + if (err != nil) != tt.wantErr { + t.Errorf("configReaderForFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + funcName1 := runtime.FuncForPC(reflect.ValueOf(got).Pointer()).Name() + funcName2 := runtime.FuncForPC(reflect.ValueOf(tt.want).Pointer()).Name() + if !assert.Equal(t, funcName1, funcName2) { + t.Errorf("configReaderForFile() got = %v, want %v", funcName1, funcName2) + } + }) + } +} + +func Test_readConfigFile(t *testing.T) { + type args struct { + configReader ReaderFunc + configFile string + obj interface{} + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + "non existing config file error", + args{ + configReader: YAMLReader, + configFile: "nonexisting.json", + obj: nil, + }, + true, + }, + { + "non parsable config file error", + args{ + configReader: YAMLReader, + configFile: "./testdata/non_parsable.json", + obj: &test{}, + }, + true, + }, + { + "parsable config file no error", + args{ + configReader: YAMLReader, + configFile: "./testdata/valid.json", + obj: &test{}, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := readConfigFile(tt.args.configReader, tt.args.configFile, tt.args.obj); (err != nil) != tt.wantErr { + t.Errorf("readConfigFile() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/internal/config/testdata/invalid.json b/internal/config/testdata/invalid.json new file mode 100644 index 0000000000..454b39367e --- /dev/null +++ b/internal/config/testdata/invalid.json @@ -0,0 +1,3 @@ +{ + "Test" : false +} \ No newline at end of file diff --git a/internal/config/testdata/non_parsable.json b/internal/config/testdata/non_parsable.json new file mode 100644 index 0000000000..8318c86b35 --- /dev/null +++ b/internal/config/testdata/non_parsable.json @@ -0,0 +1 @@ +Test \ No newline at end of file diff --git a/internal/config/testdata/valid.json b/internal/config/testdata/valid.json new file mode 100644 index 0000000000..65f3700045 --- /dev/null +++ b/internal/config/testdata/valid.json @@ -0,0 +1,3 @@ +{ + "Test" : true +} \ No newline at end of file