gitlab.dev.zniis.ru/tools/viper@v1.7.2/viper_test.go (about)

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