github.com/Nothing-no/viper@v0.0.0-20230922064921-68b4373f7378/viper_test.go (about)

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