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