github.com/lixiangzhong/viper@v1.2.2-0.20181106052354-0c5e9e53bcb8/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  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"os"
    15  	"os/exec"
    16  	"path"
    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  
    30  	"github.com/spf13/pflag"
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  var yamlExample = []byte(`Hacker: true
    36  name: steve
    37  hobbies:
    38  - skateboarding
    39  - snowboarding
    40  - go
    41  clothing:
    42    jacket: leather
    43    trousers: denim
    44    pants:
    45      size: large
    46  age: 35
    47  eyes : brown
    48  beard: true
    49  `)
    50  
    51  var yamlExampleWithExtras = []byte(`Existing: true
    52  Bogus: true
    53  `)
    54  
    55  type testUnmarshalExtra struct {
    56  	Existing bool
    57  }
    58  
    59  var tomlExample = []byte(`
    60  title = "TOML Example"
    61  
    62  [owner]
    63  organization = "MongoDB"
    64  Bio = "MongoDB Chief Developer Advocate & Hacker at Large"
    65  dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)
    66  
    67  var jsonExample = []byte(`{
    68  "id": "0001",
    69  "type": "donut",
    70  "name": "Cake",
    71  "ppu": 0.55,
    72  "batters": {
    73          "batter": [
    74                  { "type": "Regular" },
    75                  { "type": "Chocolate" },
    76                  { "type": "Blueberry" },
    77                  { "type": "Devil's Food" }
    78              ]
    79      }
    80  }`)
    81  
    82  var hclExample = []byte(`
    83  id = "0001"
    84  type = "donut"
    85  name = "Cake"
    86  ppu = 0.55
    87  foos {
    88  	foo {
    89  		key = 1
    90  	}
    91  	foo {
    92  		key = 2
    93  	}
    94  	foo {
    95  		key = 3
    96  	}
    97  	foo {
    98  		key = 4
    99  	}
   100  }`)
   101  
   102  var propertiesExample = []byte(`
   103  p_id: 0001
   104  p_type: donut
   105  p_name: Cake
   106  p_ppu: 0.55
   107  p_batters.batter.type: Regular
   108  `)
   109  
   110  var remoteExample = []byte(`{
   111  "id":"0002",
   112  "type":"cronut",
   113  "newkey":"remote"
   114  }`)
   115  
   116  func initConfigs() {
   117  	Reset()
   118  	var r io.Reader
   119  	SetConfigType("yaml")
   120  	r = bytes.NewReader(yamlExample)
   121  	unmarshalReader(r, v.config)
   122  
   123  	SetConfigType("json")
   124  	r = bytes.NewReader(jsonExample)
   125  	unmarshalReader(r, v.config)
   126  
   127  	SetConfigType("hcl")
   128  	r = bytes.NewReader(hclExample)
   129  	unmarshalReader(r, v.config)
   130  
   131  	SetConfigType("properties")
   132  	r = bytes.NewReader(propertiesExample)
   133  	unmarshalReader(r, v.config)
   134  
   135  	SetConfigType("toml")
   136  	r = bytes.NewReader(tomlExample)
   137  	unmarshalReader(r, v.config)
   138  
   139  	SetConfigType("json")
   140  	remote := bytes.NewReader(remoteExample)
   141  	unmarshalReader(remote, v.kvstore)
   142  }
   143  
   144  func initConfig(typ, config string) {
   145  	Reset()
   146  	SetConfigType(typ)
   147  	r := strings.NewReader(config)
   148  
   149  	if err := unmarshalReader(r, v.config); err != nil {
   150  		panic(err)
   151  	}
   152  }
   153  
   154  func initYAML() {
   155  	initConfig("yaml", string(yamlExample))
   156  }
   157  
   158  func initJSON() {
   159  	Reset()
   160  	SetConfigType("json")
   161  	r := bytes.NewReader(jsonExample)
   162  
   163  	unmarshalReader(r, v.config)
   164  }
   165  
   166  func initProperties() {
   167  	Reset()
   168  	SetConfigType("properties")
   169  	r := bytes.NewReader(propertiesExample)
   170  
   171  	unmarshalReader(r, v.config)
   172  }
   173  
   174  func initTOML() {
   175  	Reset()
   176  	SetConfigType("toml")
   177  	r := bytes.NewReader(tomlExample)
   178  
   179  	unmarshalReader(r, v.config)
   180  }
   181  
   182  func initHcl() {
   183  	Reset()
   184  	SetConfigType("hcl")
   185  	r := bytes.NewReader(hclExample)
   186  
   187  	unmarshalReader(r, v.config)
   188  }
   189  
   190  // make directories for testing
   191  func initDirs(t *testing.T) (string, string, func()) {
   192  
   193  	var (
   194  		testDirs = []string{`a a`, `b`, `c\c`, `D_`}
   195  		config   = `improbable`
   196  	)
   197  
   198  	root, err := ioutil.TempDir("", "")
   199  
   200  	cleanup := true
   201  	defer func() {
   202  		if cleanup {
   203  			os.Chdir("..")
   204  			os.RemoveAll(root)
   205  		}
   206  	}()
   207  
   208  	assert.Nil(t, err)
   209  
   210  	err = os.Chdir(root)
   211  	assert.Nil(t, err)
   212  
   213  	for _, dir := range testDirs {
   214  		err = os.Mkdir(dir, 0750)
   215  		assert.Nil(t, err)
   216  
   217  		err = ioutil.WriteFile(
   218  			path.Join(dir, config+".toml"),
   219  			[]byte("key = \"value is "+dir+"\"\n"),
   220  			0640)
   221  		assert.Nil(t, err)
   222  	}
   223  
   224  	cleanup = false
   225  	return root, config, func() {
   226  		os.Chdir("..")
   227  		os.RemoveAll(root)
   228  	}
   229  }
   230  
   231  //stubs for PFlag Values
   232  type stringValue string
   233  
   234  func newStringValue(val string, p *string) *stringValue {
   235  	*p = val
   236  	return (*stringValue)(p)
   237  }
   238  
   239  func (s *stringValue) Set(val string) error {
   240  	*s = stringValue(val)
   241  	return nil
   242  }
   243  
   244  func (s *stringValue) Type() string {
   245  	return "string"
   246  }
   247  
   248  func (s *stringValue) String() string {
   249  	return fmt.Sprintf("%s", *s)
   250  }
   251  
   252  func TestBasics(t *testing.T) {
   253  	SetConfigFile("/tmp/config.yaml")
   254  	filename, err := v.getConfigFile()
   255  	assert.Equal(t, "/tmp/config.yaml", filename)
   256  	assert.NoError(t, err)
   257  }
   258  
   259  func TestDefault(t *testing.T) {
   260  	SetDefault("age", 45)
   261  	assert.Equal(t, 45, Get("age"))
   262  
   263  	SetDefault("clothing.jacket", "slacks")
   264  	assert.Equal(t, "slacks", Get("clothing.jacket"))
   265  
   266  	SetConfigType("yaml")
   267  	err := ReadConfig(bytes.NewBuffer(yamlExample))
   268  
   269  	assert.NoError(t, err)
   270  	assert.Equal(t, "leather", Get("clothing.jacket"))
   271  }
   272  
   273  func TestUnmarshaling(t *testing.T) {
   274  	SetConfigType("yaml")
   275  	r := bytes.NewReader(yamlExample)
   276  
   277  	unmarshalReader(r, v.config)
   278  	assert.True(t, InConfig("name"))
   279  	assert.False(t, InConfig("state"))
   280  	assert.Equal(t, "steve", Get("name"))
   281  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
   282  	assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
   283  	assert.Equal(t, 35, Get("age"))
   284  }
   285  
   286  func TestUnmarshalExact(t *testing.T) {
   287  	vip := New()
   288  	target := &testUnmarshalExtra{}
   289  	vip.SetConfigType("yaml")
   290  	r := bytes.NewReader(yamlExampleWithExtras)
   291  	vip.ReadConfig(r)
   292  	err := vip.UnmarshalExact(target)
   293  	if err == nil {
   294  		t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields")
   295  	}
   296  }
   297  
   298  func TestOverrides(t *testing.T) {
   299  	Set("age", 40)
   300  	assert.Equal(t, 40, Get("age"))
   301  }
   302  
   303  func TestDefaultPost(t *testing.T) {
   304  	assert.NotEqual(t, "NYC", Get("state"))
   305  	SetDefault("state", "NYC")
   306  	assert.Equal(t, "NYC", Get("state"))
   307  }
   308  
   309  func TestAliases(t *testing.T) {
   310  	RegisterAlias("years", "age")
   311  	assert.Equal(t, 40, Get("years"))
   312  	Set("years", 45)
   313  	assert.Equal(t, 45, Get("age"))
   314  }
   315  
   316  func TestAliasInConfigFile(t *testing.T) {
   317  	// the config file specifies "beard".  If we make this an alias for
   318  	// "hasbeard", we still want the old config file to work with beard.
   319  	RegisterAlias("beard", "hasbeard")
   320  	assert.Equal(t, true, Get("hasbeard"))
   321  	Set("hasbeard", false)
   322  	assert.Equal(t, false, Get("beard"))
   323  }
   324  
   325  func TestYML(t *testing.T) {
   326  	initYAML()
   327  	assert.Equal(t, "steve", Get("name"))
   328  }
   329  
   330  func TestJSON(t *testing.T) {
   331  	initJSON()
   332  	assert.Equal(t, "0001", Get("id"))
   333  }
   334  
   335  func TestProperties(t *testing.T) {
   336  	initProperties()
   337  	assert.Equal(t, "0001", Get("p_id"))
   338  }
   339  
   340  func TestTOML(t *testing.T) {
   341  	initTOML()
   342  	assert.Equal(t, "TOML Example", Get("title"))
   343  }
   344  
   345  func TestHCL(t *testing.T) {
   346  	initHcl()
   347  	assert.Equal(t, "0001", Get("id"))
   348  	assert.Equal(t, 0.55, Get("ppu"))
   349  	assert.Equal(t, "donut", Get("type"))
   350  	assert.Equal(t, "Cake", Get("name"))
   351  	Set("id", "0002")
   352  	assert.Equal(t, "0002", Get("id"))
   353  	assert.NotEqual(t, "cronut", Get("type"))
   354  }
   355  
   356  func TestRemotePrecedence(t *testing.T) {
   357  	initJSON()
   358  
   359  	remote := bytes.NewReader(remoteExample)
   360  	assert.Equal(t, "0001", Get("id"))
   361  	unmarshalReader(remote, v.kvstore)
   362  	assert.Equal(t, "0001", Get("id"))
   363  	assert.NotEqual(t, "cronut", Get("type"))
   364  	assert.Equal(t, "remote", Get("newkey"))
   365  	Set("newkey", "newvalue")
   366  	assert.NotEqual(t, "remote", Get("newkey"))
   367  	assert.Equal(t, "newvalue", Get("newkey"))
   368  	Set("newkey", "remote")
   369  }
   370  
   371  func TestEnv(t *testing.T) {
   372  	initJSON()
   373  
   374  	BindEnv("id")
   375  	BindEnv("f", "FOOD")
   376  
   377  	os.Setenv("ID", "13")
   378  	os.Setenv("FOOD", "apple")
   379  	os.Setenv("NAME", "crunk")
   380  
   381  	assert.Equal(t, "13", Get("id"))
   382  	assert.Equal(t, "apple", Get("f"))
   383  	assert.Equal(t, "Cake", Get("name"))
   384  
   385  	AutomaticEnv()
   386  
   387  	assert.Equal(t, "crunk", Get("name"))
   388  
   389  }
   390  
   391  func TestEnvPrefix(t *testing.T) {
   392  	initJSON()
   393  
   394  	SetEnvPrefix("foo") // will be uppercased automatically
   395  	BindEnv("id")
   396  	BindEnv("f", "FOOD") // not using prefix
   397  
   398  	os.Setenv("FOO_ID", "13")
   399  	os.Setenv("FOOD", "apple")
   400  	os.Setenv("FOO_NAME", "crunk")
   401  
   402  	assert.Equal(t, "13", Get("id"))
   403  	assert.Equal(t, "apple", Get("f"))
   404  	assert.Equal(t, "Cake", Get("name"))
   405  
   406  	AutomaticEnv()
   407  
   408  	assert.Equal(t, "crunk", Get("name"))
   409  }
   410  
   411  func TestAutoEnv(t *testing.T) {
   412  	Reset()
   413  
   414  	AutomaticEnv()
   415  	os.Setenv("FOO_BAR", "13")
   416  	assert.Equal(t, "13", Get("foo_bar"))
   417  }
   418  
   419  func TestAutoEnvWithPrefix(t *testing.T) {
   420  	Reset()
   421  
   422  	AutomaticEnv()
   423  	SetEnvPrefix("Baz")
   424  	os.Setenv("BAZ_BAR", "13")
   425  	assert.Equal(t, "13", Get("bar"))
   426  }
   427  
   428  func TestSetEnvKeyReplacer(t *testing.T) {
   429  	Reset()
   430  
   431  	AutomaticEnv()
   432  	os.Setenv("REFRESH_INTERVAL", "30s")
   433  
   434  	replacer := strings.NewReplacer("-", "_")
   435  	SetEnvKeyReplacer(replacer)
   436  
   437  	assert.Equal(t, "30s", Get("refresh-interval"))
   438  }
   439  
   440  func TestAllKeys(t *testing.T) {
   441  	initConfigs()
   442  
   443  	ks := sort.StringSlice{"title", "newkey", "owner.organization", "owner.dob", "owner.bio", "name", "beard", "ppu", "batters.batter", "hobbies", "clothing.jacket", "clothing.trousers", "clothing.pants.size", "age", "hacker", "id", "type", "eyes", "p_id", "p_ppu", "p_batters.batter.type", "p_type", "p_name", "foos"}
   444  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
   445  	all := map[string]interface{}{"owner": map[string]interface{}{"organization": "MongoDB", "bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "title": "TOML Example", "ppu": 0.55, "eyes": "brown", "clothing": map[string]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[string]interface{}{"size": "large"}}, "id": "0001", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hacker": true, "beard": true, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "age": 35, "type": "donut", "newkey": "remote", "name": "Cake", "p_id": "0001", "p_ppu": "0.55", "p_name": "Cake", "p_batters": map[string]interface{}{"batter": map[string]interface{}{"type": "Regular"}}, "p_type": "donut", "foos": []map[string]interface{}{map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"key": 1}, map[string]interface{}{"key": 2}, map[string]interface{}{"key": 3}, map[string]interface{}{"key": 4}}}}}
   446  
   447  	var allkeys sort.StringSlice
   448  	allkeys = AllKeys()
   449  	allkeys.Sort()
   450  	ks.Sort()
   451  
   452  	assert.Equal(t, ks, allkeys)
   453  	assert.Equal(t, all, AllSettings())
   454  }
   455  
   456  func TestAllKeysWithEnv(t *testing.T) {
   457  	v := New()
   458  
   459  	// bind and define environment variables (including a nested one)
   460  	v.BindEnv("id")
   461  	v.BindEnv("foo.bar")
   462  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
   463  	os.Setenv("ID", "13")
   464  	os.Setenv("FOO_BAR", "baz")
   465  
   466  	expectedKeys := sort.StringSlice{"id", "foo.bar"}
   467  	expectedKeys.Sort()
   468  	keys := sort.StringSlice(v.AllKeys())
   469  	keys.Sort()
   470  	assert.Equal(t, expectedKeys, keys)
   471  }
   472  
   473  func TestAliasesOfAliases(t *testing.T) {
   474  	Set("Title", "Checking Case")
   475  	RegisterAlias("Foo", "Bar")
   476  	RegisterAlias("Bar", "Title")
   477  	assert.Equal(t, "Checking Case", Get("FOO"))
   478  }
   479  
   480  func TestRecursiveAliases(t *testing.T) {
   481  	RegisterAlias("Baz", "Roo")
   482  	RegisterAlias("Roo", "baz")
   483  }
   484  
   485  func TestUnmarshal(t *testing.T) {
   486  	SetDefault("port", 1313)
   487  	Set("name", "Steve")
   488  	Set("duration", "1s1ms")
   489  
   490  	type config struct {
   491  		Port     int
   492  		Name     string
   493  		Duration time.Duration
   494  	}
   495  
   496  	var C config
   497  
   498  	err := Unmarshal(&C)
   499  	if err != nil {
   500  		t.Fatalf("unable to decode into struct, %v", err)
   501  	}
   502  
   503  	assert.Equal(t, &config{Name: "Steve", Port: 1313, Duration: time.Second + time.Millisecond}, &C)
   504  
   505  	Set("port", 1234)
   506  	err = Unmarshal(&C)
   507  	if err != nil {
   508  		t.Fatalf("unable to decode into struct, %v", err)
   509  	}
   510  	assert.Equal(t, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond}, &C)
   511  }
   512  
   513  func TestUnmarshalWithDecoderOptions(t *testing.T) {
   514  	Set("credentials", "{\"foo\":\"bar\"}")
   515  
   516  	opt := DecodeHook(mapstructure.ComposeDecodeHookFunc(
   517  		mapstructure.StringToTimeDurationHookFunc(),
   518  		mapstructure.StringToSliceHookFunc(","),
   519  		// Custom Decode Hook Function
   520  		func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) {
   521  			if rf != reflect.String || rt != reflect.Map {
   522  				return data, nil
   523  			}
   524  			m := map[string]string{}
   525  			raw := data.(string)
   526  			if raw == "" {
   527  				return m, nil
   528  			}
   529  			return m, json.Unmarshal([]byte(raw), &m)
   530  		},
   531  	))
   532  
   533  	type config struct {
   534  		Credentials map[string]string
   535  	}
   536  
   537  	var C config
   538  
   539  	err := Unmarshal(&C, opt)
   540  	if err != nil {
   541  		t.Fatalf("unable to decode into struct, %v", err)
   542  	}
   543  
   544  	assert.Equal(t, &config{
   545  		Credentials: map[string]string{"foo": "bar"},
   546  	}, &C)
   547  }
   548  
   549  func TestBindPFlags(t *testing.T) {
   550  	v := New() // create independent Viper object
   551  	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
   552  
   553  	var testValues = map[string]*string{
   554  		"host":     nil,
   555  		"port":     nil,
   556  		"endpoint": nil,
   557  	}
   558  
   559  	var mutatedTestValues = map[string]string{
   560  		"host":     "localhost",
   561  		"port":     "6060",
   562  		"endpoint": "/public",
   563  	}
   564  
   565  	for name := range testValues {
   566  		testValues[name] = flagSet.String(name, "", "test")
   567  	}
   568  
   569  	err := v.BindPFlags(flagSet)
   570  	if err != nil {
   571  		t.Fatalf("error binding flag set, %v", err)
   572  	}
   573  
   574  	flagSet.VisitAll(func(flag *pflag.Flag) {
   575  		flag.Value.Set(mutatedTestValues[flag.Name])
   576  		flag.Changed = true
   577  	})
   578  
   579  	for name, expected := range mutatedTestValues {
   580  		assert.Equal(t, expected, v.Get(name))
   581  	}
   582  
   583  }
   584  
   585  func TestBindPFlagsStringSlice(t *testing.T) {
   586  	for _, testValue := range []struct {
   587  		Expected []string
   588  		Value    string
   589  	}{
   590  		{[]string{}, ""},
   591  		{[]string{"jeden"}, "jeden"},
   592  		{[]string{"dwa", "trzy"}, "dwa,trzy"},
   593  		{[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""}} {
   594  
   595  		for _, changed := range []bool{true, false} {
   596  			v := New() // create independent Viper object
   597  			flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
   598  			flagSet.StringSlice("stringslice", testValue.Expected, "test")
   599  			flagSet.Visit(func(f *pflag.Flag) {
   600  				if len(testValue.Value) > 0 {
   601  					f.Value.Set(testValue.Value)
   602  					f.Changed = changed
   603  				}
   604  			})
   605  
   606  			err := v.BindPFlags(flagSet)
   607  			if err != nil {
   608  				t.Fatalf("error binding flag set, %v", err)
   609  			}
   610  
   611  			type TestStr struct {
   612  				StringSlice []string
   613  			}
   614  			val := &TestStr{}
   615  			if err := v.Unmarshal(val); err != nil {
   616  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
   617  			}
   618  			assert.Equal(t, testValue.Expected, val.StringSlice)
   619  		}
   620  	}
   621  }
   622  
   623  func TestBindPFlag(t *testing.T) {
   624  	var testString = "testing"
   625  	var testValue = newStringValue(testString, &testString)
   626  
   627  	flag := &pflag.Flag{
   628  		Name:    "testflag",
   629  		Value:   testValue,
   630  		Changed: false,
   631  	}
   632  
   633  	BindPFlag("testvalue", flag)
   634  
   635  	assert.Equal(t, testString, Get("testvalue"))
   636  
   637  	flag.Value.Set("testing_mutate")
   638  	flag.Changed = true //hack for pflag usage
   639  
   640  	assert.Equal(t, "testing_mutate", Get("testvalue"))
   641  
   642  }
   643  
   644  func TestBoundCaseSensitivity(t *testing.T) {
   645  	assert.Equal(t, "brown", Get("eyes"))
   646  
   647  	BindEnv("eYEs", "TURTLE_EYES")
   648  	os.Setenv("TURTLE_EYES", "blue")
   649  
   650  	assert.Equal(t, "blue", Get("eyes"))
   651  
   652  	var testString = "green"
   653  	var testValue = newStringValue(testString, &testString)
   654  
   655  	flag := &pflag.Flag{
   656  		Name:    "eyeballs",
   657  		Value:   testValue,
   658  		Changed: true,
   659  	}
   660  
   661  	BindPFlag("eYEs", flag)
   662  	assert.Equal(t, "green", Get("eyes"))
   663  
   664  }
   665  
   666  func TestSizeInBytes(t *testing.T) {
   667  	input := map[string]uint{
   668  		"":               0,
   669  		"b":              0,
   670  		"12 bytes":       0,
   671  		"200000000000gb": 0,
   672  		"12 b":           12,
   673  		"43 MB":          43 * (1 << 20),
   674  		"10mb":           10 * (1 << 20),
   675  		"1gb":            1 << 30,
   676  	}
   677  
   678  	for str, expected := range input {
   679  		assert.Equal(t, expected, parseSizeInBytes(str), str)
   680  	}
   681  }
   682  
   683  func TestFindsNestedKeys(t *testing.T) {
   684  	initConfigs()
   685  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
   686  
   687  	Set("super", map[string]interface{}{
   688  		"deep": map[string]interface{}{
   689  			"nested": "value",
   690  		},
   691  	})
   692  
   693  	expected := map[string]interface{}{
   694  		"super": map[string]interface{}{
   695  			"deep": map[string]interface{}{
   696  				"nested": "value",
   697  			},
   698  		},
   699  		"super.deep": map[string]interface{}{
   700  			"nested": "value",
   701  		},
   702  		"super.deep.nested":  "value",
   703  		"owner.organization": "MongoDB",
   704  		"batters.batter": []interface{}{
   705  			map[string]interface{}{
   706  				"type": "Regular",
   707  			},
   708  			map[string]interface{}{
   709  				"type": "Chocolate",
   710  			},
   711  			map[string]interface{}{
   712  				"type": "Blueberry",
   713  			},
   714  			map[string]interface{}{
   715  				"type": "Devil's Food",
   716  			},
   717  		},
   718  		"hobbies": []interface{}{
   719  			"skateboarding", "snowboarding", "go",
   720  		},
   721  		"title":  "TOML Example",
   722  		"newkey": "remote",
   723  		"batters": map[string]interface{}{
   724  			"batter": []interface{}{
   725  				map[string]interface{}{
   726  					"type": "Regular",
   727  				},
   728  				map[string]interface{}{
   729  					"type": "Chocolate",
   730  				}, map[string]interface{}{
   731  					"type": "Blueberry",
   732  				}, map[string]interface{}{
   733  					"type": "Devil's Food",
   734  				},
   735  			},
   736  		},
   737  		"eyes": "brown",
   738  		"age":  35,
   739  		"owner": map[string]interface{}{
   740  			"organization": "MongoDB",
   741  			"bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
   742  			"dob":          dob,
   743  		},
   744  		"owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large",
   745  		"type":      "donut",
   746  		"id":        "0001",
   747  		"name":      "Cake",
   748  		"hacker":    true,
   749  		"ppu":       0.55,
   750  		"clothing": map[string]interface{}{
   751  			"jacket":   "leather",
   752  			"trousers": "denim",
   753  			"pants": map[string]interface{}{
   754  				"size": "large",
   755  			},
   756  		},
   757  		"clothing.jacket":     "leather",
   758  		"clothing.pants.size": "large",
   759  		"clothing.trousers":   "denim",
   760  		"owner.dob":           dob,
   761  		"beard":               true,
   762  		"foos": []map[string]interface{}{
   763  			map[string]interface{}{
   764  				"foo": []map[string]interface{}{
   765  					map[string]interface{}{
   766  						"key": 1,
   767  					},
   768  					map[string]interface{}{
   769  						"key": 2,
   770  					},
   771  					map[string]interface{}{
   772  						"key": 3,
   773  					},
   774  					map[string]interface{}{
   775  						"key": 4,
   776  					},
   777  				},
   778  			},
   779  		},
   780  	}
   781  
   782  	for key, expectedValue := range expected {
   783  
   784  		assert.Equal(t, expectedValue, v.Get(key))
   785  	}
   786  
   787  }
   788  
   789  func TestReadBufConfig(t *testing.T) {
   790  	v := New()
   791  	v.SetConfigType("yaml")
   792  	v.ReadConfig(bytes.NewBuffer(yamlExample))
   793  	t.Log(v.AllKeys())
   794  
   795  	assert.True(t, v.InConfig("name"))
   796  	assert.False(t, v.InConfig("state"))
   797  	assert.Equal(t, "steve", v.Get("name"))
   798  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
   799  	assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
   800  	assert.Equal(t, 35, v.Get("age"))
   801  }
   802  
   803  func TestIsSet(t *testing.T) {
   804  	v := New()
   805  	v.SetConfigType("yaml")
   806  	v.ReadConfig(bytes.NewBuffer(yamlExample))
   807  	assert.True(t, v.IsSet("clothing.jacket"))
   808  	assert.False(t, v.IsSet("clothing.jackets"))
   809  	assert.False(t, v.IsSet("helloworld"))
   810  	v.Set("helloworld", "fubar")
   811  	assert.True(t, v.IsSet("helloworld"))
   812  }
   813  
   814  func TestDirsSearch(t *testing.T) {
   815  
   816  	root, config, cleanup := initDirs(t)
   817  	defer cleanup()
   818  
   819  	v := New()
   820  	v.SetConfigName(config)
   821  	v.SetDefault(`key`, `default`)
   822  
   823  	entries, err := ioutil.ReadDir(root)
   824  	for _, e := range entries {
   825  		if e.IsDir() {
   826  			v.AddConfigPath(e.Name())
   827  		}
   828  	}
   829  
   830  	err = v.ReadInConfig()
   831  	assert.Nil(t, err)
   832  
   833  	assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`))
   834  }
   835  
   836  func TestWrongDirsSearchNotFound(t *testing.T) {
   837  
   838  	_, config, cleanup := initDirs(t)
   839  	defer cleanup()
   840  
   841  	v := New()
   842  	v.SetConfigName(config)
   843  	v.SetDefault(`key`, `default`)
   844  
   845  	v.AddConfigPath(`whattayoutalkingbout`)
   846  	v.AddConfigPath(`thispathaintthere`)
   847  
   848  	err := v.ReadInConfig()
   849  	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
   850  
   851  	// Even though config did not load and the error might have
   852  	// been ignored by the client, the default still loads
   853  	assert.Equal(t, `default`, v.GetString(`key`))
   854  }
   855  
   856  func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
   857  
   858  	_, config, cleanup := initDirs(t)
   859  	defer cleanup()
   860  
   861  	v := New()
   862  	v.SetConfigName(config)
   863  	v.SetDefault(`key`, `default`)
   864  
   865  	v.AddConfigPath(`whattayoutalkingbout`)
   866  	v.AddConfigPath(`thispathaintthere`)
   867  
   868  	err := v.MergeInConfig()
   869  	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
   870  
   871  	// Even though config did not load and the error might have
   872  	// been ignored by the client, the default still loads
   873  	assert.Equal(t, `default`, v.GetString(`key`))
   874  }
   875  
   876  func TestSub(t *testing.T) {
   877  	v := New()
   878  	v.SetConfigType("yaml")
   879  	v.ReadConfig(bytes.NewBuffer(yamlExample))
   880  
   881  	subv := v.Sub("clothing")
   882  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
   883  
   884  	subv = v.Sub("clothing.pants")
   885  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size"))
   886  
   887  	subv = v.Sub("clothing.pants.size")
   888  	assert.Equal(t, (*Viper)(nil), subv)
   889  
   890  	subv = v.Sub("missing.key")
   891  	assert.Equal(t, (*Viper)(nil), subv)
   892  }
   893  
   894  var hclWriteExpected = []byte(`"foos" = {
   895    "foo" = {
   896      "key" = 1
   897    }
   898  
   899    "foo" = {
   900      "key" = 2
   901    }
   902  
   903    "foo" = {
   904      "key" = 3
   905    }
   906  
   907    "foo" = {
   908      "key" = 4
   909    }
   910  }
   911  
   912  "id" = "0001"
   913  
   914  "name" = "Cake"
   915  
   916  "ppu" = 0.55
   917  
   918  "type" = "donut"`)
   919  
   920  func TestWriteConfigHCL(t *testing.T) {
   921  	v := New()
   922  	fs := afero.NewMemMapFs()
   923  	v.SetFs(fs)
   924  	v.SetConfigName("c")
   925  	v.SetConfigType("hcl")
   926  	err := v.ReadConfig(bytes.NewBuffer(hclExample))
   927  	if err != nil {
   928  		t.Fatal(err)
   929  	}
   930  	if err := v.WriteConfigAs("c.hcl"); err != nil {
   931  		t.Fatal(err)
   932  	}
   933  	read, err := afero.ReadFile(fs, "c.hcl")
   934  	if err != nil {
   935  		t.Fatal(err)
   936  	}
   937  	assert.Equal(t, hclWriteExpected, read)
   938  }
   939  
   940  var jsonWriteExpected = []byte(`{
   941    "batters": {
   942      "batter": [
   943        {
   944          "type": "Regular"
   945        },
   946        {
   947          "type": "Chocolate"
   948        },
   949        {
   950          "type": "Blueberry"
   951        },
   952        {
   953          "type": "Devil's Food"
   954        }
   955      ]
   956    },
   957    "id": "0001",
   958    "name": "Cake",
   959    "ppu": 0.55,
   960    "type": "donut"
   961  }`)
   962  
   963  func TestWriteConfigJson(t *testing.T) {
   964  	v := New()
   965  	fs := afero.NewMemMapFs()
   966  	v.SetFs(fs)
   967  	v.SetConfigName("c")
   968  	v.SetConfigType("json")
   969  	err := v.ReadConfig(bytes.NewBuffer(jsonExample))
   970  	if err != nil {
   971  		t.Fatal(err)
   972  	}
   973  	if err := v.WriteConfigAs("c.json"); err != nil {
   974  		t.Fatal(err)
   975  	}
   976  	read, err := afero.ReadFile(fs, "c.json")
   977  	if err != nil {
   978  		t.Fatal(err)
   979  	}
   980  	assert.Equal(t, jsonWriteExpected, read)
   981  }
   982  
   983  var propertiesWriteExpected = []byte(`p_id = 0001
   984  p_type = donut
   985  p_name = Cake
   986  p_ppu = 0.55
   987  p_batters.batter.type = Regular
   988  `)
   989  
   990  func TestWriteConfigProperties(t *testing.T) {
   991  	v := New()
   992  	fs := afero.NewMemMapFs()
   993  	v.SetFs(fs)
   994  	v.SetConfigName("c")
   995  	v.SetConfigType("properties")
   996  	err := v.ReadConfig(bytes.NewBuffer(propertiesExample))
   997  	if err != nil {
   998  		t.Fatal(err)
   999  	}
  1000  	if err := v.WriteConfigAs("c.properties"); err != nil {
  1001  		t.Fatal(err)
  1002  	}
  1003  	read, err := afero.ReadFile(fs, "c.properties")
  1004  	if err != nil {
  1005  		t.Fatal(err)
  1006  	}
  1007  	assert.Equal(t, propertiesWriteExpected, read)
  1008  }
  1009  
  1010  func TestWriteConfigTOML(t *testing.T) {
  1011  	fs := afero.NewMemMapFs()
  1012  	v := New()
  1013  	v.SetFs(fs)
  1014  	v.SetConfigName("c")
  1015  	v.SetConfigType("toml")
  1016  	err := v.ReadConfig(bytes.NewBuffer(tomlExample))
  1017  	if err != nil {
  1018  		t.Fatal(err)
  1019  	}
  1020  	if err := v.WriteConfigAs("c.toml"); err != nil {
  1021  		t.Fatal(err)
  1022  	}
  1023  
  1024  	// The TOML String method does not order the contents.
  1025  	// Therefore, we must read the generated file and compare the data.
  1026  	v2 := New()
  1027  	v2.SetFs(fs)
  1028  	v2.SetConfigName("c")
  1029  	v2.SetConfigType("toml")
  1030  	v2.SetConfigFile("c.toml")
  1031  	err = v2.ReadInConfig()
  1032  	if err != nil {
  1033  		t.Fatal(err)
  1034  	}
  1035  
  1036  	assert.Equal(t, v.GetString("title"), v2.GetString("title"))
  1037  	assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio"))
  1038  	assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob"))
  1039  	assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization"))
  1040  }
  1041  
  1042  var yamlWriteExpected = []byte(`age: 35
  1043  beard: true
  1044  clothing:
  1045    jacket: leather
  1046    pants:
  1047      size: large
  1048    trousers: denim
  1049  eyes: brown
  1050  hacker: true
  1051  hobbies:
  1052  - skateboarding
  1053  - snowboarding
  1054  - go
  1055  name: steve
  1056  `)
  1057  
  1058  func TestWriteConfigYAML(t *testing.T) {
  1059  	v := New()
  1060  	fs := afero.NewMemMapFs()
  1061  	v.SetFs(fs)
  1062  	v.SetConfigName("c")
  1063  	v.SetConfigType("yaml")
  1064  	err := v.ReadConfig(bytes.NewBuffer(yamlExample))
  1065  	if err != nil {
  1066  		t.Fatal(err)
  1067  	}
  1068  	if err := v.WriteConfigAs("c.yaml"); err != nil {
  1069  		t.Fatal(err)
  1070  	}
  1071  	read, err := afero.ReadFile(fs, "c.yaml")
  1072  	if err != nil {
  1073  		t.Fatal(err)
  1074  	}
  1075  	assert.Equal(t, yamlWriteExpected, read)
  1076  }
  1077  
  1078  var yamlMergeExampleTgt = []byte(`
  1079  hello:
  1080      pop: 37890
  1081      lagrenum: 765432101234567
  1082      world:
  1083      - us
  1084      - uk
  1085      - fr
  1086      - de
  1087  `)
  1088  
  1089  var yamlMergeExampleSrc = []byte(`
  1090  hello:
  1091      pop: 45000
  1092      lagrenum: 7654321001234567
  1093      universe:
  1094      - mw
  1095      - ad
  1096  fu: bar
  1097  `)
  1098  
  1099  func TestMergeConfig(t *testing.T) {
  1100  	v := New()
  1101  	v.SetConfigType("yml")
  1102  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1103  		t.Fatal(err)
  1104  	}
  1105  
  1106  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  1107  		t.Fatalf("pop != 37890, = %d", pop)
  1108  	}
  1109  
  1110  	if pop := v.GetInt32("hello.pop"); pop != int32(37890) {
  1111  		t.Fatalf("pop != 37890, = %d", pop)
  1112  	}
  1113  
  1114  	if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) {
  1115  		t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop)
  1116  	}
  1117  
  1118  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1119  		t.Fatalf("len(world) != 4, = %d", len(world))
  1120  	}
  1121  
  1122  	if fu := v.GetString("fu"); fu != "" {
  1123  		t.Fatalf("fu != \"\", = %s", fu)
  1124  	}
  1125  
  1126  	if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  1127  		t.Fatal(err)
  1128  	}
  1129  
  1130  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  1131  		t.Fatalf("pop != 45000, = %d", pop)
  1132  	}
  1133  
  1134  	if pop := v.GetInt32("hello.pop"); pop != int32(45000) {
  1135  		t.Fatalf("pop != 45000, = %d", pop)
  1136  	}
  1137  
  1138  	if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) {
  1139  		t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop)
  1140  	}
  1141  
  1142  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1143  		t.Fatalf("len(world) != 4, = %d", len(world))
  1144  	}
  1145  
  1146  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  1147  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  1148  	}
  1149  
  1150  	if fu := v.GetString("fu"); fu != "bar" {
  1151  		t.Fatalf("fu != \"bar\", = %s", fu)
  1152  	}
  1153  }
  1154  
  1155  func TestMergeConfigNoMerge(t *testing.T) {
  1156  	v := New()
  1157  	v.SetConfigType("yml")
  1158  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1159  		t.Fatal(err)
  1160  	}
  1161  
  1162  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  1163  		t.Fatalf("pop != 37890, = %d", pop)
  1164  	}
  1165  
  1166  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1167  		t.Fatalf("len(world) != 4, = %d", len(world))
  1168  	}
  1169  
  1170  	if fu := v.GetString("fu"); fu != "" {
  1171  		t.Fatalf("fu != \"\", = %s", fu)
  1172  	}
  1173  
  1174  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  1175  		t.Fatal(err)
  1176  	}
  1177  
  1178  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  1179  		t.Fatalf("pop != 45000, = %d", pop)
  1180  	}
  1181  
  1182  	if world := v.GetStringSlice("hello.world"); len(world) != 0 {
  1183  		t.Fatalf("len(world) != 0, = %d", len(world))
  1184  	}
  1185  
  1186  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  1187  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  1188  	}
  1189  
  1190  	if fu := v.GetString("fu"); fu != "bar" {
  1191  		t.Fatalf("fu != \"bar\", = %s", fu)
  1192  	}
  1193  }
  1194  
  1195  func TestUnmarshalingWithAliases(t *testing.T) {
  1196  	v := New()
  1197  	v.SetDefault("ID", 1)
  1198  	v.Set("name", "Steve")
  1199  	v.Set("lastname", "Owen")
  1200  
  1201  	v.RegisterAlias("UserID", "ID")
  1202  	v.RegisterAlias("Firstname", "name")
  1203  	v.RegisterAlias("Surname", "lastname")
  1204  
  1205  	type config struct {
  1206  		ID        int
  1207  		FirstName string
  1208  		Surname   string
  1209  	}
  1210  
  1211  	var C config
  1212  	err := v.Unmarshal(&C)
  1213  	if err != nil {
  1214  		t.Fatalf("unable to decode into struct, %v", err)
  1215  	}
  1216  
  1217  	assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C)
  1218  }
  1219  
  1220  func TestSetConfigNameClearsFileCache(t *testing.T) {
  1221  	SetConfigFile("/tmp/config.yaml")
  1222  	SetConfigName("default")
  1223  	f, err := v.getConfigFile()
  1224  	if err == nil {
  1225  		t.Fatalf("config file cache should have been cleared")
  1226  	}
  1227  	assert.Empty(t, f)
  1228  }
  1229  
  1230  func TestShadowedNestedValue(t *testing.T) {
  1231  
  1232  	config := `name: steve
  1233  clothing:
  1234    jacket: leather
  1235    trousers: denim
  1236    pants:
  1237      size: large
  1238  `
  1239  	initConfig("yaml", config)
  1240  
  1241  	assert.Equal(t, "steve", GetString("name"))
  1242  
  1243  	polyester := "polyester"
  1244  	SetDefault("clothing.shirt", polyester)
  1245  	SetDefault("clothing.jacket.price", 100)
  1246  
  1247  	assert.Equal(t, "leather", GetString("clothing.jacket"))
  1248  	assert.Nil(t, Get("clothing.jacket.price"))
  1249  	assert.Equal(t, polyester, GetString("clothing.shirt"))
  1250  
  1251  	clothingSettings := AllSettings()["clothing"].(map[string]interface{})
  1252  	assert.Equal(t, "leather", clothingSettings["jacket"])
  1253  	assert.Equal(t, polyester, clothingSettings["shirt"])
  1254  }
  1255  
  1256  func TestDotParameter(t *testing.T) {
  1257  	initJSON()
  1258  	// shoud take precedence over batters defined in jsonExample
  1259  	r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
  1260  	unmarshalReader(r, v.config)
  1261  
  1262  	actual := Get("batters.batter")
  1263  	expected := []interface{}{map[string]interface{}{"type": "Small"}}
  1264  	assert.Equal(t, expected, actual)
  1265  }
  1266  
  1267  func TestCaseInsensitive(t *testing.T) {
  1268  	for _, config := range []struct {
  1269  		typ     string
  1270  		content string
  1271  	}{
  1272  		{"yaml", `
  1273  aBcD: 1
  1274  eF:
  1275    gH: 2
  1276    iJk: 3
  1277    Lm:
  1278      nO: 4
  1279      P:
  1280        Q: 5
  1281        R: 6
  1282  `},
  1283  		{"json", `{
  1284    "aBcD": 1,
  1285    "eF": {
  1286      "iJk": 3,
  1287      "Lm": {
  1288        "P": {
  1289          "Q": 5,
  1290          "R": 6
  1291        },
  1292        "nO": 4
  1293      },
  1294      "gH": 2
  1295    }
  1296  }`},
  1297  		{"toml", `aBcD = 1
  1298  [eF]
  1299  gH = 2
  1300  iJk = 3
  1301  [eF.Lm]
  1302  nO = 4
  1303  [eF.Lm.P]
  1304  Q = 5
  1305  R = 6
  1306  `},
  1307  	} {
  1308  		doTestCaseInsensitive(t, config.typ, config.content)
  1309  	}
  1310  }
  1311  
  1312  func TestCaseInsensitiveSet(t *testing.T) {
  1313  	Reset()
  1314  	m1 := map[string]interface{}{
  1315  		"Foo": 32,
  1316  		"Bar": map[interface{}]interface {
  1317  		}{
  1318  			"ABc": "A",
  1319  			"cDE": "B"},
  1320  	}
  1321  
  1322  	m2 := map[string]interface{}{
  1323  		"Foo": 52,
  1324  		"Bar": map[interface{}]interface {
  1325  		}{
  1326  			"bCd": "A",
  1327  			"eFG": "B"},
  1328  	}
  1329  
  1330  	Set("Given1", m1)
  1331  	Set("Number1", 42)
  1332  
  1333  	SetDefault("Given2", m2)
  1334  	SetDefault("Number2", 52)
  1335  
  1336  	// Verify SetDefault
  1337  	if v := Get("number2"); v != 52 {
  1338  		t.Fatalf("Expected 52 got %q", v)
  1339  	}
  1340  
  1341  	if v := Get("given2.foo"); v != 52 {
  1342  		t.Fatalf("Expected 52 got %q", v)
  1343  	}
  1344  
  1345  	if v := Get("given2.bar.bcd"); v != "A" {
  1346  		t.Fatalf("Expected A got %q", v)
  1347  	}
  1348  
  1349  	if _, ok := m2["Foo"]; !ok {
  1350  		t.Fatal("Input map changed")
  1351  	}
  1352  
  1353  	// Verify Set
  1354  	if v := Get("number1"); v != 42 {
  1355  		t.Fatalf("Expected 42 got %q", v)
  1356  	}
  1357  
  1358  	if v := Get("given1.foo"); v != 32 {
  1359  		t.Fatalf("Expected 32 got %q", v)
  1360  	}
  1361  
  1362  	if v := Get("given1.bar.abc"); v != "A" {
  1363  		t.Fatalf("Expected A got %q", v)
  1364  	}
  1365  
  1366  	if _, ok := m1["Foo"]; !ok {
  1367  		t.Fatal("Input map changed")
  1368  	}
  1369  }
  1370  
  1371  func TestParseNested(t *testing.T) {
  1372  	type duration struct {
  1373  		Delay time.Duration
  1374  	}
  1375  
  1376  	type item struct {
  1377  		Name   string
  1378  		Delay  time.Duration
  1379  		Nested duration
  1380  	}
  1381  
  1382  	config := `[[parent]]
  1383  	delay="100ms"
  1384  	[parent.nested]
  1385  	delay="200ms"
  1386  `
  1387  	initConfig("toml", config)
  1388  
  1389  	var items []item
  1390  	err := v.UnmarshalKey("parent", &items)
  1391  	if err != nil {
  1392  		t.Fatalf("unable to decode into struct, %v", err)
  1393  	}
  1394  
  1395  	assert.Equal(t, 1, len(items))
  1396  	assert.Equal(t, 100*time.Millisecond, items[0].Delay)
  1397  	assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
  1398  }
  1399  
  1400  func doTestCaseInsensitive(t *testing.T, typ, config string) {
  1401  	initConfig(typ, config)
  1402  	Set("RfD", true)
  1403  	assert.Equal(t, true, Get("rfd"))
  1404  	assert.Equal(t, true, Get("rFD"))
  1405  	assert.Equal(t, 1, cast.ToInt(Get("abcd")))
  1406  	assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
  1407  	assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
  1408  	assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
  1409  	assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
  1410  	assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
  1411  
  1412  }
  1413  
  1414  func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) {
  1415  	watchDir, err := ioutil.TempDir("", "")
  1416  	require.Nil(t, err)
  1417  	configFile := path.Join(watchDir, "config.yaml")
  1418  	err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640)
  1419  	require.Nil(t, err)
  1420  	cleanup := func() {
  1421  		os.RemoveAll(watchDir)
  1422  	}
  1423  	v := New()
  1424  	v.SetConfigFile(configFile)
  1425  	err = v.ReadInConfig()
  1426  	require.Nil(t, err)
  1427  	require.Equal(t, "bar", v.Get("foo"))
  1428  	return v, configFile, cleanup
  1429  }
  1430  
  1431  func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) {
  1432  	watchDir, err := ioutil.TempDir("", "")
  1433  	require.Nil(t, err)
  1434  	dataDir1 := path.Join(watchDir, "data1")
  1435  	err = os.Mkdir(dataDir1, 0777)
  1436  	require.Nil(t, err)
  1437  	realConfigFile := path.Join(dataDir1, "config.yaml")
  1438  	t.Logf("Real config file location: %s\n", realConfigFile)
  1439  	err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640)
  1440  	require.Nil(t, err)
  1441  	cleanup := func() {
  1442  		os.RemoveAll(watchDir)
  1443  	}
  1444  	// now, symlink the tm `data1` dir to `data` in the baseDir
  1445  	os.Symlink(dataDir1, path.Join(watchDir, "data"))
  1446  	// and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`
  1447  	configFile := path.Join(watchDir, "config.yaml")
  1448  	os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile)
  1449  	t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml"))
  1450  	// init Viper
  1451  	v := New()
  1452  	v.SetConfigFile(configFile)
  1453  	err = v.ReadInConfig()
  1454  	require.Nil(t, err)
  1455  	require.Equal(t, "bar", v.Get("foo"))
  1456  	return v, watchDir, configFile, cleanup
  1457  }
  1458  
  1459  func TestWatchFile(t *testing.T) {
  1460  
  1461  	t.Run("file content changed", func(t *testing.T) {
  1462  		// given a `config.yaml` file being watched
  1463  		v, configFile, cleanup := newViperWithConfigFile(t)
  1464  		defer cleanup()
  1465  		_, err := os.Stat(configFile)
  1466  		require.NoError(t, err)
  1467  		t.Logf("test config file: %s\n", configFile)
  1468  		wg := sync.WaitGroup{}
  1469  		wg.Add(1)
  1470  		v.OnConfigChange(func(in fsnotify.Event) {
  1471  			t.Logf("config file changed")
  1472  			wg.Done()
  1473  		})
  1474  		v.WatchConfig()
  1475  		// when overwriting the file and waiting for the custom change notification handler to be triggered
  1476  		err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640)
  1477  		wg.Wait()
  1478  		// then the config value should have changed
  1479  		require.Nil(t, err)
  1480  		assert.Equal(t, "baz", v.Get("foo"))
  1481  	})
  1482  
  1483  	t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) {
  1484  		// skip if not executed on Linux
  1485  		if runtime.GOOS != "linux" {
  1486  			t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
  1487  		}
  1488  		v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t)
  1489  		// defer cleanup()
  1490  		wg := sync.WaitGroup{}
  1491  		v.WatchConfig()
  1492  		v.OnConfigChange(func(in fsnotify.Event) {
  1493  			t.Logf("config file changed")
  1494  			wg.Done()
  1495  		})
  1496  		wg.Add(1)
  1497  		// when link to another `config.yaml` file
  1498  		dataDir2 := path.Join(watchDir, "data2")
  1499  		err := os.Mkdir(dataDir2, 0777)
  1500  		require.Nil(t, err)
  1501  		configFile2 := path.Join(dataDir2, "config.yaml")
  1502  		err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640)
  1503  		require.Nil(t, err)
  1504  		// change the symlink using the `ln -sfn` command
  1505  		err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
  1506  		require.Nil(t, err)
  1507  		wg.Wait()
  1508  		// then
  1509  		require.Nil(t, err)
  1510  		assert.Equal(t, "baz", v.Get("foo"))
  1511  	})
  1512  
  1513  }
  1514  
  1515  func BenchmarkGetBool(b *testing.B) {
  1516  	key := "BenchmarkGetBool"
  1517  	v = New()
  1518  	v.Set(key, true)
  1519  
  1520  	for i := 0; i < b.N; i++ {
  1521  		if !v.GetBool(key) {
  1522  			b.Fatal("GetBool returned false")
  1523  		}
  1524  	}
  1525  }
  1526  
  1527  func BenchmarkGet(b *testing.B) {
  1528  	key := "BenchmarkGet"
  1529  	v = New()
  1530  	v.Set(key, true)
  1531  
  1532  	for i := 0; i < b.N; i++ {
  1533  		if !v.Get(key).(bool) {
  1534  			b.Fatal("Get returned false")
  1535  		}
  1536  	}
  1537  }
  1538  
  1539  // This is the "perfect result" for the above.
  1540  func BenchmarkGetBoolFromMap(b *testing.B) {
  1541  	m := make(map[string]bool)
  1542  	key := "BenchmarkGetBool"
  1543  	m[key] = true
  1544  
  1545  	for i := 0; i < b.N; i++ {
  1546  		if !m[key] {
  1547  			b.Fatal("Map value was false")
  1548  		}
  1549  	}
  1550  }