...

Source file src/github.com/spf13/viper/viper_test.go

Documentation: github.com/spf13/viper

     1  // Copyright © 2014 Steve Francia <spf@spf13.com>.
     2  //
     3  // Use of this source code is governed by an MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package viper
     7  
     8  import (
     9  	"bytes"
    10  	"encoding/json"
    11  	"io"
    12  	"os"
    13  	"os/exec"
    14  	"path"
    15  	"path/filepath"
    16  	"reflect"
    17  	"runtime"
    18  	"strings"
    19  	"sync"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/fsnotify/fsnotify"
    24  	"github.com/mitchellh/mapstructure"
    25  	"github.com/spf13/afero"
    26  	"github.com/spf13/cast"
    27  	"github.com/spf13/pflag"
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/require"
    30  
    31  	"github.com/spf13/viper/internal/features"
    32  	"github.com/spf13/viper/internal/testutil"
    33  )
    34  
    35  // var yamlExample = []byte(`Hacker: true
    36  // name: steve
    37  // hobbies:
    38  //     - skateboarding
    39  //     - snowboarding
    40  //     - go
    41  // clothing:
    42  //     jacket: leather
    43  //     trousers: denim
    44  //     pants:
    45  //         size: large
    46  // age: 35
    47  // eyes : brown
    48  // beard: true
    49  // `)
    50  
    51  var yamlExampleWithExtras = []byte(`Existing: true
    52  Bogus: true
    53  `)
    54  
    55  type testUnmarshalExtra struct {
    56  	Existing bool
    57  }
    58  
    59  var tomlExample = []byte(`
    60  title = "TOML Example"
    61  
    62  [owner]
    63  organization = "MongoDB"
    64  Bio = "MongoDB Chief Developer Advocate & Hacker at Large"
    65  dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)
    66  
    67  var dotenvExample = []byte(`
    68  TITLE_DOTENV="DotEnv Example"
    69  TYPE_DOTENV=donut
    70  NAME_DOTENV=Cake`)
    71  
    72  var jsonExample = []byte(`{
    73  "id": "0001",
    74  "type": "donut",
    75  "name": "Cake",
    76  "ppu": 0.55,
    77  "batters": {
    78          "batter": [
    79                  { "type": "Regular" },
    80                  { "type": "Chocolate" },
    81                  { "type": "Blueberry" },
    82                  { "type": "Devil's Food" }
    83              ]
    84      }
    85  }`)
    86  
    87  var hclExample = []byte(`
    88  id = "0001"
    89  type = "donut"
    90  name = "Cake"
    91  ppu = 0.55
    92  foos {
    93  	foo {
    94  		key = 1
    95  	}
    96  	foo {
    97  		key = 2
    98  	}
    99  	foo {
   100  		key = 3
   101  	}
   102  	foo {
   103  		key = 4
   104  	}
   105  }`)
   106  
   107  var propertiesExample = []byte(`
   108  p_id: 0001
   109  p_type: donut
   110  p_name: Cake
   111  p_ppu: 0.55
   112  p_batters.batter.type: Regular
   113  `)
   114  
   115  var remoteExample = []byte(`{
   116  "id":"0002",
   117  "type":"cronut",
   118  "newkey":"remote"
   119  }`)
   120  
   121  var iniExample = []byte(`; Package name
   122  NAME        = ini
   123  ; Package version
   124  VERSION     = v1
   125  ; Package import path
   126  IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
   127  
   128  # Information about package author
   129  # Bio can be written in multiple lines.
   130  [author]
   131  NAME   = Unknown  ; Succeeding comment
   132  E-MAIL = fake@localhost
   133  GITHUB = https://github.com/%(NAME)s
   134  BIO    = """Gopher.
   135  Coding addict.
   136  Good man.
   137  """  # Succeeding comment`)
   138  
   139  func initConfigs() {
   140  	Reset()
   141  	var r io.Reader
   142  	SetConfigType("yaml")
   143  	r = bytes.NewReader(yamlExample)
   144  	unmarshalReader(r, v.config)
   145  
   146  	SetConfigType("json")
   147  	r = bytes.NewReader(jsonExample)
   148  	unmarshalReader(r, v.config)
   149  
   150  	SetConfigType("hcl")
   151  	r = bytes.NewReader(hclExample)
   152  	unmarshalReader(r, v.config)
   153  
   154  	SetConfigType("properties")
   155  	r = bytes.NewReader(propertiesExample)
   156  	unmarshalReader(r, v.config)
   157  
   158  	SetConfigType("toml")
   159  	r = bytes.NewReader(tomlExample)
   160  	unmarshalReader(r, v.config)
   161  
   162  	SetConfigType("env")
   163  	r = bytes.NewReader(dotenvExample)
   164  	unmarshalReader(r, v.config)
   165  
   166  	SetConfigType("json")
   167  	remote := bytes.NewReader(remoteExample)
   168  	unmarshalReader(remote, v.kvstore)
   169  
   170  	SetConfigType("ini")
   171  	r = bytes.NewReader(iniExample)
   172  	unmarshalReader(r, v.config)
   173  }
   174  
   175  func initConfig(typ, config string) {
   176  	Reset()
   177  	SetConfigType(typ)
   178  	r := strings.NewReader(config)
   179  
   180  	if err := unmarshalReader(r, v.config); err != nil {
   181  		panic(err)
   182  	}
   183  }
   184  
   185  func initYAML() {
   186  	initConfig("yaml", string(yamlExample))
   187  }
   188  
   189  func initJSON() {
   190  	Reset()
   191  	SetConfigType("json")
   192  	r := bytes.NewReader(jsonExample)
   193  
   194  	unmarshalReader(r, v.config)
   195  }
   196  
   197  func initProperties() {
   198  	Reset()
   199  	SetConfigType("properties")
   200  	r := bytes.NewReader(propertiesExample)
   201  
   202  	unmarshalReader(r, v.config)
   203  }
   204  
   205  func initTOML() {
   206  	Reset()
   207  	SetConfigType("toml")
   208  	r := bytes.NewReader(tomlExample)
   209  
   210  	unmarshalReader(r, v.config)
   211  }
   212  
   213  func initDotEnv() {
   214  	Reset()
   215  	SetConfigType("env")
   216  	r := bytes.NewReader(dotenvExample)
   217  
   218  	unmarshalReader(r, v.config)
   219  }
   220  
   221  func initHcl() {
   222  	Reset()
   223  	SetConfigType("hcl")
   224  	r := bytes.NewReader(hclExample)
   225  
   226  	unmarshalReader(r, v.config)
   227  }
   228  
   229  func initIni() {
   230  	Reset()
   231  	SetConfigType("ini")
   232  	r := bytes.NewReader(iniExample)
   233  
   234  	unmarshalReader(r, v.config)
   235  }
   236  
   237  // initDirs makes directories for testing.
   238  func initDirs(t *testing.T) (string, string) {
   239  	var (
   240  		testDirs = []string{`a a`, `b`, `C_`}
   241  		config   = `improbable`
   242  	)
   243  
   244  	if runtime.GOOS != "windows" {
   245  		testDirs = append(testDirs, `d\d`)
   246  	}
   247  
   248  	root := t.TempDir()
   249  
   250  	for _, dir := range testDirs {
   251  		innerDir := filepath.Join(root, dir)
   252  		err := os.Mkdir(innerDir, 0o750)
   253  		require.NoError(t, err)
   254  
   255  		err = os.WriteFile(
   256  			filepath.Join(innerDir, config+".toml"),
   257  			[]byte(`key = "value is `+dir+`"`+"\n"),
   258  			0o640)
   259  		require.NoError(t, err)
   260  	}
   261  
   262  	return root, config
   263  }
   264  
   265  // stubs for PFlag Values.
   266  type stringValue string
   267  
   268  func newStringValue(val string, p *string) *stringValue {
   269  	*p = val
   270  	return (*stringValue)(p)
   271  }
   272  
   273  func (s *stringValue) Set(val string) error {
   274  	*s = stringValue(val)
   275  	return nil
   276  }
   277  
   278  func (s *stringValue) Type() string {
   279  	return "string"
   280  }
   281  
   282  func (s *stringValue) String() string {
   283  	return string(*s)
   284  }
   285  
   286  func TestGetConfigFile(t *testing.T) {
   287  	t.Run("config file set", func(t *testing.T) {
   288  		fs := afero.NewMemMapFs()
   289  
   290  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   291  		require.NoError(t, err)
   292  
   293  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   294  		require.NoError(t, err)
   295  
   296  		v := New()
   297  
   298  		v.SetFs(fs)
   299  		v.AddConfigPath("/etc/viper")
   300  		v.SetConfigFile(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   301  
   302  		filename, err := v.getConfigFile()
   303  		assert.Equal(t, testutil.AbsFilePath(t, "/etc/viper/config.yaml"), filename)
   304  		assert.NoError(t, err)
   305  	})
   306  
   307  	t.Run("find file", func(t *testing.T) {
   308  		fs := afero.NewMemMapFs()
   309  
   310  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   311  		require.NoError(t, err)
   312  
   313  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   314  		require.NoError(t, err)
   315  
   316  		v := New()
   317  
   318  		v.SetFs(fs)
   319  		v.AddConfigPath("/etc/viper")
   320  
   321  		filename, err := v.getConfigFile()
   322  		assert.Equal(t, testutil.AbsFilePath(t, "/etc/viper/config.yaml"), filename)
   323  		assert.NoError(t, err)
   324  	})
   325  
   326  	t.Run("find files only", func(t *testing.T) {
   327  		fs := afero.NewMemMapFs()
   328  
   329  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/config"), 0o777)
   330  		require.NoError(t, err)
   331  
   332  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/config/config.yaml"))
   333  		require.NoError(t, err)
   334  
   335  		v := New()
   336  
   337  		v.SetFs(fs)
   338  		v.AddConfigPath("/etc")
   339  		v.AddConfigPath("/etc/config")
   340  
   341  		filename, err := v.getConfigFile()
   342  		assert.Equal(t, testutil.AbsFilePath(t, "/etc/config/config.yaml"), filename)
   343  		assert.NoError(t, err)
   344  	})
   345  
   346  	t.Run("precedence", func(t *testing.T) {
   347  		fs := afero.NewMemMapFs()
   348  
   349  		err := fs.Mkdir(testutil.AbsFilePath(t, "/home/viper"), 0o777)
   350  		require.NoError(t, err)
   351  
   352  		_, err = fs.Create(testutil.AbsFilePath(t, "/home/viper/config.zml"))
   353  		require.NoError(t, err)
   354  
   355  		err = fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   356  		require.NoError(t, err)
   357  
   358  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.bml"))
   359  		require.NoError(t, err)
   360  
   361  		err = fs.Mkdir(testutil.AbsFilePath(t, "/var/viper"), 0o777)
   362  		require.NoError(t, err)
   363  
   364  		_, err = fs.Create(testutil.AbsFilePath(t, "/var/viper/config.yaml"))
   365  		require.NoError(t, err)
   366  
   367  		v := New()
   368  
   369  		v.SetFs(fs)
   370  		v.AddConfigPath("/home/viper")
   371  		v.AddConfigPath("/etc/viper")
   372  		v.AddConfigPath("/var/viper")
   373  
   374  		filename, err := v.getConfigFile()
   375  		assert.Equal(t, testutil.AbsFilePath(t, "/var/viper/config.yaml"), filename)
   376  		assert.NoError(t, err)
   377  	})
   378  
   379  	t.Run("without extension", func(t *testing.T) {
   380  		fs := afero.NewMemMapFs()
   381  
   382  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   383  		require.NoError(t, err)
   384  
   385  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/.dotfilenoext"))
   386  		require.NoError(t, err)
   387  
   388  		v := New()
   389  
   390  		v.SetFs(fs)
   391  		v.AddConfigPath("/etc/viper")
   392  		v.SetConfigName(".dotfilenoext")
   393  		v.SetConfigType("yaml")
   394  
   395  		filename, err := v.getConfigFile()
   396  		assert.Equal(t, testutil.AbsFilePath(t, "/etc/viper/.dotfilenoext"), filename)
   397  		assert.NoError(t, err)
   398  	})
   399  
   400  	t.Run("without extension and config type", func(t *testing.T) {
   401  		fs := afero.NewMemMapFs()
   402  
   403  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   404  		require.NoError(t, err)
   405  
   406  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/.dotfilenoext"))
   407  		require.NoError(t, err)
   408  
   409  		v := New()
   410  
   411  		v.SetFs(fs)
   412  		v.AddConfigPath("/etc/viper")
   413  		v.SetConfigName(".dotfilenoext")
   414  
   415  		_, err = v.getConfigFile()
   416  		// unless config type is set, files without extension
   417  		// are not considered
   418  		assert.Error(t, err)
   419  	})
   420  }
   421  
   422  func TestReadInConfig(t *testing.T) {
   423  	t.Run("config file set", func(t *testing.T) {
   424  		fs := afero.NewMemMapFs()
   425  
   426  		err := fs.Mkdir("/etc/viper", 0o777)
   427  		require.NoError(t, err)
   428  
   429  		file, err := fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   430  		require.NoError(t, err)
   431  
   432  		_, err = file.WriteString(`key: value`)
   433  		require.NoError(t, err)
   434  
   435  		file.Close()
   436  
   437  		v := New()
   438  
   439  		v.SetFs(fs)
   440  		v.SetConfigFile(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   441  
   442  		err = v.ReadInConfig()
   443  		require.NoError(t, err)
   444  
   445  		assert.Equal(t, "value", v.Get("key"))
   446  	})
   447  
   448  	t.Run("find file", func(t *testing.T) {
   449  		fs := afero.NewMemMapFs()
   450  
   451  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   452  		require.NoError(t, err)
   453  
   454  		file, err := fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   455  		require.NoError(t, err)
   456  
   457  		_, err = file.WriteString(`key: value`)
   458  		require.NoError(t, err)
   459  
   460  		file.Close()
   461  
   462  		v := New()
   463  
   464  		v.SetFs(fs)
   465  		v.AddConfigPath("/etc/viper")
   466  
   467  		err = v.ReadInConfig()
   468  		require.NoError(t, err)
   469  
   470  		assert.Equal(t, "value", v.Get("key"))
   471  	})
   472  }
   473  
   474  func TestDefault(t *testing.T) {
   475  	Reset()
   476  	SetDefault("age", 45)
   477  	assert.Equal(t, 45, Get("age"))
   478  
   479  	SetDefault("clothing.jacket", "slacks")
   480  	assert.Equal(t, "slacks", Get("clothing.jacket"))
   481  
   482  	SetConfigType("yaml")
   483  	err := ReadConfig(bytes.NewBuffer(yamlExample))
   484  
   485  	assert.NoError(t, err)
   486  	assert.Equal(t, "leather", Get("clothing.jacket"))
   487  }
   488  
   489  func TestUnmarshaling(t *testing.T) {
   490  	Reset()
   491  	SetConfigType("yaml")
   492  	r := bytes.NewReader(yamlExample)
   493  
   494  	unmarshalReader(r, v.config)
   495  	assert.True(t, InConfig("name"))
   496  	assert.True(t, InConfig("clothing.jacket"))
   497  	assert.False(t, InConfig("state"))
   498  	assert.False(t, InConfig("clothing.hat"))
   499  	assert.Equal(t, "steve", Get("name"))
   500  	assert.Equal(t, []any{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
   501  	assert.Equal(t, map[string]any{"jacket": "leather", "trousers": "denim", "pants": map[string]any{"size": "large"}}, Get("clothing"))
   502  	assert.Equal(t, 35, Get("age"))
   503  }
   504  
   505  func TestUnmarshalExact(t *testing.T) {
   506  	vip := New()
   507  	target := &testUnmarshalExtra{}
   508  	vip.SetConfigType("yaml")
   509  	r := bytes.NewReader(yamlExampleWithExtras)
   510  	vip.ReadConfig(r)
   511  	err := vip.UnmarshalExact(target)
   512  	assert.Error(t, err, "UnmarshalExact should error when populating a struct from a conf that contains unused fields")
   513  }
   514  
   515  func TestOverrides(t *testing.T) {
   516  	Set("age", 40)
   517  	assert.Equal(t, 40, Get("age"))
   518  }
   519  
   520  func TestDefaultPost(t *testing.T) {
   521  	assert.NotEqual(t, "NYC", Get("state"))
   522  	SetDefault("state", "NYC")
   523  	assert.Equal(t, "NYC", Get("state"))
   524  }
   525  
   526  func TestAliases(t *testing.T) {
   527  	initConfigs()
   528  	Set("age", 40)
   529  	RegisterAlias("years", "age")
   530  	assert.Equal(t, 40, Get("years"))
   531  	Set("years", 45)
   532  	assert.Equal(t, 45, Get("age"))
   533  }
   534  
   535  func TestAliasInConfigFile(t *testing.T) {
   536  	initConfigs()
   537  	// the config file specifies "beard".  If we make this an alias for
   538  	// "hasbeard", we still want the old config file to work with beard.
   539  	RegisterAlias("beard", "hasbeard")
   540  	assert.Equal(t, true, Get("hasbeard"))
   541  	Set("hasbeard", false)
   542  	assert.Equal(t, false, Get("beard"))
   543  }
   544  
   545  func TestYML(t *testing.T) {
   546  	initYAML()
   547  	assert.Equal(t, "steve", Get("name"))
   548  }
   549  
   550  func TestJSON(t *testing.T) {
   551  	initJSON()
   552  	assert.Equal(t, "0001", Get("id"))
   553  }
   554  
   555  func TestProperties(t *testing.T) {
   556  	initProperties()
   557  	assert.Equal(t, "0001", Get("p_id"))
   558  }
   559  
   560  func TestTOML(t *testing.T) {
   561  	initTOML()
   562  	assert.Equal(t, "TOML Example", Get("title"))
   563  }
   564  
   565  func TestDotEnv(t *testing.T) {
   566  	initDotEnv()
   567  	assert.Equal(t, "DotEnv Example", Get("title_dotenv"))
   568  }
   569  
   570  func TestHCL(t *testing.T) {
   571  	initHcl()
   572  	assert.Equal(t, "0001", Get("id"))
   573  	assert.Equal(t, 0.55, Get("ppu"))
   574  	assert.Equal(t, "donut", Get("type"))
   575  	assert.Equal(t, "Cake", Get("name"))
   576  	Set("id", "0002")
   577  	assert.Equal(t, "0002", Get("id"))
   578  	assert.NotEqual(t, "cronut", Get("type"))
   579  }
   580  
   581  func TestIni(t *testing.T) {
   582  	initIni()
   583  	assert.Equal(t, "ini", Get("default.name"))
   584  }
   585  
   586  func TestRemotePrecedence(t *testing.T) {
   587  	initJSON()
   588  
   589  	remote := bytes.NewReader(remoteExample)
   590  	assert.Equal(t, "0001", Get("id"))
   591  	unmarshalReader(remote, v.kvstore)
   592  	assert.Equal(t, "0001", Get("id"))
   593  	assert.NotEqual(t, "cronut", Get("type"))
   594  	assert.Equal(t, "remote", Get("newkey"))
   595  	Set("newkey", "newvalue")
   596  	assert.NotEqual(t, "remote", Get("newkey"))
   597  	assert.Equal(t, "newvalue", Get("newkey"))
   598  	Set("newkey", "remote")
   599  }
   600  
   601  func TestEnv(t *testing.T) {
   602  	initJSON()
   603  
   604  	BindEnv("id")
   605  	BindEnv("f", "FOOD", "OLD_FOOD")
   606  
   607  	t.Setenv("ID", "13")
   608  	t.Setenv("FOOD", "apple")
   609  	t.Setenv("OLD_FOOD", "banana")
   610  	t.Setenv("NAME", "crunk")
   611  
   612  	assert.Equal(t, "13", Get("id"))
   613  	assert.Equal(t, "apple", Get("f"))
   614  	assert.Equal(t, "Cake", Get("name"))
   615  
   616  	AutomaticEnv()
   617  
   618  	assert.Equal(t, "crunk", Get("name"))
   619  }
   620  
   621  func TestMultipleEnv(t *testing.T) {
   622  	initJSON()
   623  
   624  	BindEnv("f", "FOOD", "OLD_FOOD")
   625  
   626  	t.Setenv("OLD_FOOD", "banana")
   627  
   628  	assert.Equal(t, "banana", Get("f"))
   629  }
   630  
   631  func TestEmptyEnv(t *testing.T) {
   632  	initJSON()
   633  
   634  	BindEnv("type") // Empty environment variable
   635  	BindEnv("name") // Bound, but not set environment variable
   636  
   637  	t.Setenv("TYPE", "")
   638  
   639  	assert.Equal(t, "donut", Get("type"))
   640  	assert.Equal(t, "Cake", Get("name"))
   641  }
   642  
   643  func TestEmptyEnv_Allowed(t *testing.T) {
   644  	initJSON()
   645  
   646  	AllowEmptyEnv(true)
   647  
   648  	BindEnv("type") // Empty environment variable
   649  	BindEnv("name") // Bound, but not set environment variable
   650  
   651  	t.Setenv("TYPE", "")
   652  
   653  	assert.Equal(t, "", Get("type"))
   654  	assert.Equal(t, "Cake", Get("name"))
   655  }
   656  
   657  func TestEnvPrefix(t *testing.T) {
   658  	initJSON()
   659  
   660  	SetEnvPrefix("foo") // will be uppercased automatically
   661  	BindEnv("id")
   662  	BindEnv("f", "FOOD") // not using prefix
   663  
   664  	t.Setenv("FOO_ID", "13")
   665  	t.Setenv("FOOD", "apple")
   666  	t.Setenv("FOO_NAME", "crunk")
   667  
   668  	assert.Equal(t, "13", Get("id"))
   669  	assert.Equal(t, "apple", Get("f"))
   670  	assert.Equal(t, "Cake", Get("name"))
   671  
   672  	AutomaticEnv()
   673  
   674  	assert.Equal(t, "crunk", Get("name"))
   675  }
   676  
   677  func TestAutoEnv(t *testing.T) {
   678  	Reset()
   679  
   680  	AutomaticEnv()
   681  
   682  	t.Setenv("FOO_BAR", "13")
   683  
   684  	assert.Equal(t, "13", Get("foo_bar"))
   685  }
   686  
   687  func TestAutoEnvWithPrefix(t *testing.T) {
   688  	Reset()
   689  
   690  	AutomaticEnv()
   691  	SetEnvPrefix("Baz")
   692  
   693  	t.Setenv("BAZ_BAR", "13")
   694  
   695  	assert.Equal(t, "13", Get("bar"))
   696  }
   697  
   698  func TestSetEnvKeyReplacer(t *testing.T) {
   699  	Reset()
   700  
   701  	AutomaticEnv()
   702  
   703  	t.Setenv("REFRESH_INTERVAL", "30s")
   704  
   705  	replacer := strings.NewReplacer("-", "_")
   706  	SetEnvKeyReplacer(replacer)
   707  
   708  	assert.Equal(t, "30s", Get("refresh-interval"))
   709  }
   710  
   711  func TestEnvKeyReplacer(t *testing.T) {
   712  	v := NewWithOptions(EnvKeyReplacer(strings.NewReplacer("-", "_")))
   713  
   714  	v.AutomaticEnv()
   715  
   716  	t.Setenv("REFRESH_INTERVAL", "30s")
   717  
   718  	assert.Equal(t, "30s", v.Get("refresh-interval"))
   719  }
   720  
   721  func TestEnvSubConfig(t *testing.T) {
   722  	initYAML()
   723  
   724  	v.AutomaticEnv()
   725  
   726  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
   727  
   728  	t.Setenv("CLOTHING_PANTS_SIZE", "small")
   729  	subv := v.Sub("clothing").Sub("pants")
   730  	assert.Equal(t, "small", subv.Get("size"))
   731  
   732  	// again with EnvPrefix
   733  	v.SetEnvPrefix("foo") // will be uppercased automatically
   734  	subWithPrefix := v.Sub("clothing").Sub("pants")
   735  	t.Setenv("FOO_CLOTHING_PANTS_SIZE", "large")
   736  	assert.Equal(t, "large", subWithPrefix.Get("size"))
   737  }
   738  
   739  func TestAllKeys(t *testing.T) {
   740  	initConfigs()
   741  
   742  	ks := []string{
   743  		"title",
   744  		"author.bio",
   745  		"author.e-mail",
   746  		"author.github",
   747  		"author.name",
   748  		"newkey",
   749  		"owner.organization",
   750  		"owner.dob",
   751  		"owner.bio",
   752  		"name",
   753  		"beard",
   754  		"ppu",
   755  		"batters.batter",
   756  		"hobbies",
   757  		"clothing.jacket",
   758  		"clothing.trousers",
   759  		"default.import_path",
   760  		"default.name",
   761  		"default.version",
   762  		"clothing.pants.size",
   763  		"age",
   764  		"hacker",
   765  		"id",
   766  		"type",
   767  		"eyes",
   768  		"p_id",
   769  		"p_ppu",
   770  		"p_batters.batter.type",
   771  		"p_type",
   772  		"p_name",
   773  		"foos",
   774  		"title_dotenv",
   775  		"type_dotenv",
   776  		"name_dotenv",
   777  	}
   778  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
   779  	all := map[string]any{
   780  		"owner": map[string]any{
   781  			"organization": "MongoDB",
   782  			"bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
   783  			"dob":          dob,
   784  		},
   785  		"title": "TOML Example",
   786  		"author": map[string]any{
   787  			"e-mail": "fake@localhost",
   788  			"github": "https://github.com/Unknown",
   789  			"name":   "Unknown",
   790  			"bio":    "Gopher.\nCoding addict.\nGood man.\n",
   791  		},
   792  		"ppu":  0.55,
   793  		"eyes": "brown",
   794  		"clothing": map[string]any{
   795  			"trousers": "denim",
   796  			"jacket":   "leather",
   797  			"pants":    map[string]any{"size": "large"},
   798  		},
   799  		"default": map[string]any{
   800  			"import_path": "gopkg.in/ini.v1",
   801  			"name":        "ini",
   802  			"version":     "v1",
   803  		},
   804  		"id": "0001",
   805  		"batters": map[string]any{
   806  			"batter": []any{
   807  				map[string]any{"type": "Regular"},
   808  				map[string]any{"type": "Chocolate"},
   809  				map[string]any{"type": "Blueberry"},
   810  				map[string]any{"type": "Devil's Food"},
   811  			},
   812  		},
   813  		"hacker": true,
   814  		"beard":  true,
   815  		"hobbies": []any{
   816  			"skateboarding",
   817  			"snowboarding",
   818  			"go",
   819  		},
   820  		"age":    35,
   821  		"type":   "donut",
   822  		"newkey": "remote",
   823  		"name":   "Cake",
   824  		"p_id":   "0001",
   825  		"p_ppu":  "0.55",
   826  		"p_name": "Cake",
   827  		"p_batters": map[string]any{
   828  			"batter": map[string]any{"type": "Regular"},
   829  		},
   830  		"p_type": "donut",
   831  		"foos": []map[string]any{
   832  			{
   833  				"foo": []map[string]any{
   834  					{"key": 1},
   835  					{"key": 2},
   836  					{"key": 3},
   837  					{"key": 4},
   838  				},
   839  			},
   840  		},
   841  		"title_dotenv": "DotEnv Example",
   842  		"type_dotenv":  "donut",
   843  		"name_dotenv":  "Cake",
   844  	}
   845  
   846  	assert.ElementsMatch(t, ks, AllKeys())
   847  	assert.Equal(t, all, AllSettings())
   848  }
   849  
   850  func TestAllKeysWithEnv(t *testing.T) {
   851  	v := New()
   852  
   853  	// bind and define environment variables (including a nested one)
   854  	v.BindEnv("id")
   855  	v.BindEnv("foo.bar")
   856  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
   857  
   858  	t.Setenv("ID", "13")
   859  	t.Setenv("FOO_BAR", "baz")
   860  
   861  	assert.ElementsMatch(t, []string{"id", "foo.bar"}, v.AllKeys())
   862  }
   863  
   864  func TestAliasesOfAliases(t *testing.T) {
   865  	Set("Title", "Checking Case")
   866  	RegisterAlias("Foo", "Bar")
   867  	RegisterAlias("Bar", "Title")
   868  	assert.Equal(t, "Checking Case", Get("FOO"))
   869  }
   870  
   871  func TestRecursiveAliases(t *testing.T) {
   872  	Set("baz", "bat")
   873  	RegisterAlias("Baz", "Roo")
   874  	RegisterAlias("Roo", "baz")
   875  	assert.Equal(t, "bat", Get("Baz"))
   876  }
   877  
   878  func TestUnmarshal(t *testing.T) {
   879  	Reset()
   880  	SetDefault("port", 1313)
   881  	Set("name", "Steve")
   882  	Set("duration", "1s1ms")
   883  	Set("modes", []int{1, 2, 3})
   884  
   885  	type config struct {
   886  		Port     int
   887  		Name     string
   888  		Duration time.Duration
   889  		Modes    []int
   890  	}
   891  
   892  	var C config
   893  
   894  	err := Unmarshal(&C)
   895  	require.NoError(t, err, "unable to decode into struct")
   896  
   897  	assert.Equal(
   898  		t,
   899  		&config{
   900  			Name:     "Steve",
   901  			Port:     1313,
   902  			Duration: time.Second + time.Millisecond,
   903  			Modes:    []int{1, 2, 3},
   904  		},
   905  		&C,
   906  	)
   907  
   908  	Set("port", 1234)
   909  	err = Unmarshal(&C)
   910  	require.NoError(t, err, "unable to decode into struct")
   911  
   912  	assert.Equal(
   913  		t,
   914  		&config{
   915  			Name:     "Steve",
   916  			Port:     1234,
   917  			Duration: time.Second + time.Millisecond,
   918  			Modes:    []int{1, 2, 3},
   919  		},
   920  		&C,
   921  	)
   922  }
   923  
   924  func TestUnmarshalWithDecoderOptions(t *testing.T) {
   925  	Set("credentials", "{\"foo\":\"bar\"}")
   926  
   927  	opt := DecodeHook(mapstructure.ComposeDecodeHookFunc(
   928  		mapstructure.StringToTimeDurationHookFunc(),
   929  		mapstructure.StringToSliceHookFunc(","),
   930  		// Custom Decode Hook Function
   931  		func(rf reflect.Kind, rt reflect.Kind, data any) (any, error) {
   932  			if rf != reflect.String || rt != reflect.Map {
   933  				return data, nil
   934  			}
   935  			m := map[string]string{}
   936  			raw := data.(string)
   937  			if raw == "" {
   938  				return m, nil
   939  			}
   940  			err := json.Unmarshal([]byte(raw), &m)
   941  			return m, err
   942  		},
   943  	))
   944  
   945  	type config struct {
   946  		Credentials map[string]string
   947  	}
   948  
   949  	var C config
   950  
   951  	err := Unmarshal(&C, opt)
   952  	require.NoError(t, err, "unable to decode into struct")
   953  
   954  	assert.Equal(t, &config{
   955  		Credentials: map[string]string{"foo": "bar"},
   956  	}, &C)
   957  }
   958  
   959  func TestUnmarshalWithAutomaticEnv(t *testing.T) {
   960  	if !features.BindStruct {
   961  		t.Skip("binding struct is not enabled")
   962  	}
   963  
   964  	t.Setenv("PORT", "1313")
   965  	t.Setenv("NAME", "Steve")
   966  	t.Setenv("DURATION", "1s1ms")
   967  	t.Setenv("MODES", "1,2,3")
   968  	t.Setenv("SECRET", "42")
   969  	t.Setenv("FILESYSTEM_SIZE", "4096")
   970  
   971  	type AuthConfig struct {
   972  		Secret string `mapstructure:"secret"`
   973  	}
   974  
   975  	type StorageConfig struct {
   976  		Size int `mapstructure:"size"`
   977  	}
   978  
   979  	type Configuration struct {
   980  		Port     int           `mapstructure:"port"`
   981  		Name     string        `mapstructure:"name"`
   982  		Duration time.Duration `mapstructure:"duration"`
   983  
   984  		// Infer name from struct
   985  		Modes []int
   986  
   987  		// Squash nested struct (omit prefix)
   988  		Authentication AuthConfig `mapstructure:",squash"`
   989  
   990  		// Different key
   991  		Storage StorageConfig `mapstructure:"filesystem"`
   992  
   993  		// Omitted field
   994  		Flag bool `mapstructure:"flag"`
   995  	}
   996  
   997  	v := New()
   998  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
   999  	v.AutomaticEnv()
  1000  
  1001  	t.Run("OK", func(t *testing.T) {
  1002  		var config Configuration
  1003  		if err := v.Unmarshal(&config); err != nil {
  1004  			t.Fatalf("unable to decode into struct, %v", err)
  1005  		}
  1006  
  1007  		assert.Equal(
  1008  			t,
  1009  			Configuration{
  1010  				Name:     "Steve",
  1011  				Port:     1313,
  1012  				Duration: time.Second + time.Millisecond,
  1013  				Modes:    []int{1, 2, 3},
  1014  				Authentication: AuthConfig{
  1015  					Secret: "42",
  1016  				},
  1017  				Storage: StorageConfig{
  1018  					Size: 4096,
  1019  				},
  1020  			},
  1021  			config,
  1022  		)
  1023  	})
  1024  
  1025  	t.Run("Precedence", func(t *testing.T) {
  1026  		var config Configuration
  1027  
  1028  		v.Set("port", 1234)
  1029  		if err := v.Unmarshal(&config); err != nil {
  1030  			t.Fatalf("unable to decode into struct, %v", err)
  1031  		}
  1032  
  1033  		assert.Equal(
  1034  			t,
  1035  			Configuration{
  1036  				Name:     "Steve",
  1037  				Port:     1234,
  1038  				Duration: time.Second + time.Millisecond,
  1039  				Modes:    []int{1, 2, 3},
  1040  				Authentication: AuthConfig{
  1041  					Secret: "42",
  1042  				},
  1043  				Storage: StorageConfig{
  1044  					Size: 4096,
  1045  				},
  1046  			},
  1047  			config,
  1048  		)
  1049  	})
  1050  
  1051  	t.Run("Unset", func(t *testing.T) {
  1052  		var config Configuration
  1053  
  1054  		err := v.Unmarshal(&config, func(config *mapstructure.DecoderConfig) {
  1055  			config.ErrorUnset = true
  1056  		})
  1057  
  1058  		assert.Error(t, err, "expected viper.Unmarshal to return error due to unset field 'FLAG'")
  1059  	})
  1060  
  1061  	t.Run("Exact", func(t *testing.T) {
  1062  		var config Configuration
  1063  
  1064  		v.Set("port", 1234)
  1065  		if err := v.UnmarshalExact(&config); err != nil {
  1066  			t.Fatalf("unable to decode into struct, %v", err)
  1067  		}
  1068  
  1069  		assert.Equal(
  1070  			t,
  1071  			Configuration{
  1072  				Name:     "Steve",
  1073  				Port:     1234,
  1074  				Duration: time.Second + time.Millisecond,
  1075  				Modes:    []int{1, 2, 3},
  1076  				Authentication: AuthConfig{
  1077  					Secret: "42",
  1078  				},
  1079  				Storage: StorageConfig{
  1080  					Size: 4096,
  1081  				},
  1082  			},
  1083  			config,
  1084  		)
  1085  	})
  1086  }
  1087  
  1088  func TestBindPFlags(t *testing.T) {
  1089  	v := New() // create independent Viper object
  1090  	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1091  
  1092  	testValues := map[string]*string{
  1093  		"host":     nil,
  1094  		"port":     nil,
  1095  		"endpoint": nil,
  1096  	}
  1097  
  1098  	mutatedTestValues := map[string]string{
  1099  		"host":     "localhost",
  1100  		"port":     "6060",
  1101  		"endpoint": "/public",
  1102  	}
  1103  
  1104  	for name := range testValues {
  1105  		testValues[name] = flagSet.String(name, "", "test")
  1106  	}
  1107  
  1108  	err := v.BindPFlags(flagSet)
  1109  	require.NoError(t, err, "error binding flag set")
  1110  
  1111  	flagSet.VisitAll(func(flag *pflag.Flag) {
  1112  		flag.Value.Set(mutatedTestValues[flag.Name])
  1113  		flag.Changed = true
  1114  	})
  1115  
  1116  	for name, expected := range mutatedTestValues {
  1117  		assert.Equal(t, expected, v.Get(name))
  1118  	}
  1119  }
  1120  
  1121  func TestBindPFlagsStringSlice(t *testing.T) {
  1122  	tests := []struct {
  1123  		Expected []string
  1124  		Value    string
  1125  	}{
  1126  		{[]string{}, ""},
  1127  		{[]string{"jeden"}, "jeden"},
  1128  		{[]string{"dwa", "trzy"}, "dwa,trzy"},
  1129  		{[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""},
  1130  	}
  1131  
  1132  	v := New() // create independent Viper object
  1133  	defaultVal := []string{"default"}
  1134  	v.SetDefault("stringslice", defaultVal)
  1135  
  1136  	for _, testValue := range tests {
  1137  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1138  		flagSet.StringSlice("stringslice", testValue.Expected, "test")
  1139  
  1140  		for _, changed := range []bool{true, false} {
  1141  			flagSet.VisitAll(func(f *pflag.Flag) {
  1142  				f.Value.Set(testValue.Value)
  1143  				f.Changed = changed
  1144  			})
  1145  
  1146  			err := v.BindPFlags(flagSet)
  1147  			require.NoError(t, err, "error binding flag set")
  1148  
  1149  			type TestStr struct {
  1150  				StringSlice []string
  1151  			}
  1152  			val := &TestStr{}
  1153  			err = v.Unmarshal(val)
  1154  			require.NoError(t, err, "cannot unmarshal")
  1155  			if changed {
  1156  				assert.Equal(t, testValue.Expected, val.StringSlice)
  1157  				assert.Equal(t, testValue.Expected, v.Get("stringslice"))
  1158  			} else {
  1159  				assert.Equal(t, defaultVal, val.StringSlice)
  1160  			}
  1161  		}
  1162  	}
  1163  }
  1164  
  1165  func TestBindPFlagsStringArray(t *testing.T) {
  1166  	tests := []struct {
  1167  		Expected []string
  1168  		Value    string
  1169  	}{
  1170  		{[]string{}, ""},
  1171  		{[]string{"jeden"}, "jeden"},
  1172  		{[]string{"dwa,trzy"}, "dwa,trzy"},
  1173  		{[]string{"cztery,\"piec , szesc\""}, "cztery,\"piec , szesc\""},
  1174  	}
  1175  
  1176  	v := New() // create independent Viper object
  1177  	defaultVal := []string{"default"}
  1178  	v.SetDefault("stringarray", defaultVal)
  1179  
  1180  	for _, testValue := range tests {
  1181  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1182  		flagSet.StringArray("stringarray", testValue.Expected, "test")
  1183  
  1184  		for _, changed := range []bool{true, false} {
  1185  			flagSet.VisitAll(func(f *pflag.Flag) {
  1186  				f.Value.Set(testValue.Value)
  1187  				f.Changed = changed
  1188  			})
  1189  
  1190  			err := v.BindPFlags(flagSet)
  1191  			require.NoError(t, err, "error binding flag set")
  1192  
  1193  			type TestStr struct {
  1194  				StringArray []string
  1195  			}
  1196  			val := &TestStr{}
  1197  			err = v.Unmarshal(val)
  1198  			require.NoError(t, err, "cannot unmarshal")
  1199  			if changed {
  1200  				assert.Equal(t, testValue.Expected, val.StringArray)
  1201  				assert.Equal(t, testValue.Expected, v.Get("stringarray"))
  1202  			} else {
  1203  				assert.Equal(t, defaultVal, val.StringArray)
  1204  			}
  1205  		}
  1206  	}
  1207  }
  1208  
  1209  func TestSliceFlagsReturnCorrectType(t *testing.T) {
  1210  	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1211  	flagSet.IntSlice("int", []int{1, 2}, "")
  1212  	flagSet.StringSlice("str", []string{"3", "4"}, "")
  1213  	flagSet.DurationSlice("duration", []time.Duration{5 * time.Second}, "")
  1214  
  1215  	v := New()
  1216  	v.BindPFlags(flagSet)
  1217  
  1218  	all := v.AllSettings()
  1219  
  1220  	assert.IsType(t, []int{}, all["int"])
  1221  	assert.IsType(t, []string{}, all["str"])
  1222  	assert.IsType(t, []time.Duration{}, all["duration"])
  1223  }
  1224  
  1225  func TestBindPFlagsIntSlice(t *testing.T) {
  1226  	tests := []struct {
  1227  		Expected []int
  1228  		Value    string
  1229  	}{
  1230  		{[]int{}, ""},
  1231  		{[]int{1}, "1"},
  1232  		{[]int{2, 3}, "2,3"},
  1233  	}
  1234  
  1235  	v := New() // create independent Viper object
  1236  	defaultVal := []int{0}
  1237  	v.SetDefault("intslice", defaultVal)
  1238  
  1239  	for _, testValue := range tests {
  1240  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1241  		flagSet.IntSlice("intslice", testValue.Expected, "test")
  1242  
  1243  		for _, changed := range []bool{true, false} {
  1244  			flagSet.VisitAll(func(f *pflag.Flag) {
  1245  				f.Value.Set(testValue.Value)
  1246  				f.Changed = changed
  1247  			})
  1248  
  1249  			err := v.BindPFlags(flagSet)
  1250  			require.NoError(t, err, "error binding flag set")
  1251  
  1252  			type TestInt struct {
  1253  				IntSlice []int
  1254  			}
  1255  			val := &TestInt{}
  1256  			err = v.Unmarshal(val)
  1257  			require.NoError(t, err, "cannot unmarshal")
  1258  			if changed {
  1259  				assert.Equal(t, testValue.Expected, val.IntSlice)
  1260  				assert.Equal(t, testValue.Expected, v.Get("intslice"))
  1261  			} else {
  1262  				assert.Equal(t, defaultVal, val.IntSlice)
  1263  			}
  1264  		}
  1265  	}
  1266  }
  1267  
  1268  func TestBindPFlag(t *testing.T) {
  1269  	testString := "testing"
  1270  	testValue := newStringValue(testString, &testString)
  1271  
  1272  	flag := &pflag.Flag{
  1273  		Name:    "testflag",
  1274  		Value:   testValue,
  1275  		Changed: false,
  1276  	}
  1277  
  1278  	BindPFlag("testvalue", flag)
  1279  
  1280  	assert.Equal(t, testString, Get("testvalue"))
  1281  
  1282  	flag.Value.Set("testing_mutate")
  1283  	flag.Changed = true // hack for pflag usage
  1284  
  1285  	assert.Equal(t, "testing_mutate", Get("testvalue"))
  1286  }
  1287  
  1288  func TestBindPFlagDetectNilFlag(t *testing.T) {
  1289  	result := BindPFlag("testvalue", nil)
  1290  	assert.Error(t, result)
  1291  }
  1292  
  1293  func TestBindPFlagStringToString(t *testing.T) {
  1294  	tests := []struct {
  1295  		Expected map[string]string
  1296  		Value    string
  1297  	}{
  1298  		{map[string]string{}, ""},
  1299  		{map[string]string{"yo": "hi"}, "yo=hi"},
  1300  		{map[string]string{"yo": "hi", "oh": "hi=there"}, "yo=hi,oh=hi=there"},
  1301  		{map[string]string{"yo": ""}, "yo="},
  1302  		{map[string]string{"yo": "", "oh": "hi=there"}, "yo=,oh=hi=there"},
  1303  	}
  1304  
  1305  	v := New() // create independent Viper object
  1306  	defaultVal := map[string]string{}
  1307  	v.SetDefault("stringtostring", defaultVal)
  1308  
  1309  	for _, testValue := range tests {
  1310  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1311  		flagSet.StringToString("stringtostring", testValue.Expected, "test")
  1312  
  1313  		for _, changed := range []bool{true, false} {
  1314  			flagSet.VisitAll(func(f *pflag.Flag) {
  1315  				f.Value.Set(testValue.Value)
  1316  				f.Changed = changed
  1317  			})
  1318  
  1319  			err := v.BindPFlags(flagSet)
  1320  			require.NoError(t, err, "error binding flag set")
  1321  
  1322  			type TestMap struct {
  1323  				StringToString map[string]string
  1324  			}
  1325  			val := &TestMap{}
  1326  			err = v.Unmarshal(val)
  1327  			require.NoError(t, err, "cannot unmarshal")
  1328  			if changed {
  1329  				assert.Equal(t, testValue.Expected, val.StringToString)
  1330  			} else {
  1331  				assert.Equal(t, defaultVal, val.StringToString)
  1332  			}
  1333  		}
  1334  	}
  1335  }
  1336  
  1337  func TestBindPFlagStringToInt(t *testing.T) {
  1338  	tests := []struct {
  1339  		Expected map[string]int
  1340  		Value    string
  1341  	}{
  1342  		{map[string]int{"yo": 1, "oh": 21}, "yo=1,oh=21"},
  1343  		{map[string]int{"yo": 100000000, "oh": 0}, "yo=100000000,oh=0"},
  1344  		{map[string]int{}, "yo=2,oh=21.0"},
  1345  		{map[string]int{}, "yo=,oh=20.99"},
  1346  		{map[string]int{}, "yo=,oh="},
  1347  	}
  1348  
  1349  	v := New() // create independent Viper object
  1350  	defaultVal := map[string]int{}
  1351  	v.SetDefault("stringtoint", defaultVal)
  1352  
  1353  	for _, testValue := range tests {
  1354  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1355  		flagSet.StringToInt("stringtoint", testValue.Expected, "test")
  1356  
  1357  		for _, changed := range []bool{true, false} {
  1358  			flagSet.VisitAll(func(f *pflag.Flag) {
  1359  				f.Value.Set(testValue.Value)
  1360  				f.Changed = changed
  1361  			})
  1362  
  1363  			err := v.BindPFlags(flagSet)
  1364  			require.NoError(t, err, "error binding flag set")
  1365  
  1366  			type TestMap struct {
  1367  				StringToInt map[string]int
  1368  			}
  1369  			val := &TestMap{}
  1370  			err = v.Unmarshal(val)
  1371  			require.NoError(t, err, "cannot unmarshal")
  1372  			if changed {
  1373  				assert.Equal(t, testValue.Expected, val.StringToInt)
  1374  			} else {
  1375  				assert.Equal(t, defaultVal, val.StringToInt)
  1376  			}
  1377  		}
  1378  	}
  1379  }
  1380  
  1381  func TestBoundCaseSensitivity(t *testing.T) {
  1382  	initConfigs()
  1383  	assert.Equal(t, "brown", Get("eyes"))
  1384  
  1385  	BindEnv("eYEs", "TURTLE_EYES")
  1386  
  1387  	t.Setenv("TURTLE_EYES", "blue")
  1388  
  1389  	assert.Equal(t, "blue", Get("eyes"))
  1390  
  1391  	testString := "green"
  1392  	testValue := newStringValue(testString, &testString)
  1393  
  1394  	flag := &pflag.Flag{
  1395  		Name:    "eyeballs",
  1396  		Value:   testValue,
  1397  		Changed: true,
  1398  	}
  1399  
  1400  	BindPFlag("eYEs", flag)
  1401  	assert.Equal(t, "green", Get("eyes"))
  1402  }
  1403  
  1404  func TestSizeInBytes(t *testing.T) {
  1405  	input := map[string]uint{
  1406  		"":               0,
  1407  		"b":              0,
  1408  		"12 bytes":       0,
  1409  		"200000000000gb": 0,
  1410  		"12 b":           12,
  1411  		"43 MB":          43 * (1 << 20),
  1412  		"10mb":           10 * (1 << 20),
  1413  		"1gb":            1 << 30,
  1414  	}
  1415  
  1416  	for str, expected := range input {
  1417  		assert.Equal(t, expected, parseSizeInBytes(str), str)
  1418  	}
  1419  }
  1420  
  1421  func TestFindsNestedKeys(t *testing.T) {
  1422  	initConfigs()
  1423  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
  1424  
  1425  	Set("super", map[string]any{
  1426  		"deep": map[string]any{
  1427  			"nested": "value",
  1428  		},
  1429  	})
  1430  
  1431  	expected := map[string]any{
  1432  		"super": map[string]any{
  1433  			"deep": map[string]any{
  1434  				"nested": "value",
  1435  			},
  1436  		},
  1437  		"super.deep": map[string]any{
  1438  			"nested": "value",
  1439  		},
  1440  		"super.deep.nested":  "value",
  1441  		"owner.organization": "MongoDB",
  1442  		"batters.batter": []any{
  1443  			map[string]any{
  1444  				"type": "Regular",
  1445  			},
  1446  			map[string]any{
  1447  				"type": "Chocolate",
  1448  			},
  1449  			map[string]any{
  1450  				"type": "Blueberry",
  1451  			},
  1452  			map[string]any{
  1453  				"type": "Devil's Food",
  1454  			},
  1455  		},
  1456  		"hobbies": []any{
  1457  			"skateboarding", "snowboarding", "go",
  1458  		},
  1459  		"TITLE_DOTENV": "DotEnv Example",
  1460  		"TYPE_DOTENV":  "donut",
  1461  		"NAME_DOTENV":  "Cake",
  1462  		"title":        "TOML Example",
  1463  		"newkey":       "remote",
  1464  		"batters": map[string]any{
  1465  			"batter": []any{
  1466  				map[string]any{
  1467  					"type": "Regular",
  1468  				},
  1469  				map[string]any{
  1470  					"type": "Chocolate",
  1471  				},
  1472  				map[string]any{
  1473  					"type": "Blueberry",
  1474  				},
  1475  				map[string]any{
  1476  					"type": "Devil's Food",
  1477  				},
  1478  			},
  1479  		},
  1480  		"eyes": "brown",
  1481  		"age":  35,
  1482  		"owner": map[string]any{
  1483  			"organization": "MongoDB",
  1484  			"bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
  1485  			"dob":          dob,
  1486  		},
  1487  		"owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large",
  1488  		"type":      "donut",
  1489  		"id":        "0001",
  1490  		"name":      "Cake",
  1491  		"hacker":    true,
  1492  		"ppu":       0.55,
  1493  		"clothing": map[string]any{
  1494  			"jacket":   "leather",
  1495  			"trousers": "denim",
  1496  			"pants": map[string]any{
  1497  				"size": "large",
  1498  			},
  1499  		},
  1500  		"clothing.jacket":     "leather",
  1501  		"clothing.pants.size": "large",
  1502  		"clothing.trousers":   "denim",
  1503  		"owner.dob":           dob,
  1504  		"beard":               true,
  1505  		"foos": []map[string]any{
  1506  			{
  1507  				"foo": []map[string]any{
  1508  					{
  1509  						"key": 1,
  1510  					},
  1511  					{
  1512  						"key": 2,
  1513  					},
  1514  					{
  1515  						"key": 3,
  1516  					},
  1517  					{
  1518  						"key": 4,
  1519  					},
  1520  				},
  1521  			},
  1522  		},
  1523  	}
  1524  
  1525  	for key, expectedValue := range expected {
  1526  		assert.Equal(t, expectedValue, v.Get(key))
  1527  	}
  1528  }
  1529  
  1530  func TestReadBufConfig(t *testing.T) {
  1531  	v := New()
  1532  	v.SetConfigType("yaml")
  1533  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1534  	t.Log(v.AllKeys())
  1535  
  1536  	assert.True(t, v.InConfig("name"))
  1537  	assert.True(t, v.InConfig("clothing.jacket"))
  1538  	assert.False(t, v.InConfig("state"))
  1539  	assert.False(t, v.InConfig("clothing.hat"))
  1540  	assert.Equal(t, "steve", v.Get("name"))
  1541  	assert.Equal(t, []any{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
  1542  	assert.Equal(t, map[string]any{"jacket": "leather", "trousers": "denim", "pants": map[string]any{"size": "large"}}, v.Get("clothing"))
  1543  	assert.Equal(t, 35, v.Get("age"))
  1544  }
  1545  
  1546  func TestIsSet(t *testing.T) {
  1547  	v := New()
  1548  	v.SetConfigType("yaml")
  1549  
  1550  	/* config and defaults */
  1551  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1552  	v.SetDefault("clothing.shoes", "sneakers")
  1553  
  1554  	assert.True(t, v.IsSet("clothing"))
  1555  	assert.True(t, v.IsSet("clothing.jacket"))
  1556  	assert.False(t, v.IsSet("clothing.jackets"))
  1557  	assert.True(t, v.IsSet("clothing.shoes"))
  1558  
  1559  	/* state change */
  1560  	assert.False(t, v.IsSet("helloworld"))
  1561  	v.Set("helloworld", "fubar")
  1562  	assert.True(t, v.IsSet("helloworld"))
  1563  
  1564  	/* env */
  1565  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
  1566  	v.BindEnv("eyes")
  1567  	v.BindEnv("foo")
  1568  	v.BindEnv("clothing.hat")
  1569  	v.BindEnv("clothing.hats")
  1570  
  1571  	t.Setenv("FOO", "bar")
  1572  	t.Setenv("CLOTHING_HAT", "bowler")
  1573  
  1574  	assert.True(t, v.IsSet("eyes"))           // in the config file
  1575  	assert.True(t, v.IsSet("foo"))            // in the environment
  1576  	assert.True(t, v.IsSet("clothing.hat"))   // in the environment
  1577  	assert.False(t, v.IsSet("clothing.hats")) // not defined
  1578  
  1579  	/* flags */
  1580  	flagset := pflag.NewFlagSet("testisset", pflag.ContinueOnError)
  1581  	flagset.Bool("foobaz", false, "foobaz")
  1582  	flagset.Bool("barbaz", false, "barbaz")
  1583  	foobaz, barbaz := flagset.Lookup("foobaz"), flagset.Lookup("barbaz")
  1584  	v.BindPFlag("foobaz", foobaz)
  1585  	v.BindPFlag("barbaz", barbaz)
  1586  	barbaz.Value.Set("true")
  1587  	barbaz.Changed = true // hack for pflag usage
  1588  
  1589  	assert.False(t, v.IsSet("foobaz"))
  1590  	assert.True(t, v.IsSet("barbaz"))
  1591  }
  1592  
  1593  func TestDirsSearch(t *testing.T) {
  1594  	root, config := initDirs(t)
  1595  
  1596  	v := New()
  1597  	v.SetConfigName(config)
  1598  	v.SetDefault(`key`, `default`)
  1599  
  1600  	entries, err := os.ReadDir(root)
  1601  	require.NoError(t, err)
  1602  	for _, e := range entries {
  1603  		if e.IsDir() {
  1604  			v.AddConfigPath(filepath.Join(root, e.Name()))
  1605  		}
  1606  	}
  1607  
  1608  	err = v.ReadInConfig()
  1609  	require.NoError(t, err)
  1610  
  1611  	assert.Equal(t, `value is `+filepath.Base(v.configPaths[0]), v.GetString(`key`))
  1612  }
  1613  
  1614  func TestWrongDirsSearchNotFound(t *testing.T) {
  1615  	_, config := initDirs(t)
  1616  
  1617  	v := New()
  1618  	v.SetConfigName(config)
  1619  	v.SetDefault(`key`, `default`)
  1620  
  1621  	v.AddConfigPath(`whattayoutalkingbout`)
  1622  	v.AddConfigPath(`thispathaintthere`)
  1623  
  1624  	err := v.ReadInConfig()
  1625  	assert.IsType(t, err, ConfigFileNotFoundError{"", ""})
  1626  
  1627  	// Even though config did not load and the error might have
  1628  	// been ignored by the client, the default still loads
  1629  	assert.Equal(t, `default`, v.GetString(`key`))
  1630  }
  1631  
  1632  func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
  1633  	_, config := initDirs(t)
  1634  
  1635  	v := New()
  1636  	v.SetConfigName(config)
  1637  	v.SetDefault(`key`, `default`)
  1638  
  1639  	v.AddConfigPath(`whattayoutalkingbout`)
  1640  	v.AddConfigPath(`thispathaintthere`)
  1641  
  1642  	err := v.MergeInConfig()
  1643  	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
  1644  
  1645  	// Even though config did not load and the error might have
  1646  	// been ignored by the client, the default still loads
  1647  	assert.Equal(t, `default`, v.GetString(`key`))
  1648  }
  1649  
  1650  var yamlInvalid = []byte(`hash: map
  1651  - foo
  1652  - bar
  1653  `)
  1654  
  1655  func TestUnwrapParseErrors(t *testing.T) {
  1656  	SetConfigType("yaml")
  1657  	assert.ErrorAs(t, ReadConfig(bytes.NewBuffer(yamlInvalid)), &ConfigParseError{})
  1658  }
  1659  
  1660  func TestSub(t *testing.T) {
  1661  	v := New()
  1662  	v.SetConfigType("yaml")
  1663  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1664  
  1665  	subv := v.Sub("clothing")
  1666  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
  1667  
  1668  	subv = v.Sub("clothing.pants")
  1669  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size"))
  1670  
  1671  	subv = v.Sub("clothing.pants.size")
  1672  	assert.Equal(t, (*Viper)(nil), subv)
  1673  
  1674  	subv = v.Sub("missing.key")
  1675  	assert.Equal(t, (*Viper)(nil), subv)
  1676  
  1677  	subv = v.Sub("clothing")
  1678  	assert.Equal(t, []string{"clothing"}, subv.parents)
  1679  
  1680  	subv = v.Sub("clothing").Sub("pants")
  1681  	assert.Equal(t, []string{"clothing", "pants"}, subv.parents)
  1682  }
  1683  
  1684  var hclWriteExpected = []byte(`"foos" = {
  1685    "foo" = {
  1686      "key" = 1
  1687    }
  1688  
  1689    "foo" = {
  1690      "key" = 2
  1691    }
  1692  
  1693    "foo" = {
  1694      "key" = 3
  1695    }
  1696  
  1697    "foo" = {
  1698      "key" = 4
  1699    }
  1700  }
  1701  
  1702  "id" = "0001"
  1703  
  1704  "name" = "Cake"
  1705  
  1706  "ppu" = 0.55
  1707  
  1708  "type" = "donut"`)
  1709  
  1710  var jsonWriteExpected = []byte(`{
  1711    "batters": {
  1712      "batter": [
  1713        {
  1714          "type": "Regular"
  1715        },
  1716        {
  1717          "type": "Chocolate"
  1718        },
  1719        {
  1720          "type": "Blueberry"
  1721        },
  1722        {
  1723          "type": "Devil's Food"
  1724        }
  1725      ]
  1726    },
  1727    "id": "0001",
  1728    "name": "Cake",
  1729    "ppu": 0.55,
  1730    "type": "donut"
  1731  }`)
  1732  
  1733  var propertiesWriteExpected = []byte(`p_id = 0001
  1734  p_type = donut
  1735  p_name = Cake
  1736  p_ppu = 0.55
  1737  p_batters.batter.type = Regular
  1738  `)
  1739  
  1740  // var yamlWriteExpected = []byte(`age: 35
  1741  // beard: true
  1742  // clothing:
  1743  //     jacket: leather
  1744  //     pants:
  1745  //         size: large
  1746  //     trousers: denim
  1747  // eyes: brown
  1748  // hacker: true
  1749  // hobbies:
  1750  //     - skateboarding
  1751  //     - snowboarding
  1752  //     - go
  1753  // name: steve
  1754  // `)
  1755  
  1756  func TestWriteConfig(t *testing.T) {
  1757  	fs := afero.NewMemMapFs()
  1758  	testCases := map[string]struct {
  1759  		configName      string
  1760  		inConfigType    string
  1761  		outConfigType   string
  1762  		fileName        string
  1763  		input           []byte
  1764  		expectedContent []byte
  1765  	}{
  1766  		"hcl with file extension": {
  1767  			configName:      "c",
  1768  			inConfigType:    "hcl",
  1769  			outConfigType:   "hcl",
  1770  			fileName:        "c.hcl",
  1771  			input:           hclExample,
  1772  			expectedContent: hclWriteExpected,
  1773  		},
  1774  		"hcl without file extension": {
  1775  			configName:      "c",
  1776  			inConfigType:    "hcl",
  1777  			outConfigType:   "hcl",
  1778  			fileName:        "c",
  1779  			input:           hclExample,
  1780  			expectedContent: hclWriteExpected,
  1781  		},
  1782  		"hcl with file extension and mismatch type": {
  1783  			configName:      "c",
  1784  			inConfigType:    "hcl",
  1785  			outConfigType:   "json",
  1786  			fileName:        "c.hcl",
  1787  			input:           hclExample,
  1788  			expectedContent: hclWriteExpected,
  1789  		},
  1790  		"json with file extension": {
  1791  			configName:      "c",
  1792  			inConfigType:    "json",
  1793  			outConfigType:   "json",
  1794  			fileName:        "c.json",
  1795  			input:           jsonExample,
  1796  			expectedContent: jsonWriteExpected,
  1797  		},
  1798  		"json without file extension": {
  1799  			configName:      "c",
  1800  			inConfigType:    "json",
  1801  			outConfigType:   "json",
  1802  			fileName:        "c",
  1803  			input:           jsonExample,
  1804  			expectedContent: jsonWriteExpected,
  1805  		},
  1806  		"json with file extension and mismatch type": {
  1807  			configName:      "c",
  1808  			inConfigType:    "json",
  1809  			outConfigType:   "hcl",
  1810  			fileName:        "c.json",
  1811  			input:           jsonExample,
  1812  			expectedContent: jsonWriteExpected,
  1813  		},
  1814  		"properties with file extension": {
  1815  			configName:      "c",
  1816  			inConfigType:    "properties",
  1817  			outConfigType:   "properties",
  1818  			fileName:        "c.properties",
  1819  			input:           propertiesExample,
  1820  			expectedContent: propertiesWriteExpected,
  1821  		},
  1822  		"properties without file extension": {
  1823  			configName:      "c",
  1824  			inConfigType:    "properties",
  1825  			outConfigType:   "properties",
  1826  			fileName:        "c",
  1827  			input:           propertiesExample,
  1828  			expectedContent: propertiesWriteExpected,
  1829  		},
  1830  		"yaml with file extension": {
  1831  			configName:      "c",
  1832  			inConfigType:    "yaml",
  1833  			outConfigType:   "yaml",
  1834  			fileName:        "c.yaml",
  1835  			input:           yamlExample,
  1836  			expectedContent: yamlWriteExpected,
  1837  		},
  1838  		"yaml without file extension": {
  1839  			configName:      "c",
  1840  			inConfigType:    "yaml",
  1841  			outConfigType:   "yaml",
  1842  			fileName:        "c",
  1843  			input:           yamlExample,
  1844  			expectedContent: yamlWriteExpected,
  1845  		},
  1846  		"yaml with file extension and mismatch type": {
  1847  			configName:      "c",
  1848  			inConfigType:    "yaml",
  1849  			outConfigType:   "json",
  1850  			fileName:        "c.yaml",
  1851  			input:           yamlExample,
  1852  			expectedContent: yamlWriteExpected,
  1853  		},
  1854  	}
  1855  	for name, tc := range testCases {
  1856  		t.Run(name, func(t *testing.T) {
  1857  			v := New()
  1858  			v.SetFs(fs)
  1859  			v.SetConfigName(tc.fileName)
  1860  			v.SetConfigType(tc.inConfigType)
  1861  
  1862  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1863  			require.NoError(t, err)
  1864  			v.SetConfigType(tc.outConfigType)
  1865  			err = v.WriteConfigAs(tc.fileName)
  1866  			require.NoError(t, err)
  1867  			read, err := afero.ReadFile(fs, tc.fileName)
  1868  			require.NoError(t, err)
  1869  			assert.Equal(t, tc.expectedContent, read)
  1870  		})
  1871  	}
  1872  }
  1873  
  1874  func TestWriteConfigTOML(t *testing.T) {
  1875  	fs := afero.NewMemMapFs()
  1876  
  1877  	testCases := map[string]struct {
  1878  		configName string
  1879  		configType string
  1880  		fileName   string
  1881  		input      []byte
  1882  	}{
  1883  		"with file extension": {
  1884  			configName: "c",
  1885  			configType: "toml",
  1886  			fileName:   "c.toml",
  1887  			input:      tomlExample,
  1888  		},
  1889  		"without file extension": {
  1890  			configName: "c",
  1891  			configType: "toml",
  1892  			fileName:   "c",
  1893  			input:      tomlExample,
  1894  		},
  1895  	}
  1896  	for name, tc := range testCases {
  1897  		t.Run(name, func(t *testing.T) {
  1898  			v := New()
  1899  			v.SetFs(fs)
  1900  			v.SetConfigName(tc.configName)
  1901  			v.SetConfigType(tc.configType)
  1902  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1903  			require.NoError(t, err)
  1904  			err = v.WriteConfigAs(tc.fileName)
  1905  			require.NoError(t, err)
  1906  
  1907  			// The TOML String method does not order the contents.
  1908  			// Therefore, we must read the generated file and compare the data.
  1909  			v2 := New()
  1910  			v2.SetFs(fs)
  1911  			v2.SetConfigName(tc.configName)
  1912  			v2.SetConfigType(tc.configType)
  1913  			v2.SetConfigFile(tc.fileName)
  1914  			err = v2.ReadInConfig()
  1915  			require.NoError(t, err)
  1916  
  1917  			assert.Equal(t, v.GetString("title"), v2.GetString("title"))
  1918  			assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio"))
  1919  			assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob"))
  1920  			assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization"))
  1921  		})
  1922  	}
  1923  }
  1924  
  1925  func TestWriteConfigDotEnv(t *testing.T) {
  1926  	fs := afero.NewMemMapFs()
  1927  	testCases := map[string]struct {
  1928  		configName string
  1929  		configType string
  1930  		fileName   string
  1931  		input      []byte
  1932  	}{
  1933  		"with file extension": {
  1934  			configName: "c",
  1935  			configType: "env",
  1936  			fileName:   "c.env",
  1937  			input:      dotenvExample,
  1938  		},
  1939  		"without file extension": {
  1940  			configName: "c",
  1941  			configType: "env",
  1942  			fileName:   "c",
  1943  			input:      dotenvExample,
  1944  		},
  1945  	}
  1946  	for name, tc := range testCases {
  1947  		t.Run(name, func(t *testing.T) {
  1948  			v := New()
  1949  			v.SetFs(fs)
  1950  			v.SetConfigName(tc.configName)
  1951  			v.SetConfigType(tc.configType)
  1952  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1953  			require.NoError(t, err)
  1954  			err = v.WriteConfigAs(tc.fileName)
  1955  			require.NoError(t, err)
  1956  
  1957  			// The TOML String method does not order the contents.
  1958  			// Therefore, we must read the generated file and compare the data.
  1959  			v2 := New()
  1960  			v2.SetFs(fs)
  1961  			v2.SetConfigName(tc.configName)
  1962  			v2.SetConfigType(tc.configType)
  1963  			v2.SetConfigFile(tc.fileName)
  1964  			err = v2.ReadInConfig()
  1965  			require.NoError(t, err)
  1966  
  1967  			assert.Equal(t, v.GetString("title_dotenv"), v2.GetString("title_dotenv"))
  1968  			assert.Equal(t, v.GetString("type_dotenv"), v2.GetString("type_dotenv"))
  1969  			assert.Equal(t, v.GetString("kind_dotenv"), v2.GetString("kind_dotenv"))
  1970  		})
  1971  	}
  1972  }
  1973  
  1974  func TestSafeWriteConfig(t *testing.T) {
  1975  	v := New()
  1976  	fs := afero.NewMemMapFs()
  1977  	v.SetFs(fs)
  1978  	v.AddConfigPath("/test")
  1979  	v.SetConfigName("c")
  1980  	v.SetConfigType("yaml")
  1981  	require.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)))
  1982  	require.NoError(t, v.SafeWriteConfig())
  1983  	read, err := afero.ReadFile(fs, testutil.AbsFilePath(t, "/test/c.yaml"))
  1984  	require.NoError(t, err)
  1985  	assert.Equal(t, yamlWriteExpected, read)
  1986  }
  1987  
  1988  func TestSafeWriteConfigWithMissingConfigPath(t *testing.T) {
  1989  	v := New()
  1990  	fs := afero.NewMemMapFs()
  1991  	v.SetFs(fs)
  1992  	v.SetConfigName("c")
  1993  	v.SetConfigType("yaml")
  1994  	require.EqualError(t, v.SafeWriteConfig(), "missing configuration for 'configPath'")
  1995  }
  1996  
  1997  func TestSafeWriteConfigWithExistingFile(t *testing.T) {
  1998  	v := New()
  1999  	fs := afero.NewMemMapFs()
  2000  	fs.Create(testutil.AbsFilePath(t, "/test/c.yaml"))
  2001  	v.SetFs(fs)
  2002  	v.AddConfigPath("/test")
  2003  	v.SetConfigName("c")
  2004  	v.SetConfigType("yaml")
  2005  	err := v.SafeWriteConfig()
  2006  	require.Error(t, err)
  2007  	_, ok := err.(ConfigFileAlreadyExistsError)
  2008  	assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
  2009  }
  2010  
  2011  func TestSafeWriteAsConfig(t *testing.T) {
  2012  	v := New()
  2013  	fs := afero.NewMemMapFs()
  2014  	v.SetFs(fs)
  2015  	err := v.ReadConfig(bytes.NewBuffer(yamlExample))
  2016  	require.NoError(t, err)
  2017  	require.NoError(t, v.SafeWriteConfigAs("/test/c.yaml"))
  2018  	_, err = afero.ReadFile(fs, "/test/c.yaml")
  2019  	require.NoError(t, err)
  2020  }
  2021  
  2022  func TestSafeWriteConfigAsWithExistingFile(t *testing.T) {
  2023  	v := New()
  2024  	fs := afero.NewMemMapFs()
  2025  	fs.Create("/test/c.yaml")
  2026  	v.SetFs(fs)
  2027  	err := v.SafeWriteConfigAs("/test/c.yaml")
  2028  	require.Error(t, err)
  2029  	_, ok := err.(ConfigFileAlreadyExistsError)
  2030  	assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
  2031  }
  2032  
  2033  func TestWriteHiddenFile(t *testing.T) {
  2034  	v := New()
  2035  	fs := afero.NewMemMapFs()
  2036  	fs.Create(testutil.AbsFilePath(t, "/test/.config"))
  2037  	v.SetFs(fs)
  2038  
  2039  	v.SetConfigName(".config")
  2040  	v.SetConfigType("yaml")
  2041  	v.AddConfigPath("/test")
  2042  
  2043  	err := v.ReadInConfig()
  2044  	require.NoError(t, err)
  2045  
  2046  	err = v.WriteConfig()
  2047  	require.NoError(t, err)
  2048  }
  2049  
  2050  var yamlMergeExampleTgt = []byte(`
  2051  hello:
  2052      pop: 37890
  2053      largenum: 765432101234567
  2054      num2pow63: 9223372036854775808
  2055      universe: null
  2056      world:
  2057      - us
  2058      - uk
  2059      - fr
  2060      - de
  2061  `)
  2062  
  2063  var yamlMergeExampleSrc = []byte(`
  2064  hello:
  2065      pop: 45000
  2066      largenum: 7654321001234567
  2067      universe:
  2068      - mw
  2069      - ad
  2070      ints:
  2071      - 1
  2072      - 2
  2073  fu: bar
  2074  `)
  2075  
  2076  var jsonMergeExampleTgt = []byte(`
  2077  {
  2078  	"hello": {
  2079  		"foo": null,
  2080  		"pop": 123456
  2081  	}
  2082  }
  2083  `)
  2084  
  2085  var jsonMergeExampleSrc = []byte(`
  2086  {
  2087  	"hello": {
  2088  		"foo": "foo str",
  2089  		"pop": "pop str"
  2090  	}
  2091  }
  2092  `)
  2093  
  2094  func TestMergeConfig(t *testing.T) {
  2095  	v := New()
  2096  	v.SetConfigType("yml")
  2097  	err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt))
  2098  	require.NoError(t, err)
  2099  
  2100  	assert.Equal(t, 37890, v.GetInt("hello.pop"))
  2101  	assert.Equal(t, int32(37890), v.GetInt32("hello.pop"))
  2102  	assert.Equal(t, int64(765432101234567), v.GetInt64("hello.largenum"))
  2103  	assert.Equal(t, uint(37890), v.GetUint("hello.pop"))
  2104  	assert.Equal(t, uint16(37890), v.GetUint16("hello.pop"))
  2105  	assert.Equal(t, uint32(37890), v.GetUint32("hello.pop"))
  2106  	assert.Equal(t, uint64(9223372036854775808), v.GetUint64("hello.num2pow63"))
  2107  	assert.Len(t, v.GetStringSlice("hello.world"), 4)
  2108  	assert.Empty(t, v.GetString("fu"))
  2109  
  2110  	err = v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc))
  2111  	require.NoError(t, err)
  2112  
  2113  	assert.Equal(t, 45000, v.GetInt("hello.pop"))
  2114  	assert.Equal(t, int32(45000), v.GetInt32("hello.pop"))
  2115  	assert.Equal(t, int64(7654321001234567), v.GetInt64("hello.largenum"))
  2116  	assert.Len(t, v.GetStringSlice("hello.world"), 4)
  2117  	assert.Len(t, v.GetStringSlice("hello.universe"), 2)
  2118  	assert.Len(t, v.GetIntSlice("hello.ints"), 2)
  2119  	assert.Equal(t, "bar", v.GetString("fu"))
  2120  }
  2121  
  2122  func TestMergeConfigOverrideType(t *testing.T) {
  2123  	v := New()
  2124  	v.SetConfigType("json")
  2125  	err := v.ReadConfig(bytes.NewBuffer(jsonMergeExampleTgt))
  2126  	require.NoError(t, err)
  2127  
  2128  	err = v.MergeConfig(bytes.NewBuffer(jsonMergeExampleSrc))
  2129  	require.NoError(t, err)
  2130  
  2131  	assert.Equal(t, "pop str", v.GetString("hello.pop"))
  2132  	assert.Equal(t, "foo str", v.GetString("hello.foo"))
  2133  }
  2134  
  2135  func TestMergeConfigNoMerge(t *testing.T) {
  2136  	v := New()
  2137  	v.SetConfigType("yml")
  2138  	err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt))
  2139  	require.NoError(t, err)
  2140  
  2141  	assert.Equal(t, 37890, v.GetInt("hello.pop"))
  2142  	assert.Len(t, v.GetStringSlice("hello.world"), 4)
  2143  	assert.Empty(t, v.GetString("fu"))
  2144  
  2145  	err = v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc))
  2146  	require.NoError(t, err)
  2147  
  2148  	assert.Equal(t, 45000, v.GetInt("hello.pop"))
  2149  	assert.Empty(t, v.GetStringSlice("hello.world"))
  2150  	assert.Len(t, v.GetStringSlice("hello.universe"), 2)
  2151  	assert.Len(t, v.GetIntSlice("hello.ints"), 2)
  2152  	assert.Equal(t, "bar", v.GetString("fu"))
  2153  }
  2154  
  2155  func TestMergeConfigMap(t *testing.T) {
  2156  	v := New()
  2157  	v.SetConfigType("yml")
  2158  	err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt))
  2159  	require.NoError(t, err)
  2160  
  2161  	assertFn := func(i int) {
  2162  		large := v.GetInt64("hello.largenum")
  2163  		pop := v.GetInt("hello.pop")
  2164  		assert.Equal(t, int64(765432101234567), large)
  2165  		assert.Equal(t, i, pop)
  2166  	}
  2167  
  2168  	assertFn(37890)
  2169  
  2170  	update := map[string]any{
  2171  		"Hello": map[string]any{
  2172  			"Pop": 1234,
  2173  		},
  2174  		"World": map[any]any{
  2175  			"Rock": 345,
  2176  		},
  2177  	}
  2178  
  2179  	err = v.MergeConfigMap(update)
  2180  	require.NoError(t, err)
  2181  
  2182  	assert.Equal(t, 345, v.GetInt("world.rock"))
  2183  
  2184  	assertFn(1234)
  2185  }
  2186  
  2187  func TestUnmarshalingWithAliases(t *testing.T) {
  2188  	v := New()
  2189  	v.SetDefault("ID", 1)
  2190  	v.Set("name", "Steve")
  2191  	v.Set("lastname", "Owen")
  2192  
  2193  	v.RegisterAlias("UserID", "ID")
  2194  	v.RegisterAlias("Firstname", "name")
  2195  	v.RegisterAlias("Surname", "lastname")
  2196  
  2197  	type config struct {
  2198  		ID        int
  2199  		FirstName string
  2200  		Surname   string
  2201  	}
  2202  
  2203  	var C config
  2204  	err := v.Unmarshal(&C)
  2205  	require.NoError(t, err, "unable to decode into struct")
  2206  
  2207  	assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C)
  2208  }
  2209  
  2210  func TestSetConfigNameClearsFileCache(t *testing.T) {
  2211  	SetConfigFile("/tmp/config.yaml")
  2212  	SetConfigName("default")
  2213  	f, err := v.getConfigFile()
  2214  	require.Error(t, err, "config file cache should have been cleared")
  2215  	assert.Empty(t, f)
  2216  }
  2217  
  2218  func TestShadowedNestedValue(t *testing.T) {
  2219  	config := `name: steve
  2220  clothing:
  2221    jacket: leather
  2222    trousers: denim
  2223    pants:
  2224      size: large
  2225  `
  2226  	initConfig("yaml", config)
  2227  
  2228  	assert.Equal(t, "steve", GetString("name"))
  2229  
  2230  	polyester := "polyester"
  2231  	SetDefault("clothing.shirt", polyester)
  2232  	SetDefault("clothing.jacket.price", 100)
  2233  
  2234  	assert.Equal(t, "leather", GetString("clothing.jacket"))
  2235  	assert.Nil(t, Get("clothing.jacket.price"))
  2236  	assert.Equal(t, polyester, GetString("clothing.shirt"))
  2237  
  2238  	clothingSettings := AllSettings()["clothing"].(map[string]any)
  2239  	assert.Equal(t, "leather", clothingSettings["jacket"])
  2240  	assert.Equal(t, polyester, clothingSettings["shirt"])
  2241  }
  2242  
  2243  func TestDotParameter(t *testing.T) {
  2244  	initJSON()
  2245  	// should take precedence over batters defined in jsonExample
  2246  	r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
  2247  	unmarshalReader(r, v.config)
  2248  
  2249  	actual := Get("batters.batter")
  2250  	expected := []any{map[string]any{"type": "Small"}}
  2251  	assert.Equal(t, expected, actual)
  2252  }
  2253  
  2254  func TestCaseInsensitive(t *testing.T) {
  2255  	for _, config := range []struct {
  2256  		typ     string
  2257  		content string
  2258  	}{
  2259  		{"yaml", `
  2260  aBcD: 1
  2261  eF:
  2262    gH: 2
  2263    iJk: 3
  2264    Lm:
  2265      nO: 4
  2266      P:
  2267        Q: 5
  2268        R: 6
  2269  `},
  2270  		{"json", `{
  2271    "aBcD": 1,
  2272    "eF": {
  2273      "iJk": 3,
  2274      "Lm": {
  2275        "P": {
  2276          "Q": 5,
  2277          "R": 6
  2278        },
  2279        "nO": 4
  2280      },
  2281      "gH": 2
  2282    }
  2283  }`},
  2284  		{"toml", `aBcD = 1
  2285  [eF]
  2286  gH = 2
  2287  iJk = 3
  2288  [eF.Lm]
  2289  nO = 4
  2290  [eF.Lm.P]
  2291  Q = 5
  2292  R = 6
  2293  `},
  2294  	} {
  2295  		doTestCaseInsensitive(t, config.typ, config.content)
  2296  	}
  2297  }
  2298  
  2299  func TestCaseInsensitiveSet(t *testing.T) {
  2300  	Reset()
  2301  	m1 := map[string]any{
  2302  		"Foo": 32,
  2303  		"Bar": map[any]any{
  2304  			"ABc": "A",
  2305  			"cDE": "B",
  2306  		},
  2307  	}
  2308  
  2309  	m2 := map[string]any{
  2310  		"Foo": 52,
  2311  		"Bar": map[any]any{
  2312  			"bCd": "A",
  2313  			"eFG": "B",
  2314  		},
  2315  	}
  2316  
  2317  	Set("Given1", m1)
  2318  	Set("Number1", 42)
  2319  
  2320  	SetDefault("Given2", m2)
  2321  	SetDefault("Number2", 52)
  2322  
  2323  	// Verify SetDefault
  2324  	assert.Equal(t, 52, Get("number2"))
  2325  	assert.Equal(t, 52, Get("given2.foo"))
  2326  	assert.Equal(t, "A", Get("given2.bar.bcd"))
  2327  	_, ok := m2["Foo"]
  2328  	assert.True(t, ok)
  2329  
  2330  	// Verify Set
  2331  	assert.Equal(t, 42, Get("number1"))
  2332  	assert.Equal(t, 32, Get("given1.foo"))
  2333  	assert.Equal(t, "A", Get("given1.bar.abc"))
  2334  	_, ok = m1["Foo"]
  2335  	assert.True(t, ok)
  2336  }
  2337  
  2338  func TestParseNested(t *testing.T) {
  2339  	type duration struct {
  2340  		Delay time.Duration
  2341  	}
  2342  
  2343  	type item struct {
  2344  		Name   string
  2345  		Delay  time.Duration
  2346  		Nested duration
  2347  	}
  2348  
  2349  	config := `[[parent]]
  2350  	delay="100ms"
  2351  	[parent.nested]
  2352  	delay="200ms"
  2353  `
  2354  	initConfig("toml", config)
  2355  
  2356  	var items []item
  2357  	err := v.UnmarshalKey("parent", &items)
  2358  	require.NoError(t, err, "unable to decode into struct")
  2359  
  2360  	assert.Len(t, items, 1)
  2361  	assert.Equal(t, 100*time.Millisecond, items[0].Delay)
  2362  	assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
  2363  }
  2364  
  2365  func doTestCaseInsensitive(t *testing.T, typ, config string) {
  2366  	initConfig(typ, config)
  2367  	Set("RfD", true)
  2368  	assert.Equal(t, true, Get("rfd"))
  2369  	assert.Equal(t, true, Get("rFD"))
  2370  	assert.Equal(t, 1, cast.ToInt(Get("abcd")))
  2371  	assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
  2372  	assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
  2373  	assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
  2374  	assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
  2375  	assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
  2376  }
  2377  
  2378  func newViperWithConfigFile(t *testing.T) (*Viper, string) {
  2379  	watchDir := t.TempDir()
  2380  	configFile := path.Join(watchDir, "config.yaml")
  2381  	err := os.WriteFile(configFile, []byte("foo: bar\n"), 0o640)
  2382  	require.NoError(t, err)
  2383  	v := New()
  2384  	v.SetConfigFile(configFile)
  2385  	err = v.ReadInConfig()
  2386  	require.NoError(t, err)
  2387  	require.Equal(t, "bar", v.Get("foo"))
  2388  	return v, configFile
  2389  }
  2390  
  2391  func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string) {
  2392  	watchDir := t.TempDir()
  2393  	dataDir1 := path.Join(watchDir, "data1")
  2394  	err := os.Mkdir(dataDir1, 0o777)
  2395  	require.NoError(t, err)
  2396  	realConfigFile := path.Join(dataDir1, "config.yaml")
  2397  	t.Logf("Real config file location: %s\n", realConfigFile)
  2398  	err = os.WriteFile(realConfigFile, []byte("foo: bar\n"), 0o640)
  2399  	require.NoError(t, err)
  2400  	// now, symlink the tm `data1` dir to `data` in the baseDir
  2401  	os.Symlink(dataDir1, path.Join(watchDir, "data"))
  2402  	// and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`
  2403  	configFile := path.Join(watchDir, "config.yaml")
  2404  	os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile)
  2405  	t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml"))
  2406  	// init Viper
  2407  	v := New()
  2408  	v.SetConfigFile(configFile)
  2409  	err = v.ReadInConfig()
  2410  	require.NoError(t, err)
  2411  	require.Equal(t, "bar", v.Get("foo"))
  2412  	return v, watchDir, configFile
  2413  }
  2414  
  2415  func TestWatchFile(t *testing.T) {
  2416  	if runtime.GOOS == "linux" {
  2417  		// TODO(bep) FIX ME
  2418  		t.Skip("Skip test on Linux ...")
  2419  	}
  2420  
  2421  	t.Run("file content changed", func(t *testing.T) {
  2422  		// given a `config.yaml` file being watched
  2423  		v, configFile := newViperWithConfigFile(t)
  2424  		_, err := os.Stat(configFile)
  2425  		require.NoError(t, err)
  2426  		t.Logf("test config file: %s\n", configFile)
  2427  		wg := sync.WaitGroup{}
  2428  		wg.Add(1)
  2429  		var wgDoneOnce sync.Once // OnConfigChange is called twice on Windows
  2430  		v.OnConfigChange(func(in fsnotify.Event) {
  2431  			t.Logf("config file changed")
  2432  			wgDoneOnce.Do(func() {
  2433  				wg.Done()
  2434  			})
  2435  		})
  2436  		v.WatchConfig()
  2437  		// when overwriting the file and waiting for the custom change notification handler to be triggered
  2438  		err = os.WriteFile(configFile, []byte("foo: baz\n"), 0o640)
  2439  		wg.Wait()
  2440  		// then the config value should have changed
  2441  		require.NoError(t, err)
  2442  		assert.Equal(t, "baz", v.Get("foo"))
  2443  	})
  2444  
  2445  	t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) {
  2446  		// skip if not executed on Linux
  2447  		if runtime.GOOS != "linux" {
  2448  			t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
  2449  		}
  2450  		v, watchDir, _ := newViperWithSymlinkedConfigFile(t)
  2451  		wg := sync.WaitGroup{}
  2452  		v.WatchConfig()
  2453  		v.OnConfigChange(func(in fsnotify.Event) {
  2454  			t.Logf("config file changed")
  2455  			wg.Done()
  2456  		})
  2457  		wg.Add(1)
  2458  		// when link to another `config.yaml` file
  2459  		dataDir2 := path.Join(watchDir, "data2")
  2460  		err := os.Mkdir(dataDir2, 0o777)
  2461  		require.NoError(t, err)
  2462  		configFile2 := path.Join(dataDir2, "config.yaml")
  2463  		err = os.WriteFile(configFile2, []byte("foo: baz\n"), 0o640)
  2464  		require.NoError(t, err)
  2465  		// change the symlink using the `ln -sfn` command
  2466  		err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
  2467  		require.NoError(t, err)
  2468  		wg.Wait()
  2469  		// then
  2470  		require.NoError(t, err)
  2471  		assert.Equal(t, "baz", v.Get("foo"))
  2472  	})
  2473  }
  2474  
  2475  func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
  2476  	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  2477  	flags.String("foo.bar", "cobra_flag", "")
  2478  
  2479  	v := New()
  2480  	assert.NoError(t, v.BindPFlags(flags))
  2481  
  2482  	config := &struct {
  2483  		Foo struct {
  2484  			Bar string
  2485  		}
  2486  	}{}
  2487  
  2488  	assert.NoError(t, v.Unmarshal(config))
  2489  	assert.Equal(t, "cobra_flag", config.Foo.Bar)
  2490  }
  2491  
  2492  // var yamlExampleWithDot = []byte(`Hacker: true
  2493  // name: steve
  2494  // hobbies:
  2495  //     - skateboarding
  2496  //     - snowboarding
  2497  //     - go
  2498  // clothing:
  2499  //     jacket: leather
  2500  //     trousers: denim
  2501  //     pants:
  2502  //         size: large
  2503  // age: 35
  2504  // eyes : brown
  2505  // beard: true
  2506  // emails:
  2507  //     steve@hacker.com:
  2508  //         created: 01/02/03
  2509  //         active: true
  2510  // `)
  2511  
  2512  func TestKeyDelimiter(t *testing.T) {
  2513  	v := NewWithOptions(KeyDelimiter("::"))
  2514  	v.SetConfigType("yaml")
  2515  	r := strings.NewReader(string(yamlExampleWithDot))
  2516  
  2517  	err := v.unmarshalReader(r, v.config)
  2518  	require.NoError(t, err)
  2519  
  2520  	values := map[string]any{
  2521  		"image": map[string]any{
  2522  			"repository": "someImage",
  2523  			"tag":        "1.0.0",
  2524  		},
  2525  		"ingress": map[string]any{
  2526  			"annotations": map[string]any{
  2527  				"traefik.frontend.rule.type":                 "PathPrefix",
  2528  				"traefik.ingress.kubernetes.io/ssl-redirect": "true",
  2529  			},
  2530  		},
  2531  	}
  2532  
  2533  	v.SetDefault("charts::values", values)
  2534  
  2535  	assert.Equal(t, "leather", v.GetString("clothing::jacket"))
  2536  	assert.Equal(t, "01/02/03", v.GetString("emails::steve@hacker.com::created"))
  2537  
  2538  	type config struct {
  2539  		Charts struct {
  2540  			Values map[string]any
  2541  		}
  2542  	}
  2543  
  2544  	expected := config{
  2545  		Charts: struct {
  2546  			Values map[string]any
  2547  		}{
  2548  			Values: values,
  2549  		},
  2550  	}
  2551  
  2552  	var actual config
  2553  
  2554  	assert.NoError(t, v.Unmarshal(&actual))
  2555  
  2556  	assert.Equal(t, expected, actual)
  2557  }
  2558  
  2559  var yamlDeepNestedSlices = []byte(`TV:
  2560  - title: "The Expanse"
  2561    title_i18n:
  2562      USA: "The Expanse"
  2563      Japan: "エクスパンス -巨獣めざめる-"
  2564    seasons:
  2565    - first_released: "December 14, 2015"
  2566      episodes:
  2567      - title: "Dulcinea"
  2568        air_date: "December 14, 2015"
  2569      - title: "The Big Empty"
  2570        air_date: "December 15, 2015"
  2571      - title: "Remember the Cant"
  2572        air_date: "December 22, 2015"
  2573    - first_released: "February 1, 2017"
  2574      episodes:
  2575      - title: "Safe"
  2576        air_date: "February 1, 2017"
  2577      - title: "Doors & Corners"
  2578        air_date: "February 1, 2017"
  2579      - title: "Static"
  2580        air_date: "February 8, 2017"
  2581    episodes:
  2582      - ["Dulcinea", "The Big Empty", "Remember the Cant"]
  2583      - ["Safe", "Doors & Corners", "Static"]
  2584  `)
  2585  
  2586  func TestSliceIndexAccess(t *testing.T) {
  2587  	v.SetConfigType("yaml")
  2588  	r := strings.NewReader(string(yamlDeepNestedSlices))
  2589  
  2590  	err := v.unmarshalReader(r, v.config)
  2591  	require.NoError(t, err)
  2592  
  2593  	assert.Equal(t, "The Expanse", v.GetString("tv.0.title"))
  2594  	assert.Equal(t, "February 1, 2017", v.GetString("tv.0.seasons.1.first_released"))
  2595  	assert.Equal(t, "Static", v.GetString("tv.0.seasons.1.episodes.2.title"))
  2596  	assert.Equal(t, "December 15, 2015", v.GetString("tv.0.seasons.0.episodes.1.air_date"))
  2597  
  2598  	// Test nested keys with capital letters
  2599  	assert.Equal(t, "The Expanse", v.GetString("tv.0.title_i18n.USA"))
  2600  	assert.Equal(t, "エクスパンス -巨獣めざめる-", v.GetString("tv.0.title_i18n.Japan"))
  2601  
  2602  	// Test for index out of bounds
  2603  	assert.Equal(t, "", v.GetString("tv.0.seasons.2.first_released"))
  2604  
  2605  	// Accessing multidimensional arrays
  2606  	assert.Equal(t, "Static", v.GetString("tv.0.episodes.1.2"))
  2607  }
  2608  
  2609  func TestIsPathShadowedInFlatMap(t *testing.T) {
  2610  	v := New()
  2611  
  2612  	stringMap := map[string]string{
  2613  		"foo": "value",
  2614  	}
  2615  
  2616  	flagMap := map[string]FlagValue{
  2617  		"foo": pflagValue{},
  2618  	}
  2619  
  2620  	path1 := []string{"foo", "bar"}
  2621  	expected1 := "foo"
  2622  
  2623  	// "foo.bar" should shadowed by "foo"
  2624  	assert.Equal(t, expected1, v.isPathShadowedInFlatMap(path1, stringMap))
  2625  	assert.Equal(t, expected1, v.isPathShadowedInFlatMap(path1, flagMap))
  2626  
  2627  	path2 := []string{"bar", "foo"}
  2628  	expected2 := ""
  2629  
  2630  	// "bar.foo" should not shadowed by "foo"
  2631  	assert.Equal(t, expected2, v.isPathShadowedInFlatMap(path2, stringMap))
  2632  	assert.Equal(t, expected2, v.isPathShadowedInFlatMap(path2, flagMap))
  2633  }
  2634  
  2635  func TestFlagShadow(t *testing.T) {
  2636  	v := New()
  2637  
  2638  	v.SetDefault("foo.bar1.bar2", "default")
  2639  
  2640  	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  2641  	flags.String("foo.bar1", "shadowed", "")
  2642  	flags.VisitAll(func(flag *pflag.Flag) {
  2643  		flag.Changed = true
  2644  	})
  2645  
  2646  	v.BindPFlags(flags)
  2647  
  2648  	assert.Equal(t, "shadowed", v.GetString("foo.bar1"))
  2649  	// the default "foo.bar1.bar2" value should shadowed by flag "foo.bar1" value
  2650  	// and should return an empty string
  2651  	assert.Equal(t, "", v.GetString("foo.bar1.bar2"))
  2652  }
  2653  
  2654  func BenchmarkGetBool(b *testing.B) {
  2655  	key := "BenchmarkGetBool"
  2656  	v = New()
  2657  	v.Set(key, true)
  2658  
  2659  	for i := 0; i < b.N; i++ {
  2660  		if !v.GetBool(key) {
  2661  			b.Fatal("GetBool returned false")
  2662  		}
  2663  	}
  2664  }
  2665  
  2666  func BenchmarkGet(b *testing.B) {
  2667  	key := "BenchmarkGet"
  2668  	v = New()
  2669  	v.Set(key, true)
  2670  
  2671  	for i := 0; i < b.N; i++ {
  2672  		if !v.Get(key).(bool) {
  2673  			b.Fatal("Get returned false")
  2674  		}
  2675  	}
  2676  }
  2677  
  2678  // BenchmarkGetBoolFromMap is the "perfect result" for the above.
  2679  func BenchmarkGetBoolFromMap(b *testing.B) {
  2680  	m := make(map[string]bool)
  2681  	key := "BenchmarkGetBool"
  2682  	m[key] = true
  2683  
  2684  	for i := 0; i < b.N; i++ {
  2685  		if !m[key] {
  2686  			b.Fatal("Map value was false")
  2687  		}
  2688  	}
  2689  }
  2690  
  2691  // Skip some tests on Windows that kept failing when Windows was added to the CI as a target.
  2692  //
  2693  //nolint:gocritic // sloppyTestFuncName
  2694  func skipWindows(t *testing.T) {
  2695  	if runtime.GOOS == "windows" {
  2696  		t.Skip("Skip test on Windows")
  2697  	}
  2698  }
  2699  

View as plain text