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