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