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