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