github.com/grafana/pyroscope@v1.18.0/pkg/cfg/cfg_test.go (about)

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