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