github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/util/cfg/cfg_test.go (about) 1 package cfg 2 3 import ( 4 "flag" 5 "io/ioutil" 6 "os" 7 "testing" 8 "time" 9 10 "github.com/grafana/dskit/flagext" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 ) 14 15 func TestParse(t *testing.T) { 16 yamlSource := dYAML([]byte(` 17 server: 18 port: 2000 19 timeout: 60h 20 tls: 21 key: YAML 22 `)) 23 24 fs := flag.NewFlagSet(t.Name(), flag.PanicOnError) 25 flagSource := dFlags(fs, []string{"-verbose", "-server.port=21"}) 26 27 data := Data{} 28 err := Unmarshal(&data, 29 Defaults(fs), 30 yamlSource, 31 flagSource, 32 ) 33 require.NoError(t, err) 34 35 assert.Equal(t, Data{ 36 Verbose: true, // flag 37 Server: Server{ 38 Port: 21, // flag 39 Timeout: 60 * time.Hour, // defaults 40 }, 41 TLS: TLS{ 42 Cert: "DEFAULTCERT", // defaults 43 Key: "YAML", // yaml 44 }, 45 }, data) 46 } 47 48 func TestParseWithInvalidYAML(t *testing.T) { 49 yamlSource := dYAML([]byte(` 50 servers: 51 ports: 2000 52 timeoutz: 60h 53 tls: 54 keey: YAML 55 `)) 56 57 fs := flag.NewFlagSet(t.Name(), flag.PanicOnError) 58 flagSource := dFlags(fs, []string{"-verbose", "-server.port=21"}) 59 60 data := Data{} 61 err := Unmarshal(&data, 62 Defaults(fs), 63 yamlSource, 64 flagSource, 65 ) 66 require.Error(t, err) 67 require.Equal(t, err.Error(), "yaml: unmarshal errors:\n line 2: field servers not found in type cfg.Data\n line 6: field keey not found in type cfg.TLS") 68 } 69 70 func TestDefaultUnmarshal(t *testing.T) { 71 testContext := func(yamlString string, args []string) TestConfigWrapper { 72 file, err := ioutil.TempFile("", "config.yaml") 73 defer func() { 74 os.Remove(file.Name()) 75 }() 76 require.NoError(t, err) 77 78 _, err = file.WriteString(yamlString) 79 require.NoError(t, err) 80 81 configFileArgs := []string{"-config.file", file.Name()} 82 if args == nil { 83 args = configFileArgs 84 } else { 85 args = append(args, configFileArgs...) 86 } 87 88 var config TestConfigWrapper 89 flags := flag.NewFlagSet(t.Name(), flag.PanicOnError) 90 err = DefaultUnmarshal(&config, args, flags) 91 require.NoError(t, err) 92 93 return config 94 } 95 t.Run("with an empty config file and no command line args, defaults are used", func(t *testing.T) { 96 configFileString := `--- 97 required: foo` 98 config := testContext(configFileString, nil) 99 100 assert.Equal(t, "Jerry", config.Name) 101 assert.Equal(t, true, config.Role.Sings) 102 assert.Equal(t, "guitar", config.Role.Instrument) 103 }) 104 105 t.Run("values provided in config file take precedence over defaults", func(t *testing.T) { 106 configFileString := `--- 107 required: foo 108 name: Phil` 109 config := testContext(configFileString, nil) 110 111 assert.Equal(t, "Phil", config.Name) 112 assert.Equal(t, true, config.Role.Sings) 113 }) 114 115 t.Run("partial structs can be provided in the config file, with defaults filling zeros", func(t *testing.T) { 116 configFileString := `--- 117 required: foo 118 name: Phil 119 role: 120 instrument: bass` 121 config := testContext(configFileString, nil) 122 123 assert.Equal(t, "Phil", config.Name) 124 assert.Equal(t, "bass", config.Role.Instrument) 125 //zero value overridden by default 126 assert.Equal(t, true, config.Role.Sings) 127 }) 128 129 t.Run("values can be explicitly zeroed out in config file", func(t *testing.T) { 130 configFileString := `--- 131 required: foo 132 name: Mickey 133 role: 134 sings: false 135 instrument: drums` 136 config := testContext(configFileString, nil) 137 138 assert.Equal(t, "Mickey", config.Name) 139 assert.Equal(t, "drums", config.Role.Instrument) 140 assert.Equal(t, false, config.Role.Sings) 141 }) 142 143 t.Run("values passed by command line take precedence", func(t *testing.T) { 144 configFileString := `--- 145 name: Mickey 146 role: 147 sings: false` 148 149 args := []string{"-name", "Bob", "-role.sings=true", "-role.instrument", "piano"} 150 config := testContext(configFileString, args) 151 152 assert.Equal(t, "Bob", config.Name) 153 assert.Equal(t, true, config.Role.Sings) 154 assert.Equal(t, "piano", config.Role.Instrument) 155 }) 156 } 157 158 type TestConfigWrapper struct { 159 TestConfig `yaml:",inline"` 160 ConfigFile string 161 } 162 163 func (c *TestConfigWrapper) RegisterFlags(f *flag.FlagSet) { 164 f.StringVar(&c.ConfigFile, "config.file", "", "yaml file to load") 165 c.TestConfig.RegisterFlags(f) 166 } 167 168 func (c *TestConfigWrapper) Clone() flagext.Registerer { 169 return func(c TestConfigWrapper) *TestConfigWrapper { 170 return &c 171 }(*c) 172 } 173 174 type TestConfig struct { 175 //Add a parameter that will always be there, as the yaml parser exhibits 176 //weird behavior when a config file is completely empty 177 Required string `yaml:"required"` 178 Name string `yaml:"name"` 179 Role Role `yaml:"role"` 180 } 181 182 func (c *TestConfig) RegisterFlags(f *flag.FlagSet) { 183 f.StringVar(&c.Name, "name", "Jerry", "Favorite band member") 184 c.Role.RegisterFlags(f) 185 } 186 187 type Role struct { 188 Sings bool `yaml:"sings"` 189 Instrument string `yaml:"instrument"` 190 } 191 192 func (c *Role) RegisterFlags(f *flag.FlagSet) { 193 f.BoolVar(&c.Sings, "role.sings", true, "Do they sing?") 194 f.StringVar(&c.Instrument, "role.instrument", "guitar", "What instrument do they play?") 195 }