...

Source file src/github.com/ory/viper/viper_test.go

Documentation: github.com/ory/viper

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

View as plain text