github.com/kmhagan/viper@v1.6.3-0.20200410222425-d68b332001c2/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  	"encoding/json"
    11  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path"
    16  	"path/filepath"
    17  	"reflect"
    18  	"runtime"
    19  	"sort"
    20  	"strings"
    21  	"sync"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/fsnotify/fsnotify"
    26  	"github.com/mitchellh/mapstructure"
    27  	"github.com/spf13/afero"
    28  	"github.com/spf13/cast"
    29  
    30  	"github.com/spf13/pflag"
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  var yamlExample = []byte(`Hacker: true
    36  name: steve
    37  hobbies:
    38  - skateboarding
    39  - snowboarding
    40  - go
    41  clothing:
    42    jacket: leather
    43    trousers: denim
    44    pants:
    45      size: large
    46  age: 35
    47  eyes : brown
    48  beard: true
    49  `)
    50  
    51  var yamlExampleWithExtras = []byte(`Existing: true
    52  Bogus: true
    53  `)
    54  
    55  type testUnmarshalExtra struct {
    56  	Existing bool
    57  }
    58  
    59  var tomlExample = []byte(`
    60  title = "TOML Example"
    61  
    62  [owner]
    63  organization = "MongoDB"
    64  Bio = "MongoDB Chief Developer Advocate & Hacker at Large"
    65  dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)
    66  
    67  var dotenvExample = []byte(`
    68  TITLE_DOTENV="DotEnv Example"
    69  TYPE_DOTENV=donut
    70  NAME_DOTENV=Cake`)
    71  
    72  var jsonExample = []byte(`{
    73  "id": "0001",
    74  "type": "donut",
    75  "name": "Cake",
    76  "ppu": 0.55,
    77  "batters": {
    78          "batter": [
    79                  { "type": "Regular" },
    80                  { "type": "Chocolate" },
    81                  { "type": "Blueberry" },
    82                  { "type": "Devil's Food" }
    83              ]
    84      }
    85  }`)
    86  
    87  var hclExample = []byte(`
    88  id = "0001"
    89  type = "donut"
    90  name = "Cake"
    91  ppu = 0.55
    92  foos {
    93  	foo {
    94  		key = 1
    95  	}
    96  	foo {
    97  		key = 2
    98  	}
    99  	foo {
   100  		key = 3
   101  	}
   102  	foo {
   103  		key = 4
   104  	}
   105  }`)
   106  
   107  var propertiesExample = []byte(`
   108  p_id: 0001
   109  p_type: donut
   110  p_name: Cake
   111  p_ppu: 0.55
   112  p_batters.batter.type: Regular
   113  `)
   114  
   115  var remoteExample = []byte(`{
   116  "id":"0002",
   117  "type":"cronut",
   118  "newkey":"remote"
   119  }`)
   120  
   121  var iniExample = []byte(`; Package name
   122  NAME        = ini
   123  ; Package version
   124  VERSION     = v1
   125  ; Package import path
   126  IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
   127  
   128  # Information about package author
   129  # Bio can be written in multiple lines.
   130  [author]
   131  NAME   = Unknown  ; Succeeding comment
   132  E-MAIL = fake@localhost
   133  GITHUB = https://github.com/%(NAME)s
   134  BIO    = """Gopher.
   135  Coding addict.
   136  Good man.
   137  """  # Succeeding comment`)
   138  
   139  func initConfigs() {
   140  	Reset()
   141  	var r io.Reader
   142  	SetConfigType("yaml")
   143  	r = bytes.NewReader(yamlExample)
   144  	unmarshalReader(r, v.config)
   145  
   146  	SetConfigType("json")
   147  	r = bytes.NewReader(jsonExample)
   148  	unmarshalReader(r, v.config)
   149  
   150  	SetConfigType("hcl")
   151  	r = bytes.NewReader(hclExample)
   152  	unmarshalReader(r, v.config)
   153  
   154  	SetConfigType("properties")
   155  	r = bytes.NewReader(propertiesExample)
   156  	unmarshalReader(r, v.config)
   157  
   158  	SetConfigType("toml")
   159  	r = bytes.NewReader(tomlExample)
   160  	unmarshalReader(r, v.config)
   161  
   162  	SetConfigType("env")
   163  	r = bytes.NewReader(dotenvExample)
   164  	unmarshalReader(r, v.config)
   165  
   166  	SetConfigType("json")
   167  	remote := bytes.NewReader(remoteExample)
   168  	unmarshalReader(remote, v.kvstore)
   169  
   170  	SetConfigType("ini")
   171  	r = bytes.NewReader(iniExample)
   172  	unmarshalReader(r, v.config)
   173  }
   174  
   175  func initConfig(typ, config string) {
   176  	Reset()
   177  	SetConfigType(typ)
   178  	r := strings.NewReader(config)
   179  
   180  	if err := unmarshalReader(r, v.config); err != nil {
   181  		panic(err)
   182  	}
   183  }
   184  
   185  func initYAML() {
   186  	initConfig("yaml", string(yamlExample))
   187  }
   188  
   189  func initJSON() {
   190  	Reset()
   191  	SetConfigType("json")
   192  	r := bytes.NewReader(jsonExample)
   193  
   194  	unmarshalReader(r, v.config)
   195  }
   196  
   197  func initProperties() {
   198  	Reset()
   199  	SetConfigType("properties")
   200  	r := bytes.NewReader(propertiesExample)
   201  
   202  	unmarshalReader(r, v.config)
   203  }
   204  
   205  func initTOML() {
   206  	Reset()
   207  	SetConfigType("toml")
   208  	r := bytes.NewReader(tomlExample)
   209  
   210  	unmarshalReader(r, v.config)
   211  }
   212  
   213  func initDotEnv() {
   214  	Reset()
   215  	SetConfigType("env")
   216  	r := bytes.NewReader(dotenvExample)
   217  
   218  	unmarshalReader(r, v.config)
   219  }
   220  
   221  func initHcl() {
   222  	Reset()
   223  	SetConfigType("hcl")
   224  	r := bytes.NewReader(hclExample)
   225  
   226  	unmarshalReader(r, v.config)
   227  }
   228  
   229  func initIni() {
   230  	Reset()
   231  	SetConfigType("ini")
   232  	r := bytes.NewReader(iniExample)
   233  
   234  	unmarshalReader(r, v.config)
   235  }
   236  
   237  // make directories for testing
   238  func initDirs(t *testing.T) (string, string, func()) {
   239  	var (
   240  		testDirs = []string{`a a`, `b`, `C_`}
   241  		config   = `improbable`
   242  	)
   243  
   244  	if runtime.GOOS != "windows" {
   245  		testDirs = append(testDirs, `d\d`)
   246  	}
   247  
   248  	root, err := ioutil.TempDir("", "")
   249  	require.NoError(t, err, "Failed to create temporary directory")
   250  
   251  	cleanup := true
   252  	defer func() {
   253  		if cleanup {
   254  			os.Chdir("..")
   255  			os.RemoveAll(root)
   256  		}
   257  	}()
   258  
   259  	assert.Nil(t, err)
   260  
   261  	err = os.Chdir(root)
   262  	require.Nil(t, err)
   263  
   264  	for _, dir := range testDirs {
   265  		err = os.Mkdir(dir, 0750)
   266  		assert.Nil(t, err)
   267  
   268  		err = ioutil.WriteFile(
   269  			path.Join(dir, config+".toml"),
   270  			[]byte("key = \"value is "+dir+"\"\n"),
   271  			0640)
   272  		assert.Nil(t, err)
   273  	}
   274  
   275  	cleanup = false
   276  	return root, config, func() {
   277  		os.Chdir("..")
   278  		os.RemoveAll(root)
   279  	}
   280  }
   281  
   282  // stubs for PFlag Values
   283  type stringValue string
   284  
   285  func newStringValue(val string, p *string) *stringValue {
   286  	*p = val
   287  	return (*stringValue)(p)
   288  }
   289  
   290  func (s *stringValue) Set(val string) error {
   291  	*s = stringValue(val)
   292  	return nil
   293  }
   294  
   295  func (s *stringValue) Type() string {
   296  	return "string"
   297  }
   298  
   299  func (s *stringValue) String() string {
   300  	return string(*s)
   301  }
   302  
   303  func TestBasics(t *testing.T) {
   304  	SetConfigFile("/tmp/config.yaml")
   305  	filename, err := v.getConfigFile()
   306  	assert.Equal(t, "/tmp/config.yaml", filename)
   307  	assert.NoError(t, err)
   308  }
   309  
   310  func TestSearchInPath_WithoutConfigTypeSet(t *testing.T) {
   311  	filename := ".dotfilenoext"
   312  	path := "/tmp"
   313  	file := filepath.Join(path, filename)
   314  	SetConfigName(filename)
   315  	AddConfigPath(path)
   316  	_, createErr := v.fs.Create(file)
   317  	defer func() {
   318  		_ = v.fs.Remove(file)
   319  	}()
   320  	assert.NoError(t, createErr)
   321  	_, err := v.getConfigFile()
   322  	// unless config type is set, files without extension
   323  	// are not considered
   324  	assert.Error(t, err)
   325  }
   326  
   327  func TestSearchInPath(t *testing.T) {
   328  	filename := ".dotfilenoext"
   329  	path := "/tmp"
   330  	file := filepath.Join(path, filename)
   331  	SetConfigName(filename)
   332  	SetConfigType("yaml")
   333  	AddConfigPath(path)
   334  	_, createErr := v.fs.Create(file)
   335  	defer func() {
   336  		_ = v.fs.Remove(file)
   337  	}()
   338  	assert.NoError(t, createErr)
   339  	filename, err := v.getConfigFile()
   340  	assert.Equal(t, file, filename)
   341  	assert.NoError(t, err)
   342  }
   343  
   344  func TestSearchInPath_FilesOnly(t *testing.T) {
   345  	fs := afero.NewMemMapFs()
   346  
   347  	err := fs.Mkdir("/tmp/config", 0777)
   348  	require.NoError(t, err)
   349  
   350  	_, err = fs.Create("/tmp/config/config.yaml")
   351  	require.NoError(t, err)
   352  
   353  	v := New()
   354  
   355  	v.SetFs(fs)
   356  	v.AddConfigPath("/tmp")
   357  	v.AddConfigPath("/tmp/config")
   358  
   359  	filename, err := v.getConfigFile()
   360  	assert.Equal(t, "/tmp/config/config.yaml", filename)
   361  	assert.NoError(t, err)
   362  }
   363  
   364  func TestDefault(t *testing.T) {
   365  	SetDefault("age", 45)
   366  	assert.Equal(t, 45, Get("age"))
   367  
   368  	SetDefault("clothing.jacket", "slacks")
   369  	assert.Equal(t, "slacks", Get("clothing.jacket"))
   370  
   371  	SetConfigType("yaml")
   372  	err := ReadConfig(bytes.NewBuffer(yamlExample))
   373  
   374  	assert.NoError(t, err)
   375  	assert.Equal(t, "leather", Get("clothing.jacket"))
   376  }
   377  
   378  func TestUnmarshaling(t *testing.T) {
   379  	SetConfigType("yaml")
   380  	r := bytes.NewReader(yamlExample)
   381  
   382  	unmarshalReader(r, v.config)
   383  	assert.True(t, InConfig("name"))
   384  	assert.False(t, InConfig("state"))
   385  	assert.Equal(t, "steve", Get("name"))
   386  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
   387  	assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
   388  	assert.Equal(t, 35, Get("age"))
   389  }
   390  
   391  func TestUnmarshalExact(t *testing.T) {
   392  	vip := New()
   393  	target := &testUnmarshalExtra{}
   394  	vip.SetConfigType("yaml")
   395  	r := bytes.NewReader(yamlExampleWithExtras)
   396  	vip.ReadConfig(r)
   397  	err := vip.UnmarshalExact(target)
   398  	if err == nil {
   399  		t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields")
   400  	}
   401  }
   402  
   403  func TestOverrides(t *testing.T) {
   404  	Set("age", 40)
   405  	assert.Equal(t, 40, Get("age"))
   406  }
   407  
   408  func TestDefaultPost(t *testing.T) {
   409  	assert.NotEqual(t, "NYC", Get("state"))
   410  	SetDefault("state", "NYC")
   411  	assert.Equal(t, "NYC", Get("state"))
   412  }
   413  
   414  func TestAliases(t *testing.T) {
   415  	RegisterAlias("years", "age")
   416  	assert.Equal(t, 40, Get("years"))
   417  	Set("years", 45)
   418  	assert.Equal(t, 45, Get("age"))
   419  }
   420  
   421  func TestAliasInConfigFile(t *testing.T) {
   422  	// the config file specifies "beard".  If we make this an alias for
   423  	// "hasbeard", we still want the old config file to work with beard.
   424  	RegisterAlias("beard", "hasbeard")
   425  	assert.Equal(t, true, Get("hasbeard"))
   426  	Set("hasbeard", false)
   427  	assert.Equal(t, false, Get("beard"))
   428  }
   429  
   430  func TestYML(t *testing.T) {
   431  	initYAML()
   432  	assert.Equal(t, "steve", Get("name"))
   433  }
   434  
   435  func TestJSON(t *testing.T) {
   436  	initJSON()
   437  	assert.Equal(t, "0001", Get("id"))
   438  }
   439  
   440  func TestProperties(t *testing.T) {
   441  	initProperties()
   442  	assert.Equal(t, "0001", Get("p_id"))
   443  }
   444  
   445  func TestTOML(t *testing.T) {
   446  	initTOML()
   447  	assert.Equal(t, "TOML Example", Get("title"))
   448  }
   449  
   450  func TestDotEnv(t *testing.T) {
   451  	initDotEnv()
   452  	assert.Equal(t, "DotEnv Example", Get("title_dotenv"))
   453  }
   454  
   455  func TestHCL(t *testing.T) {
   456  	initHcl()
   457  	assert.Equal(t, "0001", Get("id"))
   458  	assert.Equal(t, 0.55, Get("ppu"))
   459  	assert.Equal(t, "donut", Get("type"))
   460  	assert.Equal(t, "Cake", Get("name"))
   461  	Set("id", "0002")
   462  	assert.Equal(t, "0002", Get("id"))
   463  	assert.NotEqual(t, "cronut", Get("type"))
   464  }
   465  
   466  func TestIni(t *testing.T) {
   467  	initIni()
   468  	assert.Equal(t, "ini", Get("default.name"))
   469  }
   470  
   471  func TestRemotePrecedence(t *testing.T) {
   472  	initJSON()
   473  
   474  	remote := bytes.NewReader(remoteExample)
   475  	assert.Equal(t, "0001", Get("id"))
   476  	unmarshalReader(remote, v.kvstore)
   477  	assert.Equal(t, "0001", Get("id"))
   478  	assert.NotEqual(t, "cronut", Get("type"))
   479  	assert.Equal(t, "remote", Get("newkey"))
   480  	Set("newkey", "newvalue")
   481  	assert.NotEqual(t, "remote", Get("newkey"))
   482  	assert.Equal(t, "newvalue", Get("newkey"))
   483  	Set("newkey", "remote")
   484  }
   485  
   486  func TestEnv(t *testing.T) {
   487  	initJSON()
   488  
   489  	BindEnv("id")
   490  	BindEnv("f", "FOOD")
   491  
   492  	os.Setenv("ID", "13")
   493  	os.Setenv("FOOD", "apple")
   494  	os.Setenv("NAME", "crunk")
   495  
   496  	assert.Equal(t, "13", Get("id"))
   497  	assert.Equal(t, "apple", Get("f"))
   498  	assert.Equal(t, "Cake", Get("name"))
   499  
   500  	AutomaticEnv()
   501  
   502  	assert.Equal(t, "crunk", Get("name"))
   503  }
   504  
   505  func TestEmptyEnv(t *testing.T) {
   506  	initJSON()
   507  
   508  	BindEnv("type") // Empty environment variable
   509  	BindEnv("name") // Bound, but not set environment variable
   510  
   511  	os.Unsetenv("type")
   512  	os.Unsetenv("TYPE")
   513  	os.Unsetenv("name")
   514  	os.Unsetenv("NAME")
   515  
   516  	os.Setenv("TYPE", "")
   517  
   518  	assert.Equal(t, "donut", Get("type"))
   519  	assert.Equal(t, "Cake", Get("name"))
   520  }
   521  
   522  func TestEmptyEnv_Allowed(t *testing.T) {
   523  	initJSON()
   524  
   525  	AllowEmptyEnv(true)
   526  
   527  	BindEnv("type") // Empty environment variable
   528  	BindEnv("name") // Bound, but not set environment variable
   529  
   530  	os.Unsetenv("type")
   531  	os.Unsetenv("TYPE")
   532  	os.Unsetenv("name")
   533  	os.Unsetenv("NAME")
   534  
   535  	os.Setenv("TYPE", "")
   536  
   537  	assert.Equal(t, "", Get("type"))
   538  	assert.Equal(t, "Cake", Get("name"))
   539  }
   540  
   541  func TestEnvPrefix(t *testing.T) {
   542  	initJSON()
   543  
   544  	SetEnvPrefix("foo") // will be uppercased automatically
   545  	BindEnv("id")
   546  	BindEnv("f", "FOOD") // not using prefix
   547  
   548  	os.Setenv("FOO_ID", "13")
   549  	os.Setenv("FOOD", "apple")
   550  	os.Setenv("FOO_NAME", "crunk")
   551  
   552  	assert.Equal(t, "13", Get("id"))
   553  	assert.Equal(t, "apple", Get("f"))
   554  	assert.Equal(t, "Cake", Get("name"))
   555  
   556  	AutomaticEnv()
   557  
   558  	assert.Equal(t, "crunk", Get("name"))
   559  }
   560  
   561  func TestAutoEnv(t *testing.T) {
   562  	Reset()
   563  
   564  	AutomaticEnv()
   565  	os.Setenv("FOO_BAR", "13")
   566  	assert.Equal(t, "13", Get("foo_bar"))
   567  }
   568  
   569  func TestAutoEnvWithPrefix(t *testing.T) {
   570  	Reset()
   571  
   572  	AutomaticEnv()
   573  	SetEnvPrefix("Baz")
   574  	os.Setenv("BAZ_BAR", "13")
   575  	assert.Equal(t, "13", Get("bar"))
   576  }
   577  
   578  func TestSetEnvKeyReplacer(t *testing.T) {
   579  	Reset()
   580  
   581  	AutomaticEnv()
   582  	os.Setenv("REFRESH_INTERVAL", "30s")
   583  
   584  	replacer := strings.NewReplacer("-", "_")
   585  	SetEnvKeyReplacer(replacer)
   586  
   587  	assert.Equal(t, "30s", Get("refresh-interval"))
   588  }
   589  
   590  func TestEnvKeyReplacer(t *testing.T) {
   591  	v := NewWithOptions(EnvKeyReplacer(strings.NewReplacer("-", "_")))
   592  
   593  	v.AutomaticEnv()
   594  	_ = os.Setenv("REFRESH_INTERVAL", "30s")
   595  
   596  	assert.Equal(t, "30s", v.Get("refresh-interval"))
   597  }
   598  
   599  func TestAllKeys(t *testing.T) {
   600  	initConfigs()
   601  
   602  	ks := sort.StringSlice{
   603  		"title",
   604  		"author.bio",
   605  		"author.e-mail",
   606  		"author.github",
   607  		"author.name",
   608  		"newkey",
   609  		"owner.organization",
   610  		"owner.dob",
   611  		"owner.bio",
   612  		"name",
   613  		"beard",
   614  		"ppu",
   615  		"batters.batter",
   616  		"hobbies",
   617  		"clothing.jacket",
   618  		"clothing.trousers",
   619  		"default.import_path",
   620  		"default.name",
   621  		"default.version",
   622  		"clothing.pants.size",
   623  		"age",
   624  		"hacker",
   625  		"id",
   626  		"type",
   627  		"eyes",
   628  		"p_id",
   629  		"p_ppu",
   630  		"p_batters.batter.type",
   631  		"p_type",
   632  		"p_name",
   633  		"foos",
   634  		"title_dotenv",
   635  		"type_dotenv",
   636  		"name_dotenv",
   637  	}
   638  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
   639  	all := map[string]interface{}{
   640  		"owner": map[string]interface{}{
   641  			"organization": "MongoDB",
   642  			"bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
   643  			"dob":          dob,
   644  		},
   645  		"title": "TOML Example",
   646  		"author": map[string]interface{}{
   647  			"e-mail": "fake@localhost",
   648  			"github": "https://github.com/Unknown",
   649  			"name":   "Unknown",
   650  			"bio":    "Gopher.\nCoding addict.\nGood man.\n",
   651  		},
   652  		"ppu":  0.55,
   653  		"eyes": "brown",
   654  		"clothing": map[string]interface{}{
   655  			"trousers": "denim",
   656  			"jacket":   "leather",
   657  			"pants":    map[string]interface{}{"size": "large"},
   658  		},
   659  		"default": map[string]interface{}{
   660  			"import_path": "gopkg.in/ini.v1",
   661  			"name":        "ini",
   662  			"version":     "v1",
   663  		},
   664  		"id": "0001",
   665  		"batters": map[string]interface{}{
   666  			"batter": []interface{}{
   667  				map[string]interface{}{"type": "Regular"},
   668  				map[string]interface{}{"type": "Chocolate"},
   669  				map[string]interface{}{"type": "Blueberry"},
   670  				map[string]interface{}{"type": "Devil's Food"},
   671  			},
   672  		},
   673  		"hacker": true,
   674  		"beard":  true,
   675  		"hobbies": []interface{}{
   676  			"skateboarding",
   677  			"snowboarding",
   678  			"go",
   679  		},
   680  		"age":    35,
   681  		"type":   "donut",
   682  		"newkey": "remote",
   683  		"name":   "Cake",
   684  		"p_id":   "0001",
   685  		"p_ppu":  "0.55",
   686  		"p_name": "Cake",
   687  		"p_batters": map[string]interface{}{
   688  			"batter": map[string]interface{}{"type": "Regular"},
   689  		},
   690  		"p_type": "donut",
   691  		"foos": []map[string]interface{}{
   692  			{
   693  				"foo": []map[string]interface{}{
   694  					{"key": 1},
   695  					{"key": 2},
   696  					{"key": 3},
   697  					{"key": 4}},
   698  			},
   699  		},
   700  		"title_dotenv": "DotEnv Example",
   701  		"type_dotenv":  "donut",
   702  		"name_dotenv":  "Cake",
   703  	}
   704  
   705  	allkeys := sort.StringSlice(AllKeys())
   706  	allkeys.Sort()
   707  	ks.Sort()
   708  
   709  	assert.Equal(t, ks, allkeys)
   710  	assert.Equal(t, all, AllSettings())
   711  }
   712  
   713  func TestAllKeysWithEnv(t *testing.T) {
   714  	v := New()
   715  
   716  	// bind and define environment variables (including a nested one)
   717  	v.BindEnv("id")
   718  	v.BindEnv("foo.bar")
   719  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
   720  	os.Setenv("ID", "13")
   721  	os.Setenv("FOO_BAR", "baz")
   722  
   723  	expectedKeys := sort.StringSlice{"id", "foo.bar"}
   724  	expectedKeys.Sort()
   725  	keys := sort.StringSlice(v.AllKeys())
   726  	keys.Sort()
   727  	assert.Equal(t, expectedKeys, keys)
   728  }
   729  
   730  func TestAliasesOfAliases(t *testing.T) {
   731  	Set("Title", "Checking Case")
   732  	RegisterAlias("Foo", "Bar")
   733  	RegisterAlias("Bar", "Title")
   734  	assert.Equal(t, "Checking Case", Get("FOO"))
   735  }
   736  
   737  func TestRecursiveAliases(t *testing.T) {
   738  	RegisterAlias("Baz", "Roo")
   739  	RegisterAlias("Roo", "baz")
   740  }
   741  
   742  func TestUnmarshal(t *testing.T) {
   743  	SetDefault("port", 1313)
   744  	Set("name", "Steve")
   745  	Set("duration", "1s1ms")
   746  	Set("modes", []int{1, 2, 3})
   747  
   748  	type config struct {
   749  		Port     int
   750  		Name     string
   751  		Duration time.Duration
   752  		Modes    []int
   753  	}
   754  
   755  	var C config
   756  
   757  	err := Unmarshal(&C)
   758  	if err != nil {
   759  		t.Fatalf("unable to decode into struct, %v", err)
   760  	}
   761  
   762  	assert.Equal(
   763  		t,
   764  		&config{
   765  			Name:     "Steve",
   766  			Port:     1313,
   767  			Duration: time.Second + time.Millisecond,
   768  			Modes:    []int{1, 2, 3},
   769  		},
   770  		&C,
   771  	)
   772  
   773  	Set("port", 1234)
   774  	err = Unmarshal(&C)
   775  	if err != nil {
   776  		t.Fatalf("unable to decode into struct, %v", err)
   777  	}
   778  
   779  	assert.Equal(
   780  		t,
   781  		&config{
   782  			Name:     "Steve",
   783  			Port:     1234,
   784  			Duration: time.Second + time.Millisecond,
   785  			Modes:    []int{1, 2, 3},
   786  		},
   787  		&C,
   788  	)
   789  }
   790  
   791  func TestUnmarshalWithDecoderOptions(t *testing.T) {
   792  	Set("credentials", "{\"foo\":\"bar\"}")
   793  
   794  	opt := DecodeHook(mapstructure.ComposeDecodeHookFunc(
   795  		mapstructure.StringToTimeDurationHookFunc(),
   796  		mapstructure.StringToSliceHookFunc(","),
   797  		// Custom Decode Hook Function
   798  		func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) {
   799  			if rf != reflect.String || rt != reflect.Map {
   800  				return data, nil
   801  			}
   802  			m := map[string]string{}
   803  			raw := data.(string)
   804  			if raw == "" {
   805  				return m, nil
   806  			}
   807  			return m, json.Unmarshal([]byte(raw), &m)
   808  		},
   809  	))
   810  
   811  	type config struct {
   812  		Credentials map[string]string
   813  	}
   814  
   815  	var C config
   816  
   817  	err := Unmarshal(&C, opt)
   818  	if err != nil {
   819  		t.Fatalf("unable to decode into struct, %v", err)
   820  	}
   821  
   822  	assert.Equal(t, &config{
   823  		Credentials: map[string]string{"foo": "bar"},
   824  	}, &C)
   825  }
   826  
   827  func TestBindPFlags(t *testing.T) {
   828  	v := New() // create independent Viper object
   829  	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
   830  
   831  	var testValues = map[string]*string{
   832  		"host":     nil,
   833  		"port":     nil,
   834  		"endpoint": nil,
   835  	}
   836  
   837  	var mutatedTestValues = map[string]string{
   838  		"host":     "localhost",
   839  		"port":     "6060",
   840  		"endpoint": "/public",
   841  	}
   842  
   843  	for name := range testValues {
   844  		testValues[name] = flagSet.String(name, "", "test")
   845  	}
   846  
   847  	err := v.BindPFlags(flagSet)
   848  	if err != nil {
   849  		t.Fatalf("error binding flag set, %v", err)
   850  	}
   851  
   852  	flagSet.VisitAll(func(flag *pflag.Flag) {
   853  		flag.Value.Set(mutatedTestValues[flag.Name])
   854  		flag.Changed = true
   855  	})
   856  
   857  	for name, expected := range mutatedTestValues {
   858  		assert.Equal(t, expected, v.Get(name))
   859  	}
   860  }
   861  
   862  func TestBindPFlagsStringSlice(t *testing.T) {
   863  	tests := []struct {
   864  		Expected []string
   865  		Value    string
   866  	}{
   867  		{nil, ""},
   868  		{[]string{"jeden"}, "jeden"},
   869  		{[]string{"dwa", "trzy"}, "dwa,trzy"},
   870  		{[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""},
   871  	}
   872  
   873  	v := New() // create independent Viper object
   874  	defaultVal := []string{"default"}
   875  	v.SetDefault("stringslice", defaultVal)
   876  
   877  	for _, testValue := range tests {
   878  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
   879  		flagSet.StringSlice("stringslice", testValue.Expected, "test")
   880  
   881  		for _, changed := range []bool{true, false} {
   882  			flagSet.VisitAll(func(f *pflag.Flag) {
   883  				f.Value.Set(testValue.Value)
   884  				f.Changed = changed
   885  			})
   886  
   887  			err := v.BindPFlags(flagSet)
   888  			if err != nil {
   889  				t.Fatalf("error binding flag set, %v", err)
   890  			}
   891  
   892  			type TestStr struct {
   893  				StringSlice []string
   894  			}
   895  			val := &TestStr{}
   896  			if err := v.Unmarshal(val); err != nil {
   897  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
   898  			}
   899  			if changed {
   900  				assert.Equal(t, testValue.Expected, val.StringSlice)
   901  			} else {
   902  				assert.Equal(t, defaultVal, val.StringSlice)
   903  			}
   904  		}
   905  	}
   906  }
   907  
   908  func TestBindPFlagsIntSlice(t *testing.T) {
   909  	tests := []struct {
   910  		Expected []int
   911  		Value    string
   912  	}{
   913  		{nil, ""},
   914  		{[]int{1}, "1"},
   915  		{[]int{2, 3}, "2,3"},
   916  	}
   917  
   918  	v := New() // create independent Viper object
   919  	defaultVal := []int{0}
   920  	v.SetDefault("intslice", defaultVal)
   921  
   922  	for _, testValue := range tests {
   923  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
   924  		flagSet.IntSlice("intslice", testValue.Expected, "test")
   925  
   926  		for _, changed := range []bool{true, false} {
   927  			flagSet.VisitAll(func(f *pflag.Flag) {
   928  				f.Value.Set(testValue.Value)
   929  				f.Changed = changed
   930  			})
   931  
   932  			err := v.BindPFlags(flagSet)
   933  			if err != nil {
   934  				t.Fatalf("error binding flag set, %v", err)
   935  			}
   936  
   937  			type TestInt struct {
   938  				IntSlice []int
   939  			}
   940  			val := &TestInt{}
   941  			if err := v.Unmarshal(val); err != nil {
   942  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
   943  			}
   944  			if changed {
   945  				assert.Equal(t, testValue.Expected, val.IntSlice)
   946  			} else {
   947  				assert.Equal(t, defaultVal, val.IntSlice)
   948  			}
   949  		}
   950  	}
   951  }
   952  
   953  func TestBindPFlag(t *testing.T) {
   954  	var testString = "testing"
   955  	var testValue = newStringValue(testString, &testString)
   956  
   957  	flag := &pflag.Flag{
   958  		Name:    "testflag",
   959  		Value:   testValue,
   960  		Changed: false,
   961  	}
   962  
   963  	BindPFlag("testvalue", flag)
   964  
   965  	assert.Equal(t, testString, Get("testvalue"))
   966  
   967  	flag.Value.Set("testing_mutate")
   968  	flag.Changed = true // hack for pflag usage
   969  
   970  	assert.Equal(t, "testing_mutate", Get("testvalue"))
   971  }
   972  
   973  func TestBoundCaseSensitivity(t *testing.T) {
   974  	assert.Equal(t, "brown", Get("eyes"))
   975  
   976  	BindEnv("eYEs", "TURTLE_EYES")
   977  	os.Setenv("TURTLE_EYES", "blue")
   978  
   979  	assert.Equal(t, "blue", Get("eyes"))
   980  
   981  	var testString = "green"
   982  	var testValue = newStringValue(testString, &testString)
   983  
   984  	flag := &pflag.Flag{
   985  		Name:    "eyeballs",
   986  		Value:   testValue,
   987  		Changed: true,
   988  	}
   989  
   990  	BindPFlag("eYEs", flag)
   991  	assert.Equal(t, "green", Get("eyes"))
   992  }
   993  
   994  func TestSizeInBytes(t *testing.T) {
   995  	input := map[string]uint{
   996  		"":               0,
   997  		"b":              0,
   998  		"12 bytes":       0,
   999  		"200000000000gb": 0,
  1000  		"12 b":           12,
  1001  		"43 MB":          43 * (1 << 20),
  1002  		"10mb":           10 * (1 << 20),
  1003  		"1gb":            1 << 30,
  1004  	}
  1005  
  1006  	for str, expected := range input {
  1007  		assert.Equal(t, expected, parseSizeInBytes(str), str)
  1008  	}
  1009  }
  1010  
  1011  func TestFindsNestedKeys(t *testing.T) {
  1012  	initConfigs()
  1013  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
  1014  
  1015  	Set("super", map[string]interface{}{
  1016  		"deep": map[string]interface{}{
  1017  			"nested": "value",
  1018  		},
  1019  	})
  1020  
  1021  	expected := map[string]interface{}{
  1022  		"super": map[string]interface{}{
  1023  			"deep": map[string]interface{}{
  1024  				"nested": "value",
  1025  			},
  1026  		},
  1027  		"super.deep": map[string]interface{}{
  1028  			"nested": "value",
  1029  		},
  1030  		"super.deep.nested":  "value",
  1031  		"owner.organization": "MongoDB",
  1032  		"batters.batter": []interface{}{
  1033  			map[string]interface{}{
  1034  				"type": "Regular",
  1035  			},
  1036  			map[string]interface{}{
  1037  				"type": "Chocolate",
  1038  			},
  1039  			map[string]interface{}{
  1040  				"type": "Blueberry",
  1041  			},
  1042  			map[string]interface{}{
  1043  				"type": "Devil's Food",
  1044  			},
  1045  		},
  1046  		"hobbies": []interface{}{
  1047  			"skateboarding", "snowboarding", "go",
  1048  		},
  1049  		"TITLE_DOTENV": "DotEnv Example",
  1050  		"TYPE_DOTENV":  "donut",
  1051  		"NAME_DOTENV":  "Cake",
  1052  		"title":        "TOML Example",
  1053  		"newkey":       "remote",
  1054  		"batters": map[string]interface{}{
  1055  			"batter": []interface{}{
  1056  				map[string]interface{}{
  1057  					"type": "Regular",
  1058  				},
  1059  				map[string]interface{}{
  1060  					"type": "Chocolate",
  1061  				}, map[string]interface{}{
  1062  					"type": "Blueberry",
  1063  				}, map[string]interface{}{
  1064  					"type": "Devil's Food",
  1065  				},
  1066  			},
  1067  		},
  1068  		"eyes": "brown",
  1069  		"age":  35,
  1070  		"owner": map[string]interface{}{
  1071  			"organization": "MongoDB",
  1072  			"bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
  1073  			"dob":          dob,
  1074  		},
  1075  		"owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large",
  1076  		"type":      "donut",
  1077  		"id":        "0001",
  1078  		"name":      "Cake",
  1079  		"hacker":    true,
  1080  		"ppu":       0.55,
  1081  		"clothing": map[string]interface{}{
  1082  			"jacket":   "leather",
  1083  			"trousers": "denim",
  1084  			"pants": map[string]interface{}{
  1085  				"size": "large",
  1086  			},
  1087  		},
  1088  		"clothing.jacket":     "leather",
  1089  		"clothing.pants.size": "large",
  1090  		"clothing.trousers":   "denim",
  1091  		"owner.dob":           dob,
  1092  		"beard":               true,
  1093  		"foos": []map[string]interface{}{
  1094  			{
  1095  				"foo": []map[string]interface{}{
  1096  					{
  1097  						"key": 1,
  1098  					},
  1099  					{
  1100  						"key": 2,
  1101  					},
  1102  					{
  1103  						"key": 3,
  1104  					},
  1105  					{
  1106  						"key": 4,
  1107  					},
  1108  				},
  1109  			},
  1110  		},
  1111  	}
  1112  
  1113  	for key, expectedValue := range expected {
  1114  		assert.Equal(t, expectedValue, v.Get(key))
  1115  	}
  1116  }
  1117  
  1118  func TestReadBufConfig(t *testing.T) {
  1119  	v := New()
  1120  	v.SetConfigType("yaml")
  1121  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1122  	t.Log(v.AllKeys())
  1123  
  1124  	assert.True(t, v.InConfig("name"))
  1125  	assert.False(t, v.InConfig("state"))
  1126  	assert.Equal(t, "steve", v.Get("name"))
  1127  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
  1128  	assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
  1129  	assert.Equal(t, 35, v.Get("age"))
  1130  }
  1131  
  1132  func TestIsSet(t *testing.T) {
  1133  	v := New()
  1134  	v.SetConfigType("yaml")
  1135  
  1136  	/* config and defaults */
  1137  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1138  	v.SetDefault("clothing.shoes", "sneakers")
  1139  
  1140  	assert.True(t, v.IsSet("clothing"))
  1141  	assert.True(t, v.IsSet("clothing.jacket"))
  1142  	assert.False(t, v.IsSet("clothing.jackets"))
  1143  	assert.True(t, v.IsSet("clothing.shoes"))
  1144  
  1145  	/* state change */
  1146  	assert.False(t, v.IsSet("helloworld"))
  1147  	v.Set("helloworld", "fubar")
  1148  	assert.True(t, v.IsSet("helloworld"))
  1149  
  1150  	/* env */
  1151  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
  1152  	v.BindEnv("eyes")
  1153  	v.BindEnv("foo")
  1154  	v.BindEnv("clothing.hat")
  1155  	v.BindEnv("clothing.hats")
  1156  	os.Setenv("FOO", "bar")
  1157  	os.Setenv("CLOTHING_HAT", "bowler")
  1158  
  1159  	assert.True(t, v.IsSet("eyes"))           // in the config file
  1160  	assert.True(t, v.IsSet("foo"))            // in the environment
  1161  	assert.True(t, v.IsSet("clothing.hat"))   // in the environment
  1162  	assert.False(t, v.IsSet("clothing.hats")) // not defined
  1163  
  1164  	/* flags */
  1165  	flagset := pflag.NewFlagSet("testisset", pflag.ContinueOnError)
  1166  	flagset.Bool("foobaz", false, "foobaz")
  1167  	flagset.Bool("barbaz", false, "barbaz")
  1168  	foobaz, barbaz := flagset.Lookup("foobaz"), flagset.Lookup("barbaz")
  1169  	v.BindPFlag("foobaz", foobaz)
  1170  	v.BindPFlag("barbaz", barbaz)
  1171  	barbaz.Value.Set("true")
  1172  	barbaz.Changed = true // hack for pflag usage
  1173  
  1174  	assert.False(t, v.IsSet("foobaz"))
  1175  	assert.True(t, v.IsSet("barbaz"))
  1176  }
  1177  
  1178  func TestDirsSearch(t *testing.T) {
  1179  	root, config, cleanup := initDirs(t)
  1180  	defer cleanup()
  1181  
  1182  	v := New()
  1183  	v.SetConfigName(config)
  1184  	v.SetDefault(`key`, `default`)
  1185  
  1186  	entries, err := ioutil.ReadDir(root)
  1187  	assert.Nil(t, err)
  1188  	for _, e := range entries {
  1189  		if e.IsDir() {
  1190  			v.AddConfigPath(e.Name())
  1191  		}
  1192  	}
  1193  
  1194  	err = v.ReadInConfig()
  1195  	assert.Nil(t, err)
  1196  
  1197  	assert.Equal(t, `value is `+filepath.Base(v.configPaths[0]), v.GetString(`key`))
  1198  }
  1199  
  1200  func TestWrongDirsSearchNotFound(t *testing.T) {
  1201  	_, config, cleanup := initDirs(t)
  1202  	defer cleanup()
  1203  
  1204  	v := New()
  1205  	v.SetConfigName(config)
  1206  	v.SetDefault(`key`, `default`)
  1207  
  1208  	v.AddConfigPath(`whattayoutalkingbout`)
  1209  	v.AddConfigPath(`thispathaintthere`)
  1210  
  1211  	err := v.ReadInConfig()
  1212  	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
  1213  
  1214  	// Even though config did not load and the error might have
  1215  	// been ignored by the client, the default still loads
  1216  	assert.Equal(t, `default`, v.GetString(`key`))
  1217  }
  1218  
  1219  func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
  1220  	_, config, cleanup := initDirs(t)
  1221  	defer cleanup()
  1222  
  1223  	v := New()
  1224  	v.SetConfigName(config)
  1225  	v.SetDefault(`key`, `default`)
  1226  
  1227  	v.AddConfigPath(`whattayoutalkingbout`)
  1228  	v.AddConfigPath(`thispathaintthere`)
  1229  
  1230  	err := v.MergeInConfig()
  1231  	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
  1232  
  1233  	// Even though config did not load and the error might have
  1234  	// been ignored by the client, the default still loads
  1235  	assert.Equal(t, `default`, v.GetString(`key`))
  1236  }
  1237  
  1238  func TestSub(t *testing.T) {
  1239  	v := New()
  1240  	v.SetConfigType("yaml")
  1241  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1242  
  1243  	subv := v.Sub("clothing")
  1244  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
  1245  
  1246  	subv = v.Sub("clothing.pants")
  1247  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size"))
  1248  
  1249  	subv = v.Sub("clothing.pants.size")
  1250  	assert.Equal(t, (*Viper)(nil), subv)
  1251  
  1252  	subv = v.Sub("missing.key")
  1253  	assert.Equal(t, (*Viper)(nil), subv)
  1254  }
  1255  
  1256  var hclWriteExpected = []byte(`"foos" = {
  1257    "foo" = {
  1258      "key" = 1
  1259    }
  1260  
  1261    "foo" = {
  1262      "key" = 2
  1263    }
  1264  
  1265    "foo" = {
  1266      "key" = 3
  1267    }
  1268  
  1269    "foo" = {
  1270      "key" = 4
  1271    }
  1272  }
  1273  
  1274  "id" = "0001"
  1275  
  1276  "name" = "Cake"
  1277  
  1278  "ppu" = 0.55
  1279  
  1280  "type" = "donut"`)
  1281  
  1282  var jsonWriteExpected = []byte(`{
  1283    "batters": {
  1284      "batter": [
  1285        {
  1286          "type": "Regular"
  1287        },
  1288        {
  1289          "type": "Chocolate"
  1290        },
  1291        {
  1292          "type": "Blueberry"
  1293        },
  1294        {
  1295          "type": "Devil's Food"
  1296        }
  1297      ]
  1298    },
  1299    "id": "0001",
  1300    "name": "Cake",
  1301    "ppu": 0.55,
  1302    "type": "donut"
  1303  }`)
  1304  
  1305  var propertiesWriteExpected = []byte(`p_id = 0001
  1306  p_type = donut
  1307  p_name = Cake
  1308  p_ppu = 0.55
  1309  p_batters.batter.type = Regular
  1310  `)
  1311  
  1312  var yamlWriteExpected = []byte(`age: 35
  1313  beard: true
  1314  clothing:
  1315    jacket: leather
  1316    pants:
  1317      size: large
  1318    trousers: denim
  1319  eyes: brown
  1320  hacker: true
  1321  hobbies:
  1322  - skateboarding
  1323  - snowboarding
  1324  - go
  1325  name: steve
  1326  `)
  1327  
  1328  func TestWriteConfig(t *testing.T) {
  1329  	fs := afero.NewMemMapFs()
  1330  	testCases := map[string]struct {
  1331  		configName      string
  1332  		inConfigType    string
  1333  		outConfigType   string
  1334  		fileName        string
  1335  		input           []byte
  1336  		expectedContent []byte
  1337  	}{
  1338  		"hcl with file extension": {
  1339  			configName:      "c",
  1340  			inConfigType:    "hcl",
  1341  			outConfigType:   "hcl",
  1342  			fileName:        "c.hcl",
  1343  			input:           hclExample,
  1344  			expectedContent: hclWriteExpected,
  1345  		},
  1346  		"hcl without file extension": {
  1347  			configName:      "c",
  1348  			inConfigType:    "hcl",
  1349  			outConfigType:   "hcl",
  1350  			fileName:        "c",
  1351  			input:           hclExample,
  1352  			expectedContent: hclWriteExpected,
  1353  		},
  1354  		"hcl with file extension and mismatch type": {
  1355  			configName:      "c",
  1356  			inConfigType:    "hcl",
  1357  			outConfigType:   "json",
  1358  			fileName:        "c.hcl",
  1359  			input:           hclExample,
  1360  			expectedContent: hclWriteExpected,
  1361  		},
  1362  		"json with file extension": {
  1363  			configName:      "c",
  1364  			inConfigType:    "json",
  1365  			outConfigType:   "json",
  1366  			fileName:        "c.json",
  1367  			input:           jsonExample,
  1368  			expectedContent: jsonWriteExpected,
  1369  		},
  1370  		"json without file extension": {
  1371  			configName:      "c",
  1372  			inConfigType:    "json",
  1373  			outConfigType:   "json",
  1374  			fileName:        "c",
  1375  			input:           jsonExample,
  1376  			expectedContent: jsonWriteExpected,
  1377  		},
  1378  		"json with file extension and mismatch type": {
  1379  			configName:      "c",
  1380  			inConfigType:    "json",
  1381  			outConfigType:   "hcl",
  1382  			fileName:        "c.json",
  1383  			input:           jsonExample,
  1384  			expectedContent: jsonWriteExpected,
  1385  		},
  1386  		"properties with file extension": {
  1387  			configName:      "c",
  1388  			inConfigType:    "properties",
  1389  			outConfigType:   "properties",
  1390  			fileName:        "c.properties",
  1391  			input:           propertiesExample,
  1392  			expectedContent: propertiesWriteExpected,
  1393  		},
  1394  		"properties without file extension": {
  1395  			configName:      "c",
  1396  			inConfigType:    "properties",
  1397  			outConfigType:   "properties",
  1398  			fileName:        "c",
  1399  			input:           propertiesExample,
  1400  			expectedContent: propertiesWriteExpected,
  1401  		},
  1402  		"yaml with file extension": {
  1403  			configName:      "c",
  1404  			inConfigType:    "yaml",
  1405  			outConfigType:   "yaml",
  1406  			fileName:        "c.yaml",
  1407  			input:           yamlExample,
  1408  			expectedContent: yamlWriteExpected,
  1409  		},
  1410  		"yaml without file extension": {
  1411  			configName:      "c",
  1412  			inConfigType:    "yaml",
  1413  			outConfigType:   "yaml",
  1414  			fileName:        "c",
  1415  			input:           yamlExample,
  1416  			expectedContent: yamlWriteExpected,
  1417  		},
  1418  		"yaml with file extension and mismatch type": {
  1419  			configName:      "c",
  1420  			inConfigType:    "yaml",
  1421  			outConfigType:   "json",
  1422  			fileName:        "c.yaml",
  1423  			input:           yamlExample,
  1424  			expectedContent: yamlWriteExpected,
  1425  		},
  1426  	}
  1427  	for name, tc := range testCases {
  1428  		t.Run(name, func(t *testing.T) {
  1429  			v := New()
  1430  			v.SetFs(fs)
  1431  			v.SetConfigName(tc.fileName)
  1432  			v.SetConfigType(tc.inConfigType)
  1433  
  1434  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1435  			if err != nil {
  1436  				t.Fatal(err)
  1437  			}
  1438  			v.SetConfigType(tc.outConfigType)
  1439  			if err := v.WriteConfigAs(tc.fileName); err != nil {
  1440  				t.Fatal(err)
  1441  			}
  1442  			read, err := afero.ReadFile(fs, tc.fileName)
  1443  			if err != nil {
  1444  				t.Fatal(err)
  1445  			}
  1446  			assert.Equal(t, tc.expectedContent, read)
  1447  		})
  1448  	}
  1449  }
  1450  
  1451  func TestWriteConfigTOML(t *testing.T) {
  1452  	fs := afero.NewMemMapFs()
  1453  
  1454  	testCases := map[string]struct {
  1455  		configName string
  1456  		configType string
  1457  		fileName   string
  1458  		input      []byte
  1459  	}{
  1460  		"with file extension": {
  1461  			configName: "c",
  1462  			configType: "toml",
  1463  			fileName:   "c.toml",
  1464  			input:      tomlExample,
  1465  		},
  1466  		"without file extension": {
  1467  			configName: "c",
  1468  			configType: "toml",
  1469  			fileName:   "c",
  1470  			input:      tomlExample,
  1471  		},
  1472  	}
  1473  	for name, tc := range testCases {
  1474  		t.Run(name, func(t *testing.T) {
  1475  			v := New()
  1476  			v.SetFs(fs)
  1477  			v.SetConfigName(tc.configName)
  1478  			v.SetConfigType(tc.configType)
  1479  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1480  			if err != nil {
  1481  				t.Fatal(err)
  1482  			}
  1483  			if err := v.WriteConfigAs(tc.fileName); err != nil {
  1484  				t.Fatal(err)
  1485  			}
  1486  
  1487  			// The TOML String method does not order the contents.
  1488  			// Therefore, we must read the generated file and compare the data.
  1489  			v2 := New()
  1490  			v2.SetFs(fs)
  1491  			v2.SetConfigName(tc.configName)
  1492  			v2.SetConfigType(tc.configType)
  1493  			v2.SetConfigFile(tc.fileName)
  1494  			err = v2.ReadInConfig()
  1495  			if err != nil {
  1496  				t.Fatal(err)
  1497  			}
  1498  
  1499  			assert.Equal(t, v.GetString("title"), v2.GetString("title"))
  1500  			assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio"))
  1501  			assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob"))
  1502  			assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization"))
  1503  		})
  1504  	}
  1505  }
  1506  
  1507  func TestWriteConfigDotEnv(t *testing.T) {
  1508  	fs := afero.NewMemMapFs()
  1509  	testCases := map[string]struct {
  1510  		configName string
  1511  		configType string
  1512  		fileName   string
  1513  		input      []byte
  1514  	}{
  1515  		"with file extension": {
  1516  			configName: "c",
  1517  			configType: "env",
  1518  			fileName:   "c.env",
  1519  			input:      dotenvExample,
  1520  		},
  1521  		"without file extension": {
  1522  			configName: "c",
  1523  			configType: "env",
  1524  			fileName:   "c",
  1525  			input:      dotenvExample,
  1526  		},
  1527  	}
  1528  	for name, tc := range testCases {
  1529  		t.Run(name, func(t *testing.T) {
  1530  			v := New()
  1531  			v.SetFs(fs)
  1532  			v.SetConfigName(tc.configName)
  1533  			v.SetConfigType(tc.configType)
  1534  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1535  			if err != nil {
  1536  				t.Fatal(err)
  1537  			}
  1538  			if err := v.WriteConfigAs(tc.fileName); err != nil {
  1539  				t.Fatal(err)
  1540  			}
  1541  
  1542  			// The TOML String method does not order the contents.
  1543  			// Therefore, we must read the generated file and compare the data.
  1544  			v2 := New()
  1545  			v2.SetFs(fs)
  1546  			v2.SetConfigName(tc.configName)
  1547  			v2.SetConfigType(tc.configType)
  1548  			v2.SetConfigFile(tc.fileName)
  1549  			err = v2.ReadInConfig()
  1550  			if err != nil {
  1551  				t.Fatal(err)
  1552  			}
  1553  
  1554  			assert.Equal(t, v.GetString("title_dotenv"), v2.GetString("title_dotenv"))
  1555  			assert.Equal(t, v.GetString("type_dotenv"), v2.GetString("type_dotenv"))
  1556  			assert.Equal(t, v.GetString("kind_dotenv"), v2.GetString("kind_dotenv"))
  1557  		})
  1558  	}
  1559  }
  1560  
  1561  func TestSafeWriteConfig(t *testing.T) {
  1562  	v := New()
  1563  	fs := afero.NewMemMapFs()
  1564  	v.SetFs(fs)
  1565  	v.AddConfigPath("/test")
  1566  	v.SetConfigName("c")
  1567  	v.SetConfigType("yaml")
  1568  	require.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)))
  1569  	require.NoError(t, v.SafeWriteConfig())
  1570  	read, err := afero.ReadFile(fs, "/test/c.yaml")
  1571  	require.NoError(t, err)
  1572  	assert.Equal(t, yamlWriteExpected, read)
  1573  }
  1574  
  1575  func TestSafeWriteConfigWithMissingConfigPath(t *testing.T) {
  1576  	v := New()
  1577  	fs := afero.NewMemMapFs()
  1578  	v.SetFs(fs)
  1579  	v.SetConfigName("c")
  1580  	v.SetConfigType("yaml")
  1581  	require.EqualError(t, v.SafeWriteConfig(), "missing configuration for 'configPath'")
  1582  }
  1583  
  1584  func TestSafeWriteConfigWithExistingFile(t *testing.T) {
  1585  	v := New()
  1586  	fs := afero.NewMemMapFs()
  1587  	fs.Create("/test/c.yaml")
  1588  	v.SetFs(fs)
  1589  	v.AddConfigPath("/test")
  1590  	v.SetConfigName("c")
  1591  	v.SetConfigType("yaml")
  1592  	err := v.SafeWriteConfig()
  1593  	require.Error(t, err)
  1594  	_, ok := err.(ConfigFileAlreadyExistsError)
  1595  	assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
  1596  }
  1597  
  1598  func TestSafeWriteAsConfig(t *testing.T) {
  1599  	v := New()
  1600  	fs := afero.NewMemMapFs()
  1601  	v.SetFs(fs)
  1602  	err := v.ReadConfig(bytes.NewBuffer(yamlExample))
  1603  	if err != nil {
  1604  		t.Fatal(err)
  1605  	}
  1606  	require.NoError(t, v.SafeWriteConfigAs("/test/c.yaml"))
  1607  	if _, err = afero.ReadFile(fs, "/test/c.yaml"); err != nil {
  1608  		t.Fatal(err)
  1609  	}
  1610  }
  1611  
  1612  func TestSafeWriteConfigAsWithExistingFile(t *testing.T) {
  1613  	v := New()
  1614  	fs := afero.NewMemMapFs()
  1615  	fs.Create("/test/c.yaml")
  1616  	v.SetFs(fs)
  1617  	err := v.SafeWriteConfigAs("/test/c.yaml")
  1618  	require.Error(t, err)
  1619  	_, ok := err.(ConfigFileAlreadyExistsError)
  1620  	assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
  1621  }
  1622  
  1623  var yamlMergeExampleTgt = []byte(`
  1624  hello:
  1625      pop: 37890
  1626      largenum: 765432101234567
  1627      num2pow63: 9223372036854775808
  1628      world:
  1629      - us
  1630      - uk
  1631      - fr
  1632      - de
  1633  `)
  1634  
  1635  var yamlMergeExampleSrc = []byte(`
  1636  hello:
  1637      pop: 45000
  1638      largenum: 7654321001234567
  1639      universe:
  1640      - mw
  1641      - ad
  1642      ints:
  1643      - 1
  1644      - 2
  1645  fu: bar
  1646  `)
  1647  
  1648  func TestMergeConfig(t *testing.T) {
  1649  	v := New()
  1650  	v.SetConfigType("yml")
  1651  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1652  		t.Fatal(err)
  1653  	}
  1654  
  1655  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  1656  		t.Fatalf("pop != 37890, = %d", pop)
  1657  	}
  1658  
  1659  	if pop := v.GetInt32("hello.pop"); pop != int32(37890) {
  1660  		t.Fatalf("pop != 37890, = %d", pop)
  1661  	}
  1662  
  1663  	if pop := v.GetInt64("hello.largenum"); pop != int64(765432101234567) {
  1664  		t.Fatalf("int64 largenum != 765432101234567, = %d", pop)
  1665  	}
  1666  
  1667  	if pop := v.GetUint("hello.pop"); pop != 37890 {
  1668  		t.Fatalf("uint pop != 37890, = %d", pop)
  1669  	}
  1670  
  1671  	if pop := v.GetUint32("hello.pop"); pop != 37890 {
  1672  		t.Fatalf("uint32 pop != 37890, = %d", pop)
  1673  	}
  1674  
  1675  	if pop := v.GetUint64("hello.num2pow63"); pop != 9223372036854775808 {
  1676  		t.Fatalf("uint64 num2pow63 != 9223372036854775808, = %d", pop)
  1677  	}
  1678  
  1679  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1680  		t.Fatalf("len(world) != 4, = %d", len(world))
  1681  	}
  1682  
  1683  	if fu := v.GetString("fu"); fu != "" {
  1684  		t.Fatalf("fu != \"\", = %s", fu)
  1685  	}
  1686  
  1687  	if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  1688  		t.Fatal(err)
  1689  	}
  1690  
  1691  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  1692  		t.Fatalf("pop != 45000, = %d", pop)
  1693  	}
  1694  
  1695  	if pop := v.GetInt32("hello.pop"); pop != int32(45000) {
  1696  		t.Fatalf("pop != 45000, = %d", pop)
  1697  	}
  1698  
  1699  	if pop := v.GetInt64("hello.largenum"); pop != int64(7654321001234567) {
  1700  		t.Fatalf("int64 largenum != 7654321001234567, = %d", pop)
  1701  	}
  1702  
  1703  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1704  		t.Fatalf("len(world) != 4, = %d", len(world))
  1705  	}
  1706  
  1707  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  1708  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  1709  	}
  1710  
  1711  	if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
  1712  		t.Fatalf("len(ints) != 2, = %d", len(ints))
  1713  	}
  1714  
  1715  	if fu := v.GetString("fu"); fu != "bar" {
  1716  		t.Fatalf("fu != \"bar\", = %s", fu)
  1717  	}
  1718  }
  1719  
  1720  func TestMergeConfigNoMerge(t *testing.T) {
  1721  	v := New()
  1722  	v.SetConfigType("yml")
  1723  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1724  		t.Fatal(err)
  1725  	}
  1726  
  1727  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  1728  		t.Fatalf("pop != 37890, = %d", pop)
  1729  	}
  1730  
  1731  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1732  		t.Fatalf("len(world) != 4, = %d", len(world))
  1733  	}
  1734  
  1735  	if fu := v.GetString("fu"); fu != "" {
  1736  		t.Fatalf("fu != \"\", = %s", fu)
  1737  	}
  1738  
  1739  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  1740  		t.Fatal(err)
  1741  	}
  1742  
  1743  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  1744  		t.Fatalf("pop != 45000, = %d", pop)
  1745  	}
  1746  
  1747  	if world := v.GetStringSlice("hello.world"); len(world) != 0 {
  1748  		t.Fatalf("len(world) != 0, = %d", len(world))
  1749  	}
  1750  
  1751  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  1752  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  1753  	}
  1754  
  1755  	if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
  1756  		t.Fatalf("len(ints) != 2, = %d", len(ints))
  1757  	}
  1758  
  1759  	if fu := v.GetString("fu"); fu != "bar" {
  1760  		t.Fatalf("fu != \"bar\", = %s", fu)
  1761  	}
  1762  }
  1763  
  1764  func TestMergeConfigMap(t *testing.T) {
  1765  	v := New()
  1766  	v.SetConfigType("yml")
  1767  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1768  		t.Fatal(err)
  1769  	}
  1770  
  1771  	assert := func(i int) {
  1772  		large := v.GetInt64("hello.largenum")
  1773  		pop := v.GetInt("hello.pop")
  1774  		if large != 765432101234567 {
  1775  			t.Fatal("Got large num:", large)
  1776  		}
  1777  
  1778  		if pop != i {
  1779  			t.Fatal("Got pop:", pop)
  1780  		}
  1781  	}
  1782  
  1783  	assert(37890)
  1784  
  1785  	update := map[string]interface{}{
  1786  		"Hello": map[string]interface{}{
  1787  			"Pop": 1234,
  1788  		},
  1789  		"World": map[interface{}]interface{}{
  1790  			"Rock": 345,
  1791  		},
  1792  	}
  1793  
  1794  	if err := v.MergeConfigMap(update); err != nil {
  1795  		t.Fatal(err)
  1796  	}
  1797  
  1798  	if rock := v.GetInt("world.rock"); rock != 345 {
  1799  		t.Fatal("Got rock:", rock)
  1800  	}
  1801  
  1802  	assert(1234)
  1803  }
  1804  
  1805  func TestUnmarshalingWithAliases(t *testing.T) {
  1806  	v := New()
  1807  	v.SetDefault("ID", 1)
  1808  	v.Set("name", "Steve")
  1809  	v.Set("lastname", "Owen")
  1810  
  1811  	v.RegisterAlias("UserID", "ID")
  1812  	v.RegisterAlias("Firstname", "name")
  1813  	v.RegisterAlias("Surname", "lastname")
  1814  
  1815  	type config struct {
  1816  		ID        int
  1817  		FirstName string
  1818  		Surname   string
  1819  	}
  1820  
  1821  	var C config
  1822  	err := v.Unmarshal(&C)
  1823  	if err != nil {
  1824  		t.Fatalf("unable to decode into struct, %v", err)
  1825  	}
  1826  
  1827  	assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C)
  1828  }
  1829  
  1830  func TestSetConfigNameClearsFileCache(t *testing.T) {
  1831  	SetConfigFile("/tmp/config.yaml")
  1832  	SetConfigName("default")
  1833  	f, err := v.getConfigFile()
  1834  	if err == nil {
  1835  		t.Fatalf("config file cache should have been cleared")
  1836  	}
  1837  	assert.Empty(t, f)
  1838  }
  1839  
  1840  func TestShadowedNestedValue(t *testing.T) {
  1841  	config := `name: steve
  1842  clothing:
  1843    jacket: leather
  1844    trousers: denim
  1845    pants:
  1846      size: large
  1847  `
  1848  	initConfig("yaml", config)
  1849  
  1850  	assert.Equal(t, "steve", GetString("name"))
  1851  
  1852  	polyester := "polyester"
  1853  	SetDefault("clothing.shirt", polyester)
  1854  	SetDefault("clothing.jacket.price", 100)
  1855  
  1856  	assert.Equal(t, "leather", GetString("clothing.jacket"))
  1857  	assert.Nil(t, Get("clothing.jacket.price"))
  1858  	assert.Equal(t, polyester, GetString("clothing.shirt"))
  1859  
  1860  	clothingSettings := AllSettings()["clothing"].(map[string]interface{})
  1861  	assert.Equal(t, "leather", clothingSettings["jacket"])
  1862  	assert.Equal(t, polyester, clothingSettings["shirt"])
  1863  }
  1864  
  1865  func TestDotParameter(t *testing.T) {
  1866  	initJSON()
  1867  	// shoud take precedence over batters defined in jsonExample
  1868  	r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
  1869  	unmarshalReader(r, v.config)
  1870  
  1871  	actual := Get("batters.batter")
  1872  	expected := []interface{}{map[string]interface{}{"type": "Small"}}
  1873  	assert.Equal(t, expected, actual)
  1874  }
  1875  
  1876  func TestCaseInsensitive(t *testing.T) {
  1877  	for _, config := range []struct {
  1878  		typ     string
  1879  		content string
  1880  	}{
  1881  		{"yaml", `
  1882  aBcD: 1
  1883  eF:
  1884    gH: 2
  1885    iJk: 3
  1886    Lm:
  1887      nO: 4
  1888      P:
  1889        Q: 5
  1890        R: 6
  1891  `},
  1892  		{"json", `{
  1893    "aBcD": 1,
  1894    "eF": {
  1895      "iJk": 3,
  1896      "Lm": {
  1897        "P": {
  1898          "Q": 5,
  1899          "R": 6
  1900        },
  1901        "nO": 4
  1902      },
  1903      "gH": 2
  1904    }
  1905  }`},
  1906  		{"toml", `aBcD = 1
  1907  [eF]
  1908  gH = 2
  1909  iJk = 3
  1910  [eF.Lm]
  1911  nO = 4
  1912  [eF.Lm.P]
  1913  Q = 5
  1914  R = 6
  1915  `},
  1916  	} {
  1917  		doTestCaseInsensitive(t, config.typ, config.content)
  1918  	}
  1919  }
  1920  
  1921  func TestCaseInsensitiveSet(t *testing.T) {
  1922  	Reset()
  1923  	m1 := map[string]interface{}{
  1924  		"Foo": 32,
  1925  		"Bar": map[interface{}]interface {
  1926  		}{
  1927  			"ABc": "A",
  1928  			"cDE": "B"},
  1929  	}
  1930  
  1931  	m2 := map[string]interface{}{
  1932  		"Foo": 52,
  1933  		"Bar": map[interface{}]interface {
  1934  		}{
  1935  			"bCd": "A",
  1936  			"eFG": "B"},
  1937  	}
  1938  
  1939  	Set("Given1", m1)
  1940  	Set("Number1", 42)
  1941  
  1942  	SetDefault("Given2", m2)
  1943  	SetDefault("Number2", 52)
  1944  
  1945  	// Verify SetDefault
  1946  	if v := Get("number2"); v != 52 {
  1947  		t.Fatalf("Expected 52 got %q", v)
  1948  	}
  1949  
  1950  	if v := Get("given2.foo"); v != 52 {
  1951  		t.Fatalf("Expected 52 got %q", v)
  1952  	}
  1953  
  1954  	if v := Get("given2.bar.bcd"); v != "A" {
  1955  		t.Fatalf("Expected A got %q", v)
  1956  	}
  1957  
  1958  	if _, ok := m2["Foo"]; !ok {
  1959  		t.Fatal("Input map changed")
  1960  	}
  1961  
  1962  	// Verify Set
  1963  	if v := Get("number1"); v != 42 {
  1964  		t.Fatalf("Expected 42 got %q", v)
  1965  	}
  1966  
  1967  	if v := Get("given1.foo"); v != 32 {
  1968  		t.Fatalf("Expected 32 got %q", v)
  1969  	}
  1970  
  1971  	if v := Get("given1.bar.abc"); v != "A" {
  1972  		t.Fatalf("Expected A got %q", v)
  1973  	}
  1974  
  1975  	if _, ok := m1["Foo"]; !ok {
  1976  		t.Fatal("Input map changed")
  1977  	}
  1978  }
  1979  
  1980  func TestParseNested(t *testing.T) {
  1981  	type duration struct {
  1982  		Delay time.Duration
  1983  	}
  1984  
  1985  	type item struct {
  1986  		Name   string
  1987  		Delay  time.Duration
  1988  		Nested duration
  1989  	}
  1990  
  1991  	config := `[[parent]]
  1992  	delay="100ms"
  1993  	[parent.nested]
  1994  	delay="200ms"
  1995  `
  1996  	initConfig("toml", config)
  1997  
  1998  	var items []item
  1999  	err := v.UnmarshalKey("parent", &items)
  2000  	if err != nil {
  2001  		t.Fatalf("unable to decode into struct, %v", err)
  2002  	}
  2003  
  2004  	assert.Equal(t, 1, len(items))
  2005  	assert.Equal(t, 100*time.Millisecond, items[0].Delay)
  2006  	assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
  2007  }
  2008  
  2009  func doTestCaseInsensitive(t *testing.T, typ, config string) {
  2010  	initConfig(typ, config)
  2011  	Set("RfD", true)
  2012  	assert.Equal(t, true, Get("rfd"))
  2013  	assert.Equal(t, true, Get("rFD"))
  2014  	assert.Equal(t, 1, cast.ToInt(Get("abcd")))
  2015  	assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
  2016  	assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
  2017  	assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
  2018  	assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
  2019  	assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
  2020  }
  2021  
  2022  func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) {
  2023  	watchDir, err := ioutil.TempDir("", "")
  2024  	require.Nil(t, err)
  2025  	configFile := path.Join(watchDir, "config.yaml")
  2026  	err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640)
  2027  	require.Nil(t, err)
  2028  	cleanup := func() {
  2029  		os.RemoveAll(watchDir)
  2030  	}
  2031  	v := New()
  2032  	v.SetConfigFile(configFile)
  2033  	err = v.ReadInConfig()
  2034  	require.Nil(t, err)
  2035  	require.Equal(t, "bar", v.Get("foo"))
  2036  	return v, configFile, cleanup
  2037  }
  2038  
  2039  func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) {
  2040  	watchDir, err := ioutil.TempDir("", "")
  2041  	require.Nil(t, err)
  2042  	dataDir1 := path.Join(watchDir, "data1")
  2043  	err = os.Mkdir(dataDir1, 0777)
  2044  	require.Nil(t, err)
  2045  	realConfigFile := path.Join(dataDir1, "config.yaml")
  2046  	t.Logf("Real config file location: %s\n", realConfigFile)
  2047  	err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640)
  2048  	require.Nil(t, err)
  2049  	cleanup := func() {
  2050  		os.RemoveAll(watchDir)
  2051  	}
  2052  	// now, symlink the tm `data1` dir to `data` in the baseDir
  2053  	os.Symlink(dataDir1, path.Join(watchDir, "data"))
  2054  	// and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`
  2055  	configFile := path.Join(watchDir, "config.yaml")
  2056  	os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile)
  2057  	t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml"))
  2058  	// init Viper
  2059  	v := New()
  2060  	v.SetConfigFile(configFile)
  2061  	err = v.ReadInConfig()
  2062  	require.Nil(t, err)
  2063  	require.Equal(t, "bar", v.Get("foo"))
  2064  	return v, watchDir, configFile, cleanup
  2065  }
  2066  
  2067  func TestWatchFile(t *testing.T) {
  2068  	if runtime.GOOS == "linux" {
  2069  		// TODO(bep) FIX ME
  2070  		t.Skip("Skip test on Linux ...")
  2071  	}
  2072  
  2073  	t.Run("file content changed", func(t *testing.T) {
  2074  		// given a `config.yaml` file being watched
  2075  		v, configFile, cleanup := newViperWithConfigFile(t)
  2076  		defer cleanup()
  2077  		_, err := os.Stat(configFile)
  2078  		require.NoError(t, err)
  2079  		t.Logf("test config file: %s\n", configFile)
  2080  		wg := sync.WaitGroup{}
  2081  		wg.Add(1)
  2082  		v.OnConfigChange(func(in fsnotify.Event) {
  2083  			t.Logf("config file changed")
  2084  			wg.Done()
  2085  		})
  2086  		v.WatchConfig()
  2087  		// when overwriting the file and waiting for the custom change notification handler to be triggered
  2088  		err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640)
  2089  		wg.Wait()
  2090  		// then the config value should have changed
  2091  		require.Nil(t, err)
  2092  		assert.Equal(t, "baz", v.Get("foo"))
  2093  	})
  2094  
  2095  	t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) {
  2096  		// skip if not executed on Linux
  2097  		if runtime.GOOS != "linux" {
  2098  			t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
  2099  		}
  2100  		v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t)
  2101  		// defer cleanup()
  2102  		wg := sync.WaitGroup{}
  2103  		v.WatchConfig()
  2104  		v.OnConfigChange(func(in fsnotify.Event) {
  2105  			t.Logf("config file changed")
  2106  			wg.Done()
  2107  		})
  2108  		wg.Add(1)
  2109  		// when link to another `config.yaml` file
  2110  		dataDir2 := path.Join(watchDir, "data2")
  2111  		err := os.Mkdir(dataDir2, 0777)
  2112  		require.Nil(t, err)
  2113  		configFile2 := path.Join(dataDir2, "config.yaml")
  2114  		err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640)
  2115  		require.Nil(t, err)
  2116  		// change the symlink using the `ln -sfn` command
  2117  		err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
  2118  		require.Nil(t, err)
  2119  		wg.Wait()
  2120  		// then
  2121  		require.Nil(t, err)
  2122  		assert.Equal(t, "baz", v.Get("foo"))
  2123  	})
  2124  }
  2125  
  2126  func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
  2127  	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  2128  	flags.String("foo.bar", "cobra_flag", "")
  2129  
  2130  	v := New()
  2131  	assert.NoError(t, v.BindPFlags(flags))
  2132  
  2133  	config := &struct {
  2134  		Foo struct {
  2135  			Bar string
  2136  		}
  2137  	}{}
  2138  
  2139  	assert.NoError(t, v.Unmarshal(config))
  2140  	assert.Equal(t, "cobra_flag", config.Foo.Bar)
  2141  }
  2142  
  2143  var yamlExampleWithDot = []byte(`Hacker: true
  2144  name: steve
  2145  hobbies:
  2146    - skateboarding
  2147    - snowboarding
  2148    - go
  2149  clothing:
  2150    jacket: leather
  2151    trousers: denim
  2152    pants:
  2153      size: large
  2154  age: 35
  2155  eyes : brown
  2156  beard: true
  2157  emails:
  2158    steve@hacker.com:
  2159      created: 01/02/03
  2160      active: true
  2161  `)
  2162  
  2163  func TestKeyDelimiter(t *testing.T) {
  2164  	v := NewWithOptions(KeyDelimiter("::"))
  2165  	v.SetConfigType("yaml")
  2166  	r := strings.NewReader(string(yamlExampleWithDot))
  2167  
  2168  	err := v.unmarshalReader(r, v.config)
  2169  	require.NoError(t, err)
  2170  
  2171  	values := map[string]interface{}{
  2172  		"image": map[string]interface{}{
  2173  			"repository": "someImage",
  2174  			"tag":        "1.0.0",
  2175  		},
  2176  		"ingress": map[string]interface{}{
  2177  			"annotations": map[string]interface{}{
  2178  				"traefik.frontend.rule.type":                 "PathPrefix",
  2179  				"traefik.ingress.kubernetes.io/ssl-redirect": "true",
  2180  			},
  2181  		},
  2182  	}
  2183  
  2184  	v.SetDefault("charts::values", values)
  2185  
  2186  	assert.Equal(t, "leather", v.GetString("clothing::jacket"))
  2187  	assert.Equal(t, "01/02/03", v.GetString("emails::steve@hacker.com::created"))
  2188  
  2189  	type config struct {
  2190  		Charts struct {
  2191  			Values map[string]interface{}
  2192  		}
  2193  	}
  2194  
  2195  	expected := config{
  2196  		Charts: struct {
  2197  			Values map[string]interface{}
  2198  		}{
  2199  			Values: values,
  2200  		},
  2201  	}
  2202  
  2203  	var actual config
  2204  
  2205  	assert.NoError(t, v.Unmarshal(&actual))
  2206  
  2207  	assert.Equal(t, expected, actual)
  2208  }
  2209  
  2210  func BenchmarkGetBool(b *testing.B) {
  2211  	key := "BenchmarkGetBool"
  2212  	v = New()
  2213  	v.Set(key, true)
  2214  
  2215  	for i := 0; i < b.N; i++ {
  2216  		if !v.GetBool(key) {
  2217  			b.Fatal("GetBool returned false")
  2218  		}
  2219  	}
  2220  }
  2221  
  2222  func BenchmarkGet(b *testing.B) {
  2223  	key := "BenchmarkGet"
  2224  	v = New()
  2225  	v.Set(key, true)
  2226  
  2227  	for i := 0; i < b.N; i++ {
  2228  		if !v.Get(key).(bool) {
  2229  			b.Fatal("Get returned false")
  2230  		}
  2231  	}
  2232  }
  2233  
  2234  // BenchmarkGetBoolFromMap is the "perfect result" for the above.
  2235  func BenchmarkGetBoolFromMap(b *testing.B) {
  2236  	m := make(map[string]bool)
  2237  	key := "BenchmarkGetBool"
  2238  	m[key] = true
  2239  
  2240  	for i := 0; i < b.N; i++ {
  2241  		if !m[key] {
  2242  			b.Fatal("Map value was false")
  2243  		}
  2244  	}
  2245  }