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  }