github.com/Zyvik/viper@v1.7.2-0.20210523130016-5b1eb90a6789/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/spf13/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      universe: null
  1699      world:
  1700      - us
  1701      - uk
  1702      - fr
  1703      - de
  1704  `)
  1705  
  1706  var yamlMergeExampleSrc = []byte(`
  1707  hello:
  1708      pop: 45000
  1709      largenum: 7654321001234567
  1710      universe:
  1711      - mw
  1712      - ad
  1713      ints:
  1714      - 1
  1715      - 2
  1716  fu: bar
  1717  `)
  1718  
  1719  func TestMergeConfig(t *testing.T) {
  1720  	v := New()
  1721  	v.SetConfigType("yml")
  1722  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1723  		t.Fatal(err)
  1724  	}
  1725  
  1726  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  1727  		t.Fatalf("pop != 37890, = %d", pop)
  1728  	}
  1729  
  1730  	if pop := v.GetInt32("hello.pop"); pop != int32(37890) {
  1731  		t.Fatalf("pop != 37890, = %d", pop)
  1732  	}
  1733  
  1734  	if pop := v.GetInt64("hello.largenum"); pop != int64(765432101234567) {
  1735  		t.Fatalf("int64 largenum != 765432101234567, = %d", pop)
  1736  	}
  1737  
  1738  	if pop := v.GetUint("hello.pop"); pop != 37890 {
  1739  		t.Fatalf("uint pop != 37890, = %d", pop)
  1740  	}
  1741  
  1742  	if pop := v.GetUint32("hello.pop"); pop != 37890 {
  1743  		t.Fatalf("uint32 pop != 37890, = %d", pop)
  1744  	}
  1745  
  1746  	if pop := v.GetUint64("hello.num2pow63"); pop != 9223372036854775808 {
  1747  		t.Fatalf("uint64 num2pow63 != 9223372036854775808, = %d", pop)
  1748  	}
  1749  
  1750  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1751  		t.Fatalf("len(world) != 4, = %d", len(world))
  1752  	}
  1753  
  1754  	if fu := v.GetString("fu"); fu != "" {
  1755  		t.Fatalf("fu != \"\", = %s", fu)
  1756  	}
  1757  
  1758  	if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  1759  		t.Fatal(err)
  1760  	}
  1761  
  1762  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  1763  		t.Fatalf("pop != 45000, = %d", pop)
  1764  	}
  1765  
  1766  	if pop := v.GetInt32("hello.pop"); pop != int32(45000) {
  1767  		t.Fatalf("pop != 45000, = %d", pop)
  1768  	}
  1769  
  1770  	if pop := v.GetInt64("hello.largenum"); pop != int64(7654321001234567) {
  1771  		t.Fatalf("int64 largenum != 7654321001234567, = %d", pop)
  1772  	}
  1773  
  1774  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1775  		t.Fatalf("len(world) != 4, = %d", len(world))
  1776  	}
  1777  
  1778  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  1779  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  1780  	}
  1781  
  1782  	if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
  1783  		t.Fatalf("len(ints) != 2, = %d", len(ints))
  1784  	}
  1785  
  1786  	if fu := v.GetString("fu"); fu != "bar" {
  1787  		t.Fatalf("fu != \"bar\", = %s", fu)
  1788  	}
  1789  }
  1790  
  1791  func TestMergeConfigNoMerge(t *testing.T) {
  1792  	v := New()
  1793  	v.SetConfigType("yml")
  1794  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1795  		t.Fatal(err)
  1796  	}
  1797  
  1798  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  1799  		t.Fatalf("pop != 37890, = %d", pop)
  1800  	}
  1801  
  1802  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1803  		t.Fatalf("len(world) != 4, = %d", len(world))
  1804  	}
  1805  
  1806  	if fu := v.GetString("fu"); fu != "" {
  1807  		t.Fatalf("fu != \"\", = %s", fu)
  1808  	}
  1809  
  1810  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  1811  		t.Fatal(err)
  1812  	}
  1813  
  1814  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  1815  		t.Fatalf("pop != 45000, = %d", pop)
  1816  	}
  1817  
  1818  	if world := v.GetStringSlice("hello.world"); len(world) != 0 {
  1819  		t.Fatalf("len(world) != 0, = %d", len(world))
  1820  	}
  1821  
  1822  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  1823  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  1824  	}
  1825  
  1826  	if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
  1827  		t.Fatalf("len(ints) != 2, = %d", len(ints))
  1828  	}
  1829  
  1830  	if fu := v.GetString("fu"); fu != "bar" {
  1831  		t.Fatalf("fu != \"bar\", = %s", fu)
  1832  	}
  1833  }
  1834  
  1835  func TestMergeConfigMap(t *testing.T) {
  1836  	v := New()
  1837  	v.SetConfigType("yml")
  1838  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1839  		t.Fatal(err)
  1840  	}
  1841  
  1842  	assert := func(i int) {
  1843  		large := v.GetInt64("hello.largenum")
  1844  		pop := v.GetInt("hello.pop")
  1845  		if large != 765432101234567 {
  1846  			t.Fatal("Got large num:", large)
  1847  		}
  1848  
  1849  		if pop != i {
  1850  			t.Fatal("Got pop:", pop)
  1851  		}
  1852  	}
  1853  
  1854  	assert(37890)
  1855  
  1856  	update := map[string]interface{}{
  1857  		"Hello": map[string]interface{}{
  1858  			"Pop": 1234,
  1859  		},
  1860  		"World": map[interface{}]interface{}{
  1861  			"Rock": 345,
  1862  		},
  1863  	}
  1864  
  1865  	if err := v.MergeConfigMap(update); err != nil {
  1866  		t.Fatal(err)
  1867  	}
  1868  
  1869  	if rock := v.GetInt("world.rock"); rock != 345 {
  1870  		t.Fatal("Got rock:", rock)
  1871  	}
  1872  
  1873  	assert(1234)
  1874  }
  1875  
  1876  func TestUnmarshalingWithAliases(t *testing.T) {
  1877  	v := New()
  1878  	v.SetDefault("ID", 1)
  1879  	v.Set("name", "Steve")
  1880  	v.Set("lastname", "Owen")
  1881  
  1882  	v.RegisterAlias("UserID", "ID")
  1883  	v.RegisterAlias("Firstname", "name")
  1884  	v.RegisterAlias("Surname", "lastname")
  1885  
  1886  	type config struct {
  1887  		ID        int
  1888  		FirstName string
  1889  		Surname   string
  1890  	}
  1891  
  1892  	var C config
  1893  	err := v.Unmarshal(&C)
  1894  	if err != nil {
  1895  		t.Fatalf("unable to decode into struct, %v", err)
  1896  	}
  1897  
  1898  	assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C)
  1899  }
  1900  
  1901  func TestSetConfigNameClearsFileCache(t *testing.T) {
  1902  	SetConfigFile("/tmp/config.yaml")
  1903  	SetConfigName("default")
  1904  	f, err := v.getConfigFile()
  1905  	if err == nil {
  1906  		t.Fatalf("config file cache should have been cleared")
  1907  	}
  1908  	assert.Empty(t, f)
  1909  }
  1910  
  1911  func TestShadowedNestedValue(t *testing.T) {
  1912  	config := `name: steve
  1913  clothing:
  1914    jacket: leather
  1915    trousers: denim
  1916    pants:
  1917      size: large
  1918  `
  1919  	initConfig("yaml", config)
  1920  
  1921  	assert.Equal(t, "steve", GetString("name"))
  1922  
  1923  	polyester := "polyester"
  1924  	SetDefault("clothing.shirt", polyester)
  1925  	SetDefault("clothing.jacket.price", 100)
  1926  
  1927  	assert.Equal(t, "leather", GetString("clothing.jacket"))
  1928  	assert.Nil(t, Get("clothing.jacket.price"))
  1929  	assert.Equal(t, polyester, GetString("clothing.shirt"))
  1930  
  1931  	clothingSettings := AllSettings()["clothing"].(map[string]interface{})
  1932  	assert.Equal(t, "leather", clothingSettings["jacket"])
  1933  	assert.Equal(t, polyester, clothingSettings["shirt"])
  1934  }
  1935  
  1936  func TestDotParameter(t *testing.T) {
  1937  	initJSON()
  1938  	// shoud take precedence over batters defined in jsonExample
  1939  	r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
  1940  	unmarshalReader(r, v.config)
  1941  
  1942  	actual := Get("batters.batter")
  1943  	expected := []interface{}{map[string]interface{}{"type": "Small"}}
  1944  	assert.Equal(t, expected, actual)
  1945  }
  1946  
  1947  func TestCaseInsensitive(t *testing.T) {
  1948  	for _, config := range []struct {
  1949  		typ     string
  1950  		content string
  1951  	}{
  1952  		{"yaml", `
  1953  aBcD: 1
  1954  eF:
  1955    gH: 2
  1956    iJk: 3
  1957    Lm:
  1958      nO: 4
  1959      P:
  1960        Q: 5
  1961        R: 6
  1962  `},
  1963  		{"json", `{
  1964    "aBcD": 1,
  1965    "eF": {
  1966      "iJk": 3,
  1967      "Lm": {
  1968        "P": {
  1969          "Q": 5,
  1970          "R": 6
  1971        },
  1972        "nO": 4
  1973      },
  1974      "gH": 2
  1975    }
  1976  }`},
  1977  		{"toml", `aBcD = 1
  1978  [eF]
  1979  gH = 2
  1980  iJk = 3
  1981  [eF.Lm]
  1982  nO = 4
  1983  [eF.Lm.P]
  1984  Q = 5
  1985  R = 6
  1986  `},
  1987  	} {
  1988  		doTestCaseInsensitive(t, config.typ, config.content)
  1989  	}
  1990  }
  1991  
  1992  func TestCaseInsensitiveSet(t *testing.T) {
  1993  	Reset()
  1994  	m1 := map[string]interface{}{
  1995  		"Foo": 32,
  1996  		"Bar": map[interface{}]interface {
  1997  		}{
  1998  			"ABc": "A",
  1999  			"cDE": "B",
  2000  		},
  2001  	}
  2002  
  2003  	m2 := map[string]interface{}{
  2004  		"Foo": 52,
  2005  		"Bar": map[interface{}]interface {
  2006  		}{
  2007  			"bCd": "A",
  2008  			"eFG": "B",
  2009  		},
  2010  	}
  2011  
  2012  	Set("Given1", m1)
  2013  	Set("Number1", 42)
  2014  
  2015  	SetDefault("Given2", m2)
  2016  	SetDefault("Number2", 52)
  2017  
  2018  	// Verify SetDefault
  2019  	if v := Get("number2"); v != 52 {
  2020  		t.Fatalf("Expected 52 got %q", v)
  2021  	}
  2022  
  2023  	if v := Get("given2.foo"); v != 52 {
  2024  		t.Fatalf("Expected 52 got %q", v)
  2025  	}
  2026  
  2027  	if v := Get("given2.bar.bcd"); v != "A" {
  2028  		t.Fatalf("Expected A got %q", v)
  2029  	}
  2030  
  2031  	if _, ok := m2["Foo"]; !ok {
  2032  		t.Fatal("Input map changed")
  2033  	}
  2034  
  2035  	// Verify Set
  2036  	if v := Get("number1"); v != 42 {
  2037  		t.Fatalf("Expected 42 got %q", v)
  2038  	}
  2039  
  2040  	if v := Get("given1.foo"); v != 32 {
  2041  		t.Fatalf("Expected 32 got %q", v)
  2042  	}
  2043  
  2044  	if v := Get("given1.bar.abc"); v != "A" {
  2045  		t.Fatalf("Expected A got %q", v)
  2046  	}
  2047  
  2048  	if _, ok := m1["Foo"]; !ok {
  2049  		t.Fatal("Input map changed")
  2050  	}
  2051  }
  2052  
  2053  func TestParseNested(t *testing.T) {
  2054  	type duration struct {
  2055  		Delay time.Duration
  2056  	}
  2057  
  2058  	type item struct {
  2059  		Name   string
  2060  		Delay  time.Duration
  2061  		Nested duration
  2062  	}
  2063  
  2064  	config := `[[parent]]
  2065  	delay="100ms"
  2066  	[parent.nested]
  2067  	delay="200ms"
  2068  `
  2069  	initConfig("toml", config)
  2070  
  2071  	var items []item
  2072  	err := v.UnmarshalKey("parent", &items)
  2073  	if err != nil {
  2074  		t.Fatalf("unable to decode into struct, %v", err)
  2075  	}
  2076  
  2077  	assert.Equal(t, 1, len(items))
  2078  	assert.Equal(t, 100*time.Millisecond, items[0].Delay)
  2079  	assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
  2080  }
  2081  
  2082  func doTestCaseInsensitive(t *testing.T, typ, config string) {
  2083  	initConfig(typ, config)
  2084  	Set("RfD", true)
  2085  	assert.Equal(t, true, Get("rfd"))
  2086  	assert.Equal(t, true, Get("rFD"))
  2087  	assert.Equal(t, 1, cast.ToInt(Get("abcd")))
  2088  	assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
  2089  	assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
  2090  	assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
  2091  	assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
  2092  	assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
  2093  }
  2094  
  2095  func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) {
  2096  	watchDir, err := ioutil.TempDir("", "")
  2097  	require.Nil(t, err)
  2098  	configFile := path.Join(watchDir, "config.yaml")
  2099  	err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640)
  2100  	require.Nil(t, err)
  2101  	cleanup := func() {
  2102  		os.RemoveAll(watchDir)
  2103  	}
  2104  	v := New()
  2105  	v.SetConfigFile(configFile)
  2106  	err = v.ReadInConfig()
  2107  	require.Nil(t, err)
  2108  	require.Equal(t, "bar", v.Get("foo"))
  2109  	return v, configFile, cleanup
  2110  }
  2111  
  2112  func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) {
  2113  	watchDir, err := ioutil.TempDir("", "")
  2114  	require.Nil(t, err)
  2115  	dataDir1 := path.Join(watchDir, "data1")
  2116  	err = os.Mkdir(dataDir1, 0777)
  2117  	require.Nil(t, err)
  2118  	realConfigFile := path.Join(dataDir1, "config.yaml")
  2119  	t.Logf("Real config file location: %s\n", realConfigFile)
  2120  	err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640)
  2121  	require.Nil(t, err)
  2122  	cleanup := func() {
  2123  		os.RemoveAll(watchDir)
  2124  	}
  2125  	// now, symlink the tm `data1` dir to `data` in the baseDir
  2126  	os.Symlink(dataDir1, path.Join(watchDir, "data"))
  2127  	// and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`
  2128  	configFile := path.Join(watchDir, "config.yaml")
  2129  	os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile)
  2130  	t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml"))
  2131  	// init Viper
  2132  	v := New()
  2133  	v.SetConfigFile(configFile)
  2134  	err = v.ReadInConfig()
  2135  	require.Nil(t, err)
  2136  	require.Equal(t, "bar", v.Get("foo"))
  2137  	return v, watchDir, configFile, cleanup
  2138  }
  2139  
  2140  func TestWatchFile(t *testing.T) {
  2141  	if runtime.GOOS == "linux" {
  2142  		// TODO(bep) FIX ME
  2143  		t.Skip("Skip test on Linux ...")
  2144  	}
  2145  
  2146  	t.Run("file content changed", func(t *testing.T) {
  2147  		// given a `config.yaml` file being watched
  2148  		v, configFile, cleanup := newViperWithConfigFile(t)
  2149  		defer cleanup()
  2150  		_, err := os.Stat(configFile)
  2151  		require.NoError(t, err)
  2152  		t.Logf("test config file: %s\n", configFile)
  2153  		wg := sync.WaitGroup{}
  2154  		wg.Add(1)
  2155  		v.OnConfigChange(func(in fsnotify.Event) {
  2156  			t.Logf("config file changed")
  2157  			wg.Done()
  2158  		})
  2159  		v.WatchConfig()
  2160  		// when overwriting the file and waiting for the custom change notification handler to be triggered
  2161  		err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640)
  2162  		wg.Wait()
  2163  		// then the config value should have changed
  2164  		require.Nil(t, err)
  2165  		assert.Equal(t, "baz", v.Get("foo"))
  2166  	})
  2167  
  2168  	t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) {
  2169  		// skip if not executed on Linux
  2170  		if runtime.GOOS != "linux" {
  2171  			t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
  2172  		}
  2173  		v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t)
  2174  		// defer cleanup()
  2175  		wg := sync.WaitGroup{}
  2176  		v.WatchConfig()
  2177  		v.OnConfigChange(func(in fsnotify.Event) {
  2178  			t.Logf("config file changed")
  2179  			wg.Done()
  2180  		})
  2181  		wg.Add(1)
  2182  		// when link to another `config.yaml` file
  2183  		dataDir2 := path.Join(watchDir, "data2")
  2184  		err := os.Mkdir(dataDir2, 0777)
  2185  		require.Nil(t, err)
  2186  		configFile2 := path.Join(dataDir2, "config.yaml")
  2187  		err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640)
  2188  		require.Nil(t, err)
  2189  		// change the symlink using the `ln -sfn` command
  2190  		err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
  2191  		require.Nil(t, err)
  2192  		wg.Wait()
  2193  		// then
  2194  		require.Nil(t, err)
  2195  		assert.Equal(t, "baz", v.Get("foo"))
  2196  	})
  2197  }
  2198  
  2199  func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
  2200  	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  2201  	flags.String("foo.bar", "cobra_flag", "")
  2202  
  2203  	v := New()
  2204  	assert.NoError(t, v.BindPFlags(flags))
  2205  
  2206  	config := &struct {
  2207  		Foo struct {
  2208  			Bar string
  2209  		}
  2210  	}{}
  2211  
  2212  	assert.NoError(t, v.Unmarshal(config))
  2213  	assert.Equal(t, "cobra_flag", config.Foo.Bar)
  2214  }
  2215  
  2216  var yamlExampleWithDot = []byte(`Hacker: true
  2217  name: steve
  2218  hobbies:
  2219    - skateboarding
  2220    - snowboarding
  2221    - go
  2222  clothing:
  2223    jacket: leather
  2224    trousers: denim
  2225    pants:
  2226      size: large
  2227  age: 35
  2228  eyes : brown
  2229  beard: true
  2230  emails:
  2231    steve@hacker.com:
  2232      created: 01/02/03
  2233      active: true
  2234  `)
  2235  
  2236  func TestKeyDelimiter(t *testing.T) {
  2237  	v := NewWithOptions(KeyDelimiter("::"))
  2238  	v.SetConfigType("yaml")
  2239  	r := strings.NewReader(string(yamlExampleWithDot))
  2240  
  2241  	err := v.unmarshalReader(r, v.config)
  2242  	require.NoError(t, err)
  2243  
  2244  	values := map[string]interface{}{
  2245  		"image": map[string]interface{}{
  2246  			"repository": "someImage",
  2247  			"tag":        "1.0.0",
  2248  		},
  2249  		"ingress": map[string]interface{}{
  2250  			"annotations": map[string]interface{}{
  2251  				"traefik.frontend.rule.type":                 "PathPrefix",
  2252  				"traefik.ingress.kubernetes.io/ssl-redirect": "true",
  2253  			},
  2254  		},
  2255  	}
  2256  
  2257  	v.SetDefault("charts::values", values)
  2258  
  2259  	assert.Equal(t, "leather", v.GetString("clothing::jacket"))
  2260  	assert.Equal(t, "01/02/03", v.GetString("emails::steve@hacker.com::created"))
  2261  
  2262  	type config struct {
  2263  		Charts struct {
  2264  			Values map[string]interface{}
  2265  		}
  2266  	}
  2267  
  2268  	expected := config{
  2269  		Charts: struct {
  2270  			Values map[string]interface{}
  2271  		}{
  2272  			Values: values,
  2273  		},
  2274  	}
  2275  
  2276  	var actual config
  2277  
  2278  	assert.NoError(t, v.Unmarshal(&actual))
  2279  
  2280  	assert.Equal(t, expected, actual)
  2281  }
  2282  
  2283  var yamlDeepNestedSlices = []byte(`TV:
  2284  - title: "The expanse"
  2285    seasons:
  2286    - first_released: "December 14, 2015"
  2287      episodes:
  2288      - title: "Dulcinea"
  2289        air_date: "December 14, 2015"
  2290      - title: "The Big Empty"
  2291        air_date: "December 15, 2015"
  2292      - title: "Remember the Cant"
  2293        air_date: "December 22, 2015"
  2294    - first_released: "February 1, 2017"
  2295      episodes:
  2296      - title: "Safe"
  2297        air_date: "February 1, 2017"
  2298      - title: "Doors & Corners"
  2299        air_date: "February 1, 2017"
  2300      - title: "Static"
  2301        air_date: "February 8, 2017"
  2302    episodes:
  2303      - ["Dulcinea", "The Big Empty", "Remember the Cant"]
  2304      - ["Safe", "Doors & Corners", "Static"]
  2305  `)
  2306  
  2307  func TestSliceIndexAccess(t *testing.T) {
  2308  	v.SetConfigType("yaml")
  2309  	r := strings.NewReader(string(yamlDeepNestedSlices))
  2310  
  2311  	err := v.unmarshalReader(r, v.config)
  2312  	require.NoError(t, err)
  2313  
  2314  	assert.Equal(t, "The expanse", v.GetString("tv.0.title"))
  2315  	assert.Equal(t, "February 1, 2017", v.GetString("tv.0.seasons.1.first_released"))
  2316  	assert.Equal(t, "Static", v.GetString("tv.0.seasons.1.episodes.2.title"))
  2317  	assert.Equal(t, "December 15, 2015", v.GetString("tv.0.seasons.0.episodes.1.air_date"))
  2318  
  2319  	// Test for index out of bounds
  2320  	assert.Equal(t, "", v.GetString("tv.0.seasons.2.first_released"))
  2321  
  2322  	// Accessing multidimensional arrays
  2323  	assert.Equal(t, "Static", v.GetString("tv.0.episodes.1.2"))
  2324  }
  2325  
  2326  func BenchmarkGetBool(b *testing.B) {
  2327  	key := "BenchmarkGetBool"
  2328  	v = New()
  2329  	v.Set(key, true)
  2330  
  2331  	for i := 0; i < b.N; i++ {
  2332  		if !v.GetBool(key) {
  2333  			b.Fatal("GetBool returned false")
  2334  		}
  2335  	}
  2336  }
  2337  
  2338  func BenchmarkGet(b *testing.B) {
  2339  	key := "BenchmarkGet"
  2340  	v = New()
  2341  	v.Set(key, true)
  2342  
  2343  	for i := 0; i < b.N; i++ {
  2344  		if !v.Get(key).(bool) {
  2345  			b.Fatal("Get returned false")
  2346  		}
  2347  	}
  2348  }
  2349  
  2350  // BenchmarkGetBoolFromMap is the "perfect result" for the above.
  2351  func BenchmarkGetBoolFromMap(b *testing.B) {
  2352  	m := make(map[string]bool)
  2353  	key := "BenchmarkGetBool"
  2354  	m[key] = true
  2355  
  2356  	for i := 0; i < b.N; i++ {
  2357  		if !m[key] {
  2358  			b.Fatal("Map value was false")
  2359  		}
  2360  	}
  2361  }