github.com/hukeping/viper@v0.0.0-20151110042204-e37b56e207dd/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 propertiesExample = []byte(`
    64  p_id: 0001
    65  p_type: donut
    66  p_name: Cake
    67  p_ppu: 0.55
    68  p_batters.batter.type: Regular
    69  `)
    70  
    71  var remoteExample = []byte(`{
    72  "id":"0002",
    73  "type":"cronut",
    74  "newkey":"remote"
    75  }`)
    76  
    77  func initConfigs() {
    78  	Reset()
    79  	SetConfigType("yaml")
    80  	r := bytes.NewReader(yamlExample)
    81  	unmarshalReader(r, v.config)
    82  
    83  	SetConfigType("json")
    84  	r = bytes.NewReader(jsonExample)
    85  	unmarshalReader(r, v.config)
    86  
    87  	SetConfigType("properties")
    88  	r = bytes.NewReader(propertiesExample)
    89  	unmarshalReader(r, v.config)
    90  
    91  	SetConfigType("toml")
    92  	r = bytes.NewReader(tomlExample)
    93  	unmarshalReader(r, v.config)
    94  
    95  	SetConfigType("json")
    96  	remote := bytes.NewReader(remoteExample)
    97  	unmarshalReader(remote, v.kvstore)
    98  }
    99  
   100  func initYAML() {
   101  	Reset()
   102  	SetConfigType("yaml")
   103  	r := bytes.NewReader(yamlExample)
   104  
   105  	unmarshalReader(r, v.config)
   106  }
   107  
   108  func initJSON() {
   109  	Reset()
   110  	SetConfigType("json")
   111  	r := bytes.NewReader(jsonExample)
   112  
   113  	unmarshalReader(r, v.config)
   114  }
   115  
   116  func initProperties() {
   117  	Reset()
   118  	SetConfigType("properties")
   119  	r := bytes.NewReader(propertiesExample)
   120  
   121  	unmarshalReader(r, v.config)
   122  }
   123  
   124  func initTOML() {
   125  	Reset()
   126  	SetConfigType("toml")
   127  	r := bytes.NewReader(tomlExample)
   128  
   129  	unmarshalReader(r, v.config)
   130  }
   131  
   132  // make directories for testing
   133  func initDirs(t *testing.T) (string, string, func()) {
   134  
   135  	var (
   136  		testDirs = []string{`a a`, `b`, `c\c`, `D_`}
   137  		config   = `improbable`
   138  	)
   139  
   140  	root, err := ioutil.TempDir("", "")
   141  
   142  	cleanup := true
   143  	defer func() {
   144  		if cleanup {
   145  			os.Chdir("..")
   146  			os.RemoveAll(root)
   147  		}
   148  	}()
   149  
   150  	assert.Nil(t, err)
   151  
   152  	err = os.Chdir(root)
   153  	assert.Nil(t, err)
   154  
   155  	for _, dir := range testDirs {
   156  		err = os.Mkdir(dir, 0750)
   157  		assert.Nil(t, err)
   158  
   159  		err = ioutil.WriteFile(
   160  			path.Join(dir, config+".toml"),
   161  			[]byte("key = \"value is "+dir+"\"\n"),
   162  			0640)
   163  		assert.Nil(t, err)
   164  	}
   165  
   166  	cleanup = false
   167  	return root, config, func() {
   168  		os.Chdir("..")
   169  		os.RemoveAll(root)
   170  	}
   171  }
   172  
   173  //stubs for PFlag Values
   174  type stringValue string
   175  
   176  func newStringValue(val string, p *string) *stringValue {
   177  	*p = val
   178  	return (*stringValue)(p)
   179  }
   180  
   181  func (s *stringValue) Set(val string) error {
   182  	*s = stringValue(val)
   183  	return nil
   184  }
   185  
   186  func (s *stringValue) Type() string {
   187  	return "string"
   188  }
   189  
   190  func (s *stringValue) String() string {
   191  	return fmt.Sprintf("%s", *s)
   192  }
   193  
   194  func TestBasics(t *testing.T) {
   195  	SetConfigFile("/tmp/config.yaml")
   196  	assert.Equal(t, "/tmp/config.yaml", v.getConfigFile())
   197  }
   198  
   199  func TestDefault(t *testing.T) {
   200  	SetDefault("age", 45)
   201  	assert.Equal(t, 45, Get("age"))
   202  
   203  	SetDefault("clothing.jacket", "slacks")
   204  	assert.Equal(t, "slacks", Get("clothing.jacket"))
   205  
   206  	SetConfigType("yaml")
   207  	err := ReadConfig(bytes.NewBuffer(yamlExample))
   208  
   209  	assert.NoError(t, err)
   210  	assert.Equal(t, "leather", Get("clothing.jacket"))
   211  }
   212  
   213  func TestUnmarshalling(t *testing.T) {
   214  	SetConfigType("yaml")
   215  	r := bytes.NewReader(yamlExample)
   216  
   217  	unmarshalReader(r, v.config)
   218  	assert.True(t, InConfig("name"))
   219  	assert.False(t, InConfig("state"))
   220  	assert.Equal(t, "steve", Get("name"))
   221  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
   222  	assert.Equal(t, map[interface{}]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[interface{}]interface{}{"size": "large"}}, Get("clothing"))
   223  	assert.Equal(t, 35, Get("age"))
   224  }
   225  
   226  func TestOverrides(t *testing.T) {
   227  	Set("age", 40)
   228  	assert.Equal(t, 40, Get("age"))
   229  }
   230  
   231  func TestDefaultPost(t *testing.T) {
   232  	assert.NotEqual(t, "NYC", Get("state"))
   233  	SetDefault("state", "NYC")
   234  	assert.Equal(t, "NYC", Get("state"))
   235  }
   236  
   237  func TestAliases(t *testing.T) {
   238  	RegisterAlias("years", "age")
   239  	assert.Equal(t, 40, Get("years"))
   240  	Set("years", 45)
   241  	assert.Equal(t, 45, Get("age"))
   242  }
   243  
   244  func TestAliasInConfigFile(t *testing.T) {
   245  	// the config file specifies "beard".  If we make this an alias for
   246  	// "hasbeard", we still want the old config file to work with beard.
   247  	RegisterAlias("beard", "hasbeard")
   248  	assert.Equal(t, true, Get("hasbeard"))
   249  	Set("hasbeard", false)
   250  	assert.Equal(t, false, Get("beard"))
   251  }
   252  
   253  func TestYML(t *testing.T) {
   254  	initYAML()
   255  	assert.Equal(t, "steve", Get("name"))
   256  }
   257  
   258  func TestJSON(t *testing.T) {
   259  	initJSON()
   260  	assert.Equal(t, "0001", Get("id"))
   261  }
   262  
   263  func TestProperties(t *testing.T) {
   264  	initProperties()
   265  	assert.Equal(t, "0001", Get("p_id"))
   266  }
   267  
   268  func TestTOML(t *testing.T) {
   269  	initTOML()
   270  	assert.Equal(t, "TOML Example", Get("title"))
   271  }
   272  
   273  func TestRemotePrecedence(t *testing.T) {
   274  	initJSON()
   275  
   276  	remote := bytes.NewReader(remoteExample)
   277  	assert.Equal(t, "0001", Get("id"))
   278  	unmarshalReader(remote, v.kvstore)
   279  	assert.Equal(t, "0001", Get("id"))
   280  	assert.NotEqual(t, "cronut", Get("type"))
   281  	assert.Equal(t, "remote", Get("newkey"))
   282  	Set("newkey", "newvalue")
   283  	assert.NotEqual(t, "remote", Get("newkey"))
   284  	assert.Equal(t, "newvalue", Get("newkey"))
   285  	Set("newkey", "remote")
   286  }
   287  
   288  func TestEnv(t *testing.T) {
   289  	initJSON()
   290  
   291  	BindEnv("id")
   292  	BindEnv("f", "FOOD")
   293  
   294  	os.Setenv("ID", "13")
   295  	os.Setenv("FOOD", "apple")
   296  	os.Setenv("NAME", "crunk")
   297  
   298  	assert.Equal(t, "13", Get("id"))
   299  	assert.Equal(t, "apple", Get("f"))
   300  	assert.Equal(t, "Cake", Get("name"))
   301  
   302  	AutomaticEnv()
   303  
   304  	assert.Equal(t, "crunk", Get("name"))
   305  
   306  }
   307  
   308  func TestEnvPrefix(t *testing.T) {
   309  	initJSON()
   310  
   311  	SetEnvPrefix("foo") // will be uppercased automatically
   312  	BindEnv("id")
   313  	BindEnv("f", "FOOD") // not using prefix
   314  
   315  	os.Setenv("FOO_ID", "13")
   316  	os.Setenv("FOOD", "apple")
   317  	os.Setenv("FOO_NAME", "crunk")
   318  
   319  	assert.Equal(t, "13", Get("id"))
   320  	assert.Equal(t, "apple", Get("f"))
   321  	assert.Equal(t, "Cake", Get("name"))
   322  
   323  	AutomaticEnv()
   324  
   325  	assert.Equal(t, "crunk", Get("name"))
   326  }
   327  
   328  func TestAutoEnv(t *testing.T) {
   329  	Reset()
   330  
   331  	AutomaticEnv()
   332  	os.Setenv("FOO_BAR", "13")
   333  	assert.Equal(t, "13", Get("foo_bar"))
   334  }
   335  
   336  func TestAutoEnvWithPrefix(t *testing.T) {
   337  	Reset()
   338  
   339  	AutomaticEnv()
   340  	SetEnvPrefix("Baz")
   341  	os.Setenv("BAZ_BAR", "13")
   342  	assert.Equal(t, "13", Get("bar"))
   343  }
   344  
   345  func TestSetEnvReplacer(t *testing.T) {
   346  	Reset()
   347  
   348  	AutomaticEnv()
   349  	os.Setenv("REFRESH_INTERVAL", "30s")
   350  
   351  	replacer := strings.NewReplacer("-", "_")
   352  	SetEnvKeyReplacer(replacer)
   353  
   354  	assert.Equal(t, "30s", Get("refresh-interval"))
   355  }
   356  
   357  func TestAllKeys(t *testing.T) {
   358  	initConfigs()
   359  
   360  	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"}
   361  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
   362  	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"}
   363  
   364  	var allkeys sort.StringSlice
   365  	allkeys = AllKeys()
   366  	allkeys.Sort()
   367  	ks.Sort()
   368  
   369  	assert.Equal(t, ks, allkeys)
   370  	assert.Equal(t, all, AllSettings())
   371  }
   372  
   373  func TestCaseInSensitive(t *testing.T) {
   374  	assert.Equal(t, true, Get("hacker"))
   375  	Set("Title", "Checking Case")
   376  	assert.Equal(t, "Checking Case", Get("tItle"))
   377  }
   378  
   379  func TestAliasesOfAliases(t *testing.T) {
   380  	RegisterAlias("Foo", "Bar")
   381  	RegisterAlias("Bar", "Title")
   382  	assert.Equal(t, "Checking Case", Get("FOO"))
   383  }
   384  
   385  func TestRecursiveAliases(t *testing.T) {
   386  	RegisterAlias("Baz", "Roo")
   387  	RegisterAlias("Roo", "baz")
   388  }
   389  
   390  func TestUnmarshal(t *testing.T) {
   391  	SetDefault("port", 1313)
   392  	Set("name", "Steve")
   393  
   394  	type config struct {
   395  		Port int
   396  		Name string
   397  	}
   398  
   399  	var C config
   400  
   401  	err := Unmarshal(&C)
   402  	if err != nil {
   403  		t.Fatalf("unable to decode into struct, %v", err)
   404  	}
   405  
   406  	assert.Equal(t, &C, &config{Name: "Steve", Port: 1313})
   407  
   408  	Set("port", 1234)
   409  	err = Unmarshal(&C)
   410  	if err != nil {
   411  		t.Fatalf("unable to decode into struct, %v", err)
   412  	}
   413  	assert.Equal(t, &C, &config{Name: "Steve", Port: 1234})
   414  }
   415  
   416  func TestBindPFlags(t *testing.T) {
   417  	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
   418  
   419  	var testValues = map[string]*string{
   420  		"host":     nil,
   421  		"port":     nil,
   422  		"endpoint": nil,
   423  	}
   424  
   425  	var mutatedTestValues = map[string]string{
   426  		"host":     "localhost",
   427  		"port":     "6060",
   428  		"endpoint": "/public",
   429  	}
   430  
   431  	for name, _ := range testValues {
   432  		testValues[name] = flagSet.String(name, "", "test")
   433  	}
   434  
   435  	err := BindPFlags(flagSet)
   436  	if err != nil {
   437  		t.Fatalf("error binding flag set, %v", err)
   438  	}
   439  
   440  	flagSet.VisitAll(func(flag *pflag.Flag) {
   441  		flag.Value.Set(mutatedTestValues[flag.Name])
   442  		flag.Changed = true
   443  	})
   444  
   445  	for name, expected := range mutatedTestValues {
   446  		assert.Equal(t, Get(name), expected)
   447  	}
   448  
   449  }
   450  
   451  func TestBindPFlag(t *testing.T) {
   452  	var testString = "testing"
   453  	var testValue = newStringValue(testString, &testString)
   454  
   455  	flag := &pflag.Flag{
   456  		Name:    "testflag",
   457  		Value:   testValue,
   458  		Changed: false,
   459  	}
   460  
   461  	BindPFlag("testvalue", flag)
   462  
   463  	assert.Equal(t, testString, Get("testvalue"))
   464  
   465  	flag.Value.Set("testing_mutate")
   466  	flag.Changed = true //hack for pflag usage
   467  
   468  	assert.Equal(t, "testing_mutate", Get("testvalue"))
   469  
   470  }
   471  
   472  func TestBoundCaseSensitivity(t *testing.T) {
   473  
   474  	assert.Equal(t, "brown", Get("eyes"))
   475  
   476  	BindEnv("eYEs", "TURTLE_EYES")
   477  	os.Setenv("TURTLE_EYES", "blue")
   478  
   479  	assert.Equal(t, "blue", Get("eyes"))
   480  
   481  	var testString = "green"
   482  	var testValue = newStringValue(testString, &testString)
   483  
   484  	flag := &pflag.Flag{
   485  		Name:    "eyeballs",
   486  		Value:   testValue,
   487  		Changed: true,
   488  	}
   489  
   490  	BindPFlag("eYEs", flag)
   491  	assert.Equal(t, "green", Get("eyes"))
   492  
   493  }
   494  
   495  func TestSizeInBytes(t *testing.T) {
   496  	input := map[string]uint{
   497  		"":               0,
   498  		"b":              0,
   499  		"12 bytes":       0,
   500  		"200000000000gb": 0,
   501  		"12 b":           12,
   502  		"43 MB":          43 * (1 << 20),
   503  		"10mb":           10 * (1 << 20),
   504  		"1gb":            1 << 30,
   505  	}
   506  
   507  	for str, expected := range input {
   508  		assert.Equal(t, expected, parseSizeInBytes(str), str)
   509  	}
   510  }
   511  
   512  func TestFindsNestedKeys(t *testing.T) {
   513  	initConfigs()
   514  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
   515  
   516  	Set("super", map[string]interface{}{
   517  		"deep": map[string]interface{}{
   518  			"nested": "value",
   519  		},
   520  	})
   521  
   522  	expected := map[string]interface{}{
   523  		"super": map[string]interface{}{
   524  			"deep": map[string]interface{}{
   525  				"nested": "value",
   526  			},
   527  		},
   528  		"super.deep": map[string]interface{}{
   529  			"nested": "value",
   530  		},
   531  		"super.deep.nested":  "value",
   532  		"owner.organization": "MongoDB",
   533  		"batters.batter": []interface{}{
   534  			map[string]interface{}{
   535  				"type": "Regular",
   536  			},
   537  			map[string]interface{}{
   538  				"type": "Chocolate",
   539  			},
   540  			map[string]interface{}{
   541  				"type": "Blueberry",
   542  			},
   543  			map[string]interface{}{
   544  				"type": "Devil's Food",
   545  			},
   546  		},
   547  		"hobbies": []interface{}{
   548  			"skateboarding", "snowboarding", "go",
   549  		},
   550  		"title":  "TOML Example",
   551  		"newkey": "remote",
   552  		"batters": map[string]interface{}{
   553  			"batter": []interface{}{
   554  				map[string]interface{}{
   555  					"type": "Regular",
   556  				},
   557  				map[string]interface{}{
   558  					"type": "Chocolate",
   559  				}, map[string]interface{}{
   560  					"type": "Blueberry",
   561  				}, map[string]interface{}{
   562  					"type": "Devil's Food",
   563  				},
   564  			},
   565  		},
   566  		"eyes": "brown",
   567  		"age":  35,
   568  		"owner": map[string]interface{}{
   569  			"organization": "MongoDB",
   570  			"Bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
   571  			"dob":          dob,
   572  		},
   573  		"owner.Bio": "MongoDB Chief Developer Advocate & Hacker at Large",
   574  		"type":      "donut",
   575  		"id":        "0001",
   576  		"name":      "Cake",
   577  		"hacker":    true,
   578  		"ppu":       0.55,
   579  		"clothing": map[interface{}]interface{}{
   580  			"jacket":   "leather",
   581  			"trousers": "denim",
   582  			"pants": map[interface{}]interface{}{
   583  				"size": "large",
   584  			},
   585  		},
   586  		"clothing.jacket":     "leather",
   587  		"clothing.pants.size": "large",
   588  		"clothing.trousers":   "denim",
   589  		"owner.dob":           dob,
   590  		"beard":               true,
   591  	}
   592  
   593  	for key, expectedValue := range expected {
   594  
   595  		assert.Equal(t, expectedValue, v.Get(key))
   596  	}
   597  
   598  }
   599  
   600  func TestReadBufConfig(t *testing.T) {
   601  	v := New()
   602  	v.SetConfigType("yaml")
   603  	v.ReadConfig(bytes.NewBuffer(yamlExample))
   604  	t.Log(v.AllKeys())
   605  
   606  	assert.True(t, v.InConfig("name"))
   607  	assert.False(t, v.InConfig("state"))
   608  	assert.Equal(t, "steve", v.Get("name"))
   609  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
   610  	assert.Equal(t, map[interface{}]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[interface{}]interface{}{"size": "large"}}, v.Get("clothing"))
   611  	assert.Equal(t, 35, v.Get("age"))
   612  }
   613  
   614  func TestDirsSearch(t *testing.T) {
   615  
   616  	root, config, cleanup := initDirs(t)
   617  	defer cleanup()
   618  
   619  	v := New()
   620  	v.SetConfigName(config)
   621  	v.SetDefault(`key`, `default`)
   622  
   623  	entries, err := ioutil.ReadDir(root)
   624  	for _, e := range entries {
   625  		if e.IsDir() {
   626  			v.AddConfigPath(e.Name())
   627  		}
   628  	}
   629  
   630  	err = v.ReadInConfig()
   631  	assert.Nil(t, err)
   632  
   633  	assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`))
   634  }
   635  
   636  func TestWrongDirsSearchNotFound(t *testing.T) {
   637  
   638  	_, config, cleanup := initDirs(t)
   639  	defer cleanup()
   640  
   641  	v := New()
   642  	v.SetConfigName(config)
   643  	v.SetDefault(`key`, `default`)
   644  
   645  	v.AddConfigPath(`whattayoutalkingbout`)
   646  	v.AddConfigPath(`thispathaintthere`)
   647  
   648  	err := v.ReadInConfig()
   649  	assert.Equal(t, reflect.TypeOf(UnsupportedConfigError("")), reflect.TypeOf(err))
   650  
   651  	// Even though config did not load and the error might have
   652  	// been ignored by the client, the default still loads
   653  	assert.Equal(t, `default`, v.GetString(`key`))
   654  }