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