github.com/flei2000/viper@v0.0.0-20160111150723-a212099cbe6f/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/ioutil"
    12  	"os"
    13  	"path"
    14  	"reflect"
    15  	"sort"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/spf13/pflag"
    21  	"github.com/stretchr/testify/assert"
    22  )
    23  
    24  var yamlExample = []byte(`Hacker: true
    25  name: steve
    26  hobbies:
    27  - skateboarding
    28  - snowboarding
    29  - go
    30  clothing:
    31    jacket: leather
    32    trousers: denim
    33    pants:
    34      size: large
    35  age: 35
    36  eyes : brown
    37  beard: true
    38  `)
    39  
    40  var tomlExample = []byte(`
    41  title = "TOML Example"
    42  
    43  [owner]
    44  organization = "MongoDB"
    45  Bio = "MongoDB Chief Developer Advocate & Hacker at Large"
    46  dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)
    47  
    48  var jsonExample = []byte(`{
    49  "id": "0001",
    50  "type": "donut",
    51  "name": "Cake",
    52  "ppu": 0.55,
    53  "batters": {
    54          "batter": [
    55                  { "type": "Regular" },
    56                  { "type": "Chocolate" },
    57                  { "type": "Blueberry" },
    58                  { "type": "Devil's Food" }
    59              ]
    60      }
    61  }`)
    62  
    63  var hclExample = []byte(`
    64  id = "0001"
    65  type = "donut"
    66  name = "Cake"
    67  ppu = 0.55
    68  foos {
    69  	foo {
    70  		key = 1
    71  	}
    72  	foo {
    73  		key = 2
    74  	}
    75  	foo {
    76  		key = 3
    77  	}
    78  	foo {
    79  		key = 4
    80  	}
    81  }`)
    82  
    83  var propertiesExample = []byte(`
    84  p_id: 0001
    85  p_type: donut
    86  p_name: Cake
    87  p_ppu: 0.55
    88  p_batters.batter.type: Regular
    89  `)
    90  
    91  var remoteExample = []byte(`{
    92  "id":"0002",
    93  "type":"cronut",
    94  "newkey":"remote"
    95  }`)
    96  
    97  func initConfigs() {
    98  	Reset()
    99  	SetConfigType("yaml")
   100  	r := bytes.NewReader(yamlExample)
   101  	unmarshalReader(r, v.config)
   102  
   103  	SetConfigType("json")
   104  	r = bytes.NewReader(jsonExample)
   105  	unmarshalReader(r, v.config)
   106  
   107  	SetConfigType("hcl")
   108  	r = bytes.NewReader(hclExample)
   109  	unmarshalReader(r, v.config)
   110  
   111  	SetConfigType("properties")
   112  	r = bytes.NewReader(propertiesExample)
   113  	unmarshalReader(r, v.config)
   114  
   115  	SetConfigType("toml")
   116  	r = bytes.NewReader(tomlExample)
   117  	unmarshalReader(r, v.config)
   118  
   119  	SetConfigType("json")
   120  	remote := bytes.NewReader(remoteExample)
   121  	unmarshalReader(remote, v.kvstore)
   122  }
   123  
   124  func initYAML() {
   125  	Reset()
   126  	SetConfigType("yaml")
   127  	r := bytes.NewReader(yamlExample)
   128  
   129  	unmarshalReader(r, v.config)
   130  }
   131  
   132  func initJSON() {
   133  	Reset()
   134  	SetConfigType("json")
   135  	r := bytes.NewReader(jsonExample)
   136  
   137  	unmarshalReader(r, v.config)
   138  }
   139  
   140  func initProperties() {
   141  	Reset()
   142  	SetConfigType("properties")
   143  	r := bytes.NewReader(propertiesExample)
   144  
   145  	unmarshalReader(r, v.config)
   146  }
   147  
   148  func initTOML() {
   149  	Reset()
   150  	SetConfigType("toml")
   151  	r := bytes.NewReader(tomlExample)
   152  
   153  	unmarshalReader(r, v.config)
   154  }
   155  
   156  func initHcl() {
   157  	Reset()
   158  	SetConfigType("hcl")
   159  	r := bytes.NewReader(hclExample)
   160  
   161  	unmarshalReader(r, v.config)
   162  }
   163  
   164  // make directories for testing
   165  func initDirs(t *testing.T) (string, string, func()) {
   166  
   167  	var (
   168  		testDirs = []string{`a a`, `b`, `c\c`, `D_`}
   169  		config   = `improbable`
   170  	)
   171  
   172  	root, err := ioutil.TempDir("", "")
   173  
   174  	cleanup := true
   175  	defer func() {
   176  		if cleanup {
   177  			os.Chdir("..")
   178  			os.RemoveAll(root)
   179  		}
   180  	}()
   181  
   182  	assert.Nil(t, err)
   183  
   184  	err = os.Chdir(root)
   185  	assert.Nil(t, err)
   186  
   187  	for _, dir := range testDirs {
   188  		err = os.Mkdir(dir, 0750)
   189  		assert.Nil(t, err)
   190  
   191  		err = ioutil.WriteFile(
   192  			path.Join(dir, config+".toml"),
   193  			[]byte("key = \"value is "+dir+"\"\n"),
   194  			0640)
   195  		assert.Nil(t, err)
   196  	}
   197  
   198  	cleanup = false
   199  	return root, config, func() {
   200  		os.Chdir("..")
   201  		os.RemoveAll(root)
   202  	}
   203  }
   204  
   205  //stubs for PFlag Values
   206  type stringValue string
   207  
   208  func newStringValue(val string, p *string) *stringValue {
   209  	*p = val
   210  	return (*stringValue)(p)
   211  }
   212  
   213  func (s *stringValue) Set(val string) error {
   214  	*s = stringValue(val)
   215  	return nil
   216  }
   217  
   218  func (s *stringValue) Type() string {
   219  	return "string"
   220  }
   221  
   222  func (s *stringValue) String() string {
   223  	return fmt.Sprintf("%s", *s)
   224  }
   225  
   226  func TestBasics(t *testing.T) {
   227  	SetConfigFile("/tmp/config.yaml")
   228  	assert.Equal(t, "/tmp/config.yaml", v.getConfigFile())
   229  }
   230  
   231  func TestDefault(t *testing.T) {
   232  	SetDefault("age", 45)
   233  	assert.Equal(t, 45, Get("age"))
   234  
   235  	SetDefault("clothing.jacket", "slacks")
   236  	assert.Equal(t, "slacks", Get("clothing.jacket"))
   237  
   238  	SetConfigType("yaml")
   239  	err := ReadConfig(bytes.NewBuffer(yamlExample))
   240  
   241  	assert.NoError(t, err)
   242  	assert.Equal(t, "leather", Get("clothing.jacket"))
   243  }
   244  
   245  func TestUnmarshalling(t *testing.T) {
   246  	SetConfigType("yaml")
   247  	r := bytes.NewReader(yamlExample)
   248  
   249  	unmarshalReader(r, v.config)
   250  	assert.True(t, InConfig("name"))
   251  	assert.False(t, InConfig("state"))
   252  	assert.Equal(t, "steve", Get("name"))
   253  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
   254  	assert.Equal(t, map[interface{}]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[interface{}]interface{}{"size": "large"}}, Get("clothing"))
   255  	assert.Equal(t, 35, Get("age"))
   256  }
   257  
   258  func TestOverrides(t *testing.T) {
   259  	Set("age", 40)
   260  	assert.Equal(t, 40, Get("age"))
   261  }
   262  
   263  func TestDefaultPost(t *testing.T) {
   264  	assert.NotEqual(t, "NYC", Get("state"))
   265  	SetDefault("state", "NYC")
   266  	assert.Equal(t, "NYC", Get("state"))
   267  }
   268  
   269  func TestAliases(t *testing.T) {
   270  	RegisterAlias("years", "age")
   271  	assert.Equal(t, 40, Get("years"))
   272  	Set("years", 45)
   273  	assert.Equal(t, 45, Get("age"))
   274  }
   275  
   276  func TestAliasInConfigFile(t *testing.T) {
   277  	// the config file specifies "beard".  If we make this an alias for
   278  	// "hasbeard", we still want the old config file to work with beard.
   279  	RegisterAlias("beard", "hasbeard")
   280  	assert.Equal(t, true, Get("hasbeard"))
   281  	Set("hasbeard", false)
   282  	assert.Equal(t, false, Get("beard"))
   283  }
   284  
   285  func TestYML(t *testing.T) {
   286  	initYAML()
   287  	assert.Equal(t, "steve", Get("name"))
   288  }
   289  
   290  func TestJSON(t *testing.T) {
   291  	initJSON()
   292  	assert.Equal(t, "0001", Get("id"))
   293  }
   294  
   295  func TestProperties(t *testing.T) {
   296  	initProperties()
   297  	assert.Equal(t, "0001", Get("p_id"))
   298  }
   299  
   300  func TestTOML(t *testing.T) {
   301  	initTOML()
   302  	assert.Equal(t, "TOML Example", Get("title"))
   303  }
   304  
   305  func TestHCL(t *testing.T) {
   306  	initHcl()
   307  	assert.Equal(t, "0001", Get("id"))
   308  	assert.Equal(t, 0.55, Get("ppu"))
   309  	assert.Equal(t, "donut", Get("type"))
   310  	assert.Equal(t, "Cake", Get("name"))
   311  	Set("id", "0002")
   312  	assert.Equal(t, "0002", Get("id"))
   313  	assert.NotEqual(t, "cronut", Get("type"))
   314  }
   315  
   316  func TestRemotePrecedence(t *testing.T) {
   317  	initJSON()
   318  
   319  	remote := bytes.NewReader(remoteExample)
   320  	assert.Equal(t, "0001", Get("id"))
   321  	unmarshalReader(remote, v.kvstore)
   322  	assert.Equal(t, "0001", Get("id"))
   323  	assert.NotEqual(t, "cronut", Get("type"))
   324  	assert.Equal(t, "remote", Get("newkey"))
   325  	Set("newkey", "newvalue")
   326  	assert.NotEqual(t, "remote", Get("newkey"))
   327  	assert.Equal(t, "newvalue", Get("newkey"))
   328  	Set("newkey", "remote")
   329  }
   330  
   331  func TestEnv(t *testing.T) {
   332  	initJSON()
   333  
   334  	BindEnv("id")
   335  	BindEnv("f", "FOOD")
   336  
   337  	os.Setenv("ID", "13")
   338  	os.Setenv("FOOD", "apple")
   339  	os.Setenv("NAME", "crunk")
   340  
   341  	assert.Equal(t, "13", Get("id"))
   342  	assert.Equal(t, "apple", Get("f"))
   343  	assert.Equal(t, "Cake", Get("name"))
   344  
   345  	AutomaticEnv()
   346  
   347  	assert.Equal(t, "crunk", Get("name"))
   348  
   349  }
   350  
   351  func TestEnvPrefix(t *testing.T) {
   352  	initJSON()
   353  
   354  	SetEnvPrefix("foo") // will be uppercased automatically
   355  	BindEnv("id")
   356  	BindEnv("f", "FOOD") // not using prefix
   357  
   358  	os.Setenv("FOO_ID", "13")
   359  	os.Setenv("FOOD", "apple")
   360  	os.Setenv("FOO_NAME", "crunk")
   361  
   362  	assert.Equal(t, "13", Get("id"))
   363  	assert.Equal(t, "apple", Get("f"))
   364  	assert.Equal(t, "Cake", Get("name"))
   365  
   366  	AutomaticEnv()
   367  
   368  	assert.Equal(t, "crunk", Get("name"))
   369  }
   370  
   371  func TestAutoEnv(t *testing.T) {
   372  	Reset()
   373  
   374  	AutomaticEnv()
   375  	os.Setenv("FOO_BAR", "13")
   376  	assert.Equal(t, "13", Get("foo_bar"))
   377  }
   378  
   379  func TestAutoEnvWithPrefix(t *testing.T) {
   380  	Reset()
   381  
   382  	AutomaticEnv()
   383  	SetEnvPrefix("Baz")
   384  	os.Setenv("BAZ_BAR", "13")
   385  	assert.Equal(t, "13", Get("bar"))
   386  }
   387  
   388  func TestSetEnvReplacer(t *testing.T) {
   389  	Reset()
   390  
   391  	AutomaticEnv()
   392  	os.Setenv("REFRESH_INTERVAL", "30s")
   393  
   394  	replacer := strings.NewReplacer("-", "_")
   395  	SetEnvKeyReplacer(replacer)
   396  
   397  	assert.Equal(t, "30s", Get("refresh-interval"))
   398  }
   399  
   400  func TestAllKeys(t *testing.T) {
   401  	initConfigs()
   402  
   403  	ks := sort.StringSlice{"title", "newkey", "owner", "name", "beard", "ppu", "batters", "hobbies", "clothing", "age", "hacker", "id", "type", "eyes", "p_id", "p_ppu", "p_batters.batter.type", "p_type", "p_name", "foos"}
   404  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
   405  	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[interface{}]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[interface{}]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.batter.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}}}}}
   406  
   407  	var allkeys sort.StringSlice
   408  	allkeys = AllKeys()
   409  	allkeys.Sort()
   410  	ks.Sort()
   411  
   412  	assert.Equal(t, ks, allkeys)
   413  	assert.Equal(t, all, AllSettings())
   414  }
   415  
   416  func TestCaseInSensitive(t *testing.T) {
   417  	assert.Equal(t, true, Get("hacker"))
   418  	Set("Title", "Checking Case")
   419  	assert.Equal(t, "Checking Case", Get("tItle"))
   420  }
   421  
   422  func TestAliasesOfAliases(t *testing.T) {
   423  	RegisterAlias("Foo", "Bar")
   424  	RegisterAlias("Bar", "Title")
   425  	assert.Equal(t, "Checking Case", Get("FOO"))
   426  }
   427  
   428  func TestRecursiveAliases(t *testing.T) {
   429  	RegisterAlias("Baz", "Roo")
   430  	RegisterAlias("Roo", "baz")
   431  }
   432  
   433  func TestUnmarshal(t *testing.T) {
   434  	SetDefault("port", 1313)
   435  	Set("name", "Steve")
   436  
   437  	type config struct {
   438  		Port int
   439  		Name string
   440  	}
   441  
   442  	var C config
   443  
   444  	err := Unmarshal(&C)
   445  	if err != nil {
   446  		t.Fatalf("unable to decode into struct, %v", err)
   447  	}
   448  
   449  	assert.Equal(t, &C, &config{Name: "Steve", Port: 1313})
   450  
   451  	Set("port", 1234)
   452  	err = Unmarshal(&C)
   453  	if err != nil {
   454  		t.Fatalf("unable to decode into struct, %v", err)
   455  	}
   456  	assert.Equal(t, &C, &config{Name: "Steve", Port: 1234})
   457  }
   458  
   459  func TestBindPFlags(t *testing.T) {
   460  	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
   461  
   462  	var testValues = map[string]*string{
   463  		"host":     nil,
   464  		"port":     nil,
   465  		"endpoint": nil,
   466  	}
   467  
   468  	var mutatedTestValues = map[string]string{
   469  		"host":     "localhost",
   470  		"port":     "6060",
   471  		"endpoint": "/public",
   472  	}
   473  
   474  	for name, _ := range testValues {
   475  		testValues[name] = flagSet.String(name, "", "test")
   476  	}
   477  
   478  	err := BindPFlags(flagSet)
   479  	if err != nil {
   480  		t.Fatalf("error binding flag set, %v", err)
   481  	}
   482  
   483  	flagSet.VisitAll(func(flag *pflag.Flag) {
   484  		flag.Value.Set(mutatedTestValues[flag.Name])
   485  		flag.Changed = true
   486  	})
   487  
   488  	for name, expected := range mutatedTestValues {
   489  		assert.Equal(t, Get(name), expected)
   490  	}
   491  
   492  }
   493  
   494  func TestBindPFlag(t *testing.T) {
   495  	var testString = "testing"
   496  	var testValue = newStringValue(testString, &testString)
   497  
   498  	flag := &pflag.Flag{
   499  		Name:    "testflag",
   500  		Value:   testValue,
   501  		Changed: false,
   502  	}
   503  
   504  	BindPFlag("testvalue", flag)
   505  
   506  	assert.Equal(t, testString, Get("testvalue"))
   507  
   508  	flag.Value.Set("testing_mutate")
   509  	flag.Changed = true //hack for pflag usage
   510  
   511  	assert.Equal(t, "testing_mutate", Get("testvalue"))
   512  
   513  }
   514  
   515  func TestBoundCaseSensitivity(t *testing.T) {
   516  
   517  	assert.Equal(t, "brown", Get("eyes"))
   518  
   519  	BindEnv("eYEs", "TURTLE_EYES")
   520  	os.Setenv("TURTLE_EYES", "blue")
   521  
   522  	assert.Equal(t, "blue", Get("eyes"))
   523  
   524  	var testString = "green"
   525  	var testValue = newStringValue(testString, &testString)
   526  
   527  	flag := &pflag.Flag{
   528  		Name:    "eyeballs",
   529  		Value:   testValue,
   530  		Changed: true,
   531  	}
   532  
   533  	BindPFlag("eYEs", flag)
   534  	assert.Equal(t, "green", Get("eyes"))
   535  
   536  }
   537  
   538  func TestSizeInBytes(t *testing.T) {
   539  	input := map[string]uint{
   540  		"":               0,
   541  		"b":              0,
   542  		"12 bytes":       0,
   543  		"200000000000gb": 0,
   544  		"12 b":           12,
   545  		"43 MB":          43 * (1 << 20),
   546  		"10mb":           10 * (1 << 20),
   547  		"1gb":            1 << 30,
   548  	}
   549  
   550  	for str, expected := range input {
   551  		assert.Equal(t, expected, parseSizeInBytes(str), str)
   552  	}
   553  }
   554  
   555  func TestFindsNestedKeys(t *testing.T) {
   556  	initConfigs()
   557  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
   558  
   559  	Set("super", map[string]interface{}{
   560  		"deep": map[string]interface{}{
   561  			"nested": "value",
   562  		},
   563  	})
   564  
   565  	expected := map[string]interface{}{
   566  		"super": map[string]interface{}{
   567  			"deep": map[string]interface{}{
   568  				"nested": "value",
   569  			},
   570  		},
   571  		"super.deep": map[string]interface{}{
   572  			"nested": "value",
   573  		},
   574  		"super.deep.nested":  "value",
   575  		"owner.organization": "MongoDB",
   576  		"batters.batter": []interface{}{
   577  			map[string]interface{}{
   578  				"type": "Regular",
   579  			},
   580  			map[string]interface{}{
   581  				"type": "Chocolate",
   582  			},
   583  			map[string]interface{}{
   584  				"type": "Blueberry",
   585  			},
   586  			map[string]interface{}{
   587  				"type": "Devil's Food",
   588  			},
   589  		},
   590  		"hobbies": []interface{}{
   591  			"skateboarding", "snowboarding", "go",
   592  		},
   593  		"title":  "TOML Example",
   594  		"newkey": "remote",
   595  		"batters": map[string]interface{}{
   596  			"batter": []interface{}{
   597  				map[string]interface{}{
   598  					"type": "Regular",
   599  				},
   600  				map[string]interface{}{
   601  					"type": "Chocolate",
   602  				}, map[string]interface{}{
   603  					"type": "Blueberry",
   604  				}, map[string]interface{}{
   605  					"type": "Devil's Food",
   606  				},
   607  			},
   608  		},
   609  		"eyes": "brown",
   610  		"age":  35,
   611  		"owner": map[string]interface{}{
   612  			"organization": "MongoDB",
   613  			"Bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
   614  			"dob":          dob,
   615  		},
   616  		"owner.Bio": "MongoDB Chief Developer Advocate & Hacker at Large",
   617  		"type":      "donut",
   618  		"id":        "0001",
   619  		"name":      "Cake",
   620  		"hacker":    true,
   621  		"ppu":       0.55,
   622  		"clothing": map[interface{}]interface{}{
   623  			"jacket":   "leather",
   624  			"trousers": "denim",
   625  			"pants": map[interface{}]interface{}{
   626  				"size": "large",
   627  			},
   628  		},
   629  		"clothing.jacket":     "leather",
   630  		"clothing.pants.size": "large",
   631  		"clothing.trousers":   "denim",
   632  		"owner.dob":           dob,
   633  		"beard":               true,
   634  		"foos": []map[string]interface{}{
   635  			map[string]interface{}{
   636  				"foo": []map[string]interface{}{
   637  					map[string]interface{}{
   638  						"key": 1,
   639  					},
   640  					map[string]interface{}{
   641  						"key": 2,
   642  					},
   643  					map[string]interface{}{
   644  						"key": 3,
   645  					},
   646  					map[string]interface{}{
   647  						"key": 4,
   648  					},
   649  				},
   650  			},
   651  		},
   652  	}
   653  
   654  	for key, expectedValue := range expected {
   655  
   656  		assert.Equal(t, expectedValue, v.Get(key))
   657  	}
   658  
   659  }
   660  
   661  func TestReadBufConfig(t *testing.T) {
   662  	v := New()
   663  	v.SetConfigType("yaml")
   664  	v.ReadConfig(bytes.NewBuffer(yamlExample))
   665  	t.Log(v.AllKeys())
   666  
   667  	assert.True(t, v.InConfig("name"))
   668  	assert.False(t, v.InConfig("state"))
   669  	assert.Equal(t, "steve", v.Get("name"))
   670  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
   671  	assert.Equal(t, map[interface{}]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[interface{}]interface{}{"size": "large"}}, v.Get("clothing"))
   672  	assert.Equal(t, 35, v.Get("age"))
   673  }
   674  
   675  func TestIsSet(t *testing.T) {
   676  	v := New()
   677  	v.SetConfigType("yaml")
   678  	v.ReadConfig(bytes.NewBuffer(yamlExample))
   679  	assert.True(t, v.IsSet("clothing.jacket"))
   680  	assert.False(t, v.IsSet("clothing.jackets"))
   681  	assert.False(t, v.IsSet("helloworld"))
   682  	v.Set("helloworld", "fubar")
   683  	assert.True(t, v.IsSet("helloworld"))
   684  }
   685  
   686  func TestDirsSearch(t *testing.T) {
   687  
   688  	root, config, cleanup := initDirs(t)
   689  	defer cleanup()
   690  
   691  	v := New()
   692  	v.SetConfigName(config)
   693  	v.SetDefault(`key`, `default`)
   694  
   695  	entries, err := ioutil.ReadDir(root)
   696  	for _, e := range entries {
   697  		if e.IsDir() {
   698  			v.AddConfigPath(e.Name())
   699  		}
   700  	}
   701  
   702  	err = v.ReadInConfig()
   703  	assert.Nil(t, err)
   704  
   705  	assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`))
   706  }
   707  
   708  func TestWrongDirsSearchNotFound(t *testing.T) {
   709  
   710  	_, config, cleanup := initDirs(t)
   711  	defer cleanup()
   712  
   713  	v := New()
   714  	v.SetConfigName(config)
   715  	v.SetDefault(`key`, `default`)
   716  
   717  	v.AddConfigPath(`whattayoutalkingbout`)
   718  	v.AddConfigPath(`thispathaintthere`)
   719  
   720  	err := v.ReadInConfig()
   721  	assert.Equal(t, reflect.TypeOf(UnsupportedConfigError("")), reflect.TypeOf(err))
   722  
   723  	// Even though config did not load and the error might have
   724  	// been ignored by the client, the default still loads
   725  	assert.Equal(t, `default`, v.GetString(`key`))
   726  }
   727  
   728  func TestSub(t *testing.T) {
   729  	v := New()
   730  	v.SetConfigType("yaml")
   731  	v.ReadConfig(bytes.NewBuffer(yamlExample))
   732  
   733  	subv := v.Sub("clothing")
   734  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
   735  
   736  	subv = v.Sub("clothing.pants")
   737  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size"))
   738  
   739  	subv = v.Sub("clothing.pants.size")
   740  	assert.Equal(t, subv, (*Viper)(nil))
   741  }
   742  
   743  var yamlMergeExampleTgt = []byte(`
   744  hello:
   745      pop: 37890
   746      world:
   747      - us
   748      - uk
   749      - fr
   750      - de
   751  `)
   752  
   753  var yamlMergeExampleSrc = []byte(`
   754  hello:
   755      pop: 45000
   756      universe:
   757      - mw
   758      - ad
   759  fu: bar
   760  `)
   761  
   762  func TestMergeConfig(t *testing.T) {
   763  	v := New()
   764  	v.SetConfigType("yml")
   765  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
   766  		t.Fatal(err)
   767  	}
   768  
   769  	if pop := v.GetInt("hello.pop"); pop != 37890 {
   770  		t.Fatalf("pop != 37890, = %d", pop)
   771  	}
   772  
   773  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
   774  		t.Fatalf("len(world) != 4, = %d", len(world))
   775  	}
   776  
   777  	if fu := v.GetString("fu"); fu != "" {
   778  		t.Fatalf("fu != \"\", = %s", fu)
   779  	}
   780  
   781  	if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
   782  		t.Fatal(err)
   783  	}
   784  
   785  	if pop := v.GetInt("hello.pop"); pop != 45000 {
   786  		t.Fatalf("pop != 45000, = %d", pop)
   787  	}
   788  
   789  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
   790  		t.Fatalf("len(world) != 4, = %d", len(world))
   791  	}
   792  
   793  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
   794  		t.Fatalf("len(universe) != 2, = %d", len(universe))
   795  	}
   796  
   797  	if fu := v.GetString("fu"); fu != "bar" {
   798  		t.Fatalf("fu != \"bar\", = %s", fu)
   799  	}
   800  }
   801  
   802  func TestMergeConfigNoMerge(t *testing.T) {
   803  	v := New()
   804  	v.SetConfigType("yml")
   805  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
   806  		t.Fatal(err)
   807  	}
   808  
   809  	if pop := v.GetInt("hello.pop"); pop != 37890 {
   810  		t.Fatalf("pop != 37890, = %d", pop)
   811  	}
   812  
   813  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
   814  		t.Fatalf("len(world) != 4, = %d", len(world))
   815  	}
   816  
   817  	if fu := v.GetString("fu"); fu != "" {
   818  		t.Fatalf("fu != \"\", = %s", fu)
   819  	}
   820  
   821  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
   822  		t.Fatal(err)
   823  	}
   824  
   825  	if pop := v.GetInt("hello.pop"); pop != 45000 {
   826  		t.Fatalf("pop != 45000, = %d", pop)
   827  	}
   828  
   829  	if world := v.GetStringSlice("hello.world"); len(world) != 0 {
   830  		t.Fatalf("len(world) != 0, = %d", len(world))
   831  	}
   832  
   833  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
   834  		t.Fatalf("len(universe) != 2, = %d", len(universe))
   835  	}
   836  
   837  	if fu := v.GetString("fu"); fu != "bar" {
   838  		t.Fatalf("fu != \"bar\", = %s", fu)
   839  	}
   840  }