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