github.com/gottingen/lung@v0.3.0/lung_test.go (about)

     1  package lung
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"os/exec"
    10  	"path"
    11  	"path/filepath"
    12  	"reflect"
    13  	"runtime"
    14  	"sort"
    15  	"strings"
    16  	"sync"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/fsnotify/fsnotify"
    21  	"github.com/gottingen/felix"
    22  	"github.com/gottingen/gekko/cast"
    23  	"github.com/mitchellh/mapstructure"
    24  
    25  	"github.com/gottingen/gekko/gflag"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  var yamlExample = []byte(`Hacker: true
    31  name: steve
    32  hobbies:
    33  - skateboarding
    34  - snowboarding
    35  - go
    36  clothing:
    37    jacket: leather
    38    trousers: denim
    39    pants:
    40      size: large
    41  age: 35
    42  eyes : brown
    43  beard: true
    44  `)
    45  
    46  var yamlExampleWithExtras = []byte(`Existing: true
    47  Bogus: true
    48  `)
    49  
    50  type testUnmarshalExtra struct {
    51  	Existing bool
    52  }
    53  
    54  var tomlExample = []byte(`
    55  title = "TOML Example"
    56  
    57  [owner]
    58  organization = "MongoDB"
    59  Bio = "MongoDB Chief Developer Advocate & Hacker at Large"
    60  dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)
    61  
    62  var dotenvExample = []byte(`
    63  TITLE_DOTENV="DotEnv Example"
    64  TYPE_DOTENV=donut
    65  NAME_DOTENV=Cake`)
    66  
    67  var jsonExample = []byte(`{
    68  "id": "0001",
    69  "type": "donut",
    70  "name": "Cake",
    71  "ppu": 0.55,
    72  "batters": {
    73          "batter": [
    74                  { "type": "Regular" },
    75                  { "type": "Chocolate" },
    76                  { "type": "Blueberry" },
    77                  { "type": "Devil's Food" }
    78              ]
    79      }
    80  }`)
    81  
    82  var propertiesExample = []byte(`
    83  p_id: 0001
    84  p_type: donut
    85  p_name: Cake
    86  p_ppu: 0.55
    87  p_batters.batter.type: Regular
    88  `)
    89  
    90  var remoteExample = []byte(`{
    91  "id":"0002",
    92  "type":"cronut",
    93  "newkey":"remote"
    94  }`)
    95  
    96  func initConfigs() {
    97  	Reset()
    98  	var r io.Reader
    99  	SetConfigType("yaml")
   100  	r = bytes.NewReader(yamlExample)
   101  	unmarshalReader(r, l.config)
   102  
   103  	SetConfigType("json")
   104  	r = bytes.NewReader(jsonExample)
   105  	unmarshalReader(r, l.config)
   106  
   107  	SetConfigType("properties")
   108  	r = bytes.NewReader(propertiesExample)
   109  	unmarshalReader(r, l.config)
   110  
   111  	SetConfigType("toml")
   112  	r = bytes.NewReader(tomlExample)
   113  	unmarshalReader(r, l.config)
   114  
   115  	SetConfigType("env")
   116  	r = bytes.NewReader(dotenvExample)
   117  	unmarshalReader(r, l.config)
   118  
   119  	SetConfigType("json")
   120  	remote := bytes.NewReader(remoteExample)
   121  	unmarshalReader(remote, l.kvstore)
   122  }
   123  
   124  func initConfig(typ, config string) {
   125  	Reset()
   126  	SetConfigType(typ)
   127  	r := strings.NewReader(config)
   128  
   129  	if err := unmarshalReader(r, l.config); err != nil {
   130  		panic(err)
   131  	}
   132  }
   133  
   134  func initYAML() {
   135  	initConfig("yaml", string(yamlExample))
   136  }
   137  
   138  func initJSON() {
   139  	Reset()
   140  	SetConfigType("json")
   141  	r := bytes.NewReader(jsonExample)
   142  
   143  	unmarshalReader(r, l.config)
   144  }
   145  
   146  func initProperties() {
   147  	Reset()
   148  	SetConfigType("properties")
   149  	r := bytes.NewReader(propertiesExample)
   150  
   151  	unmarshalReader(r, l.config)
   152  }
   153  
   154  func initTOML() {
   155  	Reset()
   156  	SetConfigType("toml")
   157  	r := bytes.NewReader(tomlExample)
   158  
   159  	unmarshalReader(r, l.config)
   160  }
   161  
   162  func initDotEnv() {
   163  	Reset()
   164  	SetConfigType("env")
   165  	r := bytes.NewReader(dotenvExample)
   166  
   167  	unmarshalReader(r, l.config)
   168  }
   169  
   170  // make directories for testing
   171  func initDirs(t *testing.T) (string, string, func()) {
   172  
   173  	var (
   174  		testDirs = []string{`a a`, `b`, `C_`}
   175  		config   = `improbable`
   176  	)
   177  
   178  	if runtime.GOOS != "windows" {
   179  		testDirs = append(testDirs, `d\d`)
   180  	}
   181  
   182  	root, err := ioutil.TempDir("", "")
   183  	require.NoError(t, err, "Failed to create temporary directory")
   184  
   185  	cleanup := true
   186  	defer func() {
   187  		if cleanup {
   188  			os.Chdir("..")
   189  			os.RemoveAll(root)
   190  		}
   191  	}()
   192  
   193  	assert.Nil(t, err)
   194  
   195  	err = os.Chdir(root)
   196  	require.Nil(t, err)
   197  
   198  	for _, dir := range testDirs {
   199  		err = os.Mkdir(dir, 0750)
   200  		assert.Nil(t, err)
   201  
   202  		err = ioutil.WriteFile(
   203  			path.Join(dir, config+".toml"),
   204  			[]byte("key = \"value is "+dir+"\"\n"),
   205  			0640)
   206  		assert.Nil(t, err)
   207  	}
   208  
   209  	cleanup = false
   210  	return root, config, func() {
   211  		os.Chdir("..")
   212  		os.RemoveAll(root)
   213  	}
   214  }
   215  
   216  // stubs for PFlag Values
   217  type stringValue string
   218  
   219  func newStringValue(val string, p *string) *stringValue {
   220  	*p = val
   221  	return (*stringValue)(p)
   222  }
   223  
   224  func (s *stringValue) Set(val string) error {
   225  	*s = stringValue(val)
   226  	return nil
   227  }
   228  
   229  func (s *stringValue) Type() string {
   230  	return "string"
   231  }
   232  
   233  func (s *stringValue) String() string {
   234  	return string(*s)
   235  }
   236  
   237  func TestBasics(t *testing.T) {
   238  	SetConfigFile("/tmp/config.yaml")
   239  	filename, err := l.getConfigFile()
   240  	assert.Equal(t, "/tmp/config.yaml", filename)
   241  	assert.NoError(t, err)
   242  }
   243  
   244  func TestDefault(t *testing.T) {
   245  	SetDefault("age", 45)
   246  	assert.Equal(t, 45, Get("age"))
   247  
   248  	SetDefault("clothing.jacket", "slacks")
   249  	assert.Equal(t, "slacks", Get("clothing.jacket"))
   250  
   251  	SetConfigType("yaml")
   252  	err := ReadConfig(bytes.NewBuffer(yamlExample))
   253  
   254  	assert.NoError(t, err)
   255  	assert.Equal(t, "leather", Get("clothing.jacket"))
   256  }
   257  
   258  func TestUnmarshaling(t *testing.T) {
   259  	SetConfigType("yaml")
   260  	r := bytes.NewReader(yamlExample)
   261  
   262  	unmarshalReader(r, l.config)
   263  	assert.True(t, InConfig("name"))
   264  	assert.False(t, InConfig("state"))
   265  	assert.Equal(t, "steve", Get("name"))
   266  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
   267  	assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
   268  	assert.Equal(t, 35, Get("age"))
   269  }
   270  
   271  func TestUnmarshalExact(t *testing.T) {
   272  	vip := New()
   273  	target := &testUnmarshalExtra{}
   274  	vip.SetConfigType("yaml")
   275  	r := bytes.NewReader(yamlExampleWithExtras)
   276  	vip.ReadConfig(r)
   277  	err := vip.UnmarshalExact(target)
   278  	if err == nil {
   279  		t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields")
   280  	}
   281  }
   282  
   283  func TestOverrides(t *testing.T) {
   284  	Set("age", 40)
   285  	assert.Equal(t, 40, Get("age"))
   286  }
   287  
   288  func TestDefaultPost(t *testing.T) {
   289  	assert.NotEqual(t, "NYC", Get("state"))
   290  	SetDefault("state", "NYC")
   291  	assert.Equal(t, "NYC", Get("state"))
   292  }
   293  
   294  func TestAliases(t *testing.T) {
   295  	RegisterAlias("years", "age")
   296  	assert.Equal(t, 40, Get("years"))
   297  	Set("years", 45)
   298  	assert.Equal(t, 45, Get("age"))
   299  }
   300  
   301  func TestAliasInConfigFile(t *testing.T) {
   302  	// the config file specifies "beard".  If we make this an alias for
   303  	// "hasbeard", we still want the old config file to work with beard.
   304  	RegisterAlias("beard", "hasbeard")
   305  	assert.Equal(t, true, Get("hasbeard"))
   306  	Set("hasbeard", false)
   307  	assert.Equal(t, false, Get("beard"))
   308  }
   309  
   310  func TestYML(t *testing.T) {
   311  	initYAML()
   312  	assert.Equal(t, "steve", Get("name"))
   313  }
   314  
   315  func TestJSON(t *testing.T) {
   316  	initJSON()
   317  	assert.Equal(t, "0001", Get("id"))
   318  }
   319  
   320  func TestProperties(t *testing.T) {
   321  	initProperties()
   322  	assert.Equal(t, "0001", Get("p_id"))
   323  }
   324  
   325  func TestTOML(t *testing.T) {
   326  	initTOML()
   327  	assert.Equal(t, "TOML Example", Get("title"))
   328  }
   329  
   330  func TestDotEnv(t *testing.T) {
   331  	initDotEnv()
   332  	assert.Equal(t, "DotEnv Example", Get("title_dotenv"))
   333  }
   334  
   335  func TestRemotePrecedence(t *testing.T) {
   336  	initJSON()
   337  
   338  	remote := bytes.NewReader(remoteExample)
   339  	assert.Equal(t, "0001", Get("id"))
   340  	unmarshalReader(remote, l.kvstore)
   341  	assert.Equal(t, "0001", Get("id"))
   342  	assert.NotEqual(t, "cronut", Get("type"))
   343  	assert.Equal(t, "remote", Get("newkey"))
   344  	Set("newkey", "newvalue")
   345  	assert.NotEqual(t, "remote", Get("newkey"))
   346  	assert.Equal(t, "newvalue", Get("newkey"))
   347  	Set("newkey", "remote")
   348  }
   349  
   350  func TestEnv(t *testing.T) {
   351  	initJSON()
   352  
   353  	BindEnv("id")
   354  	BindEnv("f", "FOOD")
   355  
   356  	os.Setenv("ID", "13")
   357  	os.Setenv("FOOD", "apple")
   358  	os.Setenv("NAME", "crunk")
   359  
   360  	assert.Equal(t, "13", Get("id"))
   361  	assert.Equal(t, "apple", Get("f"))
   362  	assert.Equal(t, "Cake", Get("name"))
   363  
   364  	AutomaticEnv()
   365  
   366  	assert.Equal(t, "crunk", Get("name"))
   367  
   368  }
   369  
   370  func TestEmptyEnv(t *testing.T) {
   371  	initJSON()
   372  
   373  	BindEnv("type") // Empty environment variable
   374  	BindEnv("name") // Bound, but not set environment variable
   375  
   376  	os.Unsetenv("type")
   377  	os.Unsetenv("TYPE")
   378  	os.Unsetenv("name")
   379  	os.Unsetenv("NAME")
   380  
   381  	os.Setenv("TYPE", "")
   382  
   383  	assert.Equal(t, "donut", Get("type"))
   384  	assert.Equal(t, "Cake", Get("name"))
   385  }
   386  
   387  func TestEmptyEnv_Allowed(t *testing.T) {
   388  	initJSON()
   389  
   390  	AllowEmptyEnv(true)
   391  
   392  	BindEnv("type") // Empty environment variable
   393  	BindEnv("name") // Bound, but not set environment variable
   394  
   395  	os.Unsetenv("type")
   396  	os.Unsetenv("TYPE")
   397  	os.Unsetenv("name")
   398  	os.Unsetenv("NAME")
   399  
   400  	os.Setenv("TYPE", "")
   401  
   402  	assert.Equal(t, "", Get("type"))
   403  	assert.Equal(t, "Cake", Get("name"))
   404  }
   405  
   406  func TestEnvPrefix(t *testing.T) {
   407  	initJSON()
   408  
   409  	SetEnvPrefix("foo") // will be uppercased automatically
   410  	BindEnv("id")
   411  	BindEnv("f", "FOOD") // not using prefix
   412  
   413  	os.Setenv("FOO_ID", "13")
   414  	os.Setenv("FOOD", "apple")
   415  	os.Setenv("FOO_NAME", "crunk")
   416  
   417  	assert.Equal(t, "13", Get("id"))
   418  	assert.Equal(t, "apple", Get("f"))
   419  	assert.Equal(t, "Cake", Get("name"))
   420  
   421  	AutomaticEnv()
   422  
   423  	assert.Equal(t, "crunk", Get("name"))
   424  }
   425  
   426  func TestAutoEnv(t *testing.T) {
   427  	Reset()
   428  
   429  	AutomaticEnv()
   430  	os.Setenv("FOO_BAR", "13")
   431  	assert.Equal(t, "13", Get("foo_bar"))
   432  }
   433  
   434  func TestAutoEnvWithPrefix(t *testing.T) {
   435  	Reset()
   436  
   437  	AutomaticEnv()
   438  	SetEnvPrefix("Baz")
   439  	os.Setenv("BAZ_BAR", "13")
   440  	assert.Equal(t, "13", Get("bar"))
   441  }
   442  
   443  func TestSetEnvKeyReplacer(t *testing.T) {
   444  	Reset()
   445  
   446  	AutomaticEnv()
   447  	os.Setenv("REFRESH_INTERVAL", "30s")
   448  
   449  	replacer := strings.NewReplacer("-", "_")
   450  	SetEnvKeyReplacer(replacer)
   451  
   452  	assert.Equal(t, "30s", Get("refresh-interval"))
   453  }
   454  
   455  func TestAllKeys(t *testing.T) {
   456  	initConfigs()
   457  
   458  	ks := sort.StringSlice{
   459  		"title",
   460  		"newkey",
   461  		"owner.organization",
   462  		"owner.dob",
   463  		"owner.bio",
   464  		"name",
   465  		"beard",
   466  		"ppu",
   467  		"batters.batter",
   468  		"hobbies",
   469  		"clothing.jacket",
   470  		"clothing.trousers",
   471  		"clothing.pants.size",
   472  		"age",
   473  		"hacker",
   474  		"id",
   475  		"type",
   476  		"eyes",
   477  		"p_id",
   478  		"p_ppu",
   479  		"p_batters.batter.type",
   480  		"p_type",
   481  		"p_name",
   482  		"title_dotenv",
   483  		"type_dotenv",
   484  		"name_dotenv",
   485  	}
   486  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
   487  	all := map[string]interface{}{
   488  		"owner": map[string]interface{}{
   489  			"organization": "MongoDB",
   490  			"bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
   491  			"dob":          dob,
   492  		},
   493  		"title": "TOML Example",
   494  		"ppu":   0.55,
   495  		"eyes":  "brown",
   496  		"clothing": map[string]interface{}{
   497  			"trousers": "denim",
   498  			"jacket":   "leather",
   499  			"pants":    map[string]interface{}{"size": "large"},
   500  		},
   501  		"id": "0001",
   502  		"batters": map[string]interface{}{
   503  			"batter": []interface{}{
   504  				map[string]interface{}{"type": "Regular"},
   505  				map[string]interface{}{"type": "Chocolate"},
   506  				map[string]interface{}{"type": "Blueberry"},
   507  				map[string]interface{}{"type": "Devil's Food"},
   508  			},
   509  		},
   510  		"hacker": true,
   511  		"beard":  true,
   512  		"hobbies": []interface{}{
   513  			"skateboarding",
   514  			"snowboarding",
   515  			"go",
   516  		},
   517  		"age":    35,
   518  		"type":   "donut",
   519  		"newkey": "remote",
   520  		"name":   "Cake",
   521  		"p_id":   "0001",
   522  		"p_ppu":  "0.55",
   523  		"p_name": "Cake",
   524  		"p_batters": map[string]interface{}{
   525  			"batter": map[string]interface{}{"type": "Regular"},
   526  		},
   527  		"p_type": "donut",
   528  		"title_dotenv": "DotEnv Example",
   529  		"type_dotenv":  "donut",
   530  		"name_dotenv":  "Cake",
   531  	}
   532  
   533  	allkeys := sort.StringSlice(AllKeys())
   534  	allkeys.Sort()
   535  	ks.Sort()
   536  
   537  	assert.Equal(t, ks, allkeys)
   538  	assert.Equal(t, all, AllSettings())
   539  }
   540  
   541  func TestAllKeysWithEnv(t *testing.T) {
   542  	v := New()
   543  
   544  	// bind and define environment variables (including a nested one)
   545  	v.BindEnv("id")
   546  	v.BindEnv("foo.bar")
   547  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
   548  	os.Setenv("ID", "13")
   549  	os.Setenv("FOO_BAR", "baz")
   550  
   551  	expectedKeys := sort.StringSlice{"id", "foo.bar"}
   552  	expectedKeys.Sort()
   553  	keys := sort.StringSlice(v.AllKeys())
   554  	keys.Sort()
   555  	assert.Equal(t, expectedKeys, keys)
   556  }
   557  
   558  func TestAliasesOfAliases(t *testing.T) {
   559  	Set("Title", "Checking Case")
   560  	RegisterAlias("Foo", "Bar")
   561  	RegisterAlias("Bar", "Title")
   562  	assert.Equal(t, "Checking Case", Get("FOO"))
   563  }
   564  
   565  func TestRecursiveAliases(t *testing.T) {
   566  	RegisterAlias("Baz", "Roo")
   567  	RegisterAlias("Roo", "baz")
   568  }
   569  
   570  func TestUnmarshal(t *testing.T) {
   571  	SetDefault("port", 1313)
   572  	Set("name", "Steve")
   573  	Set("duration", "1s1ms")
   574  	Set("modes", []int{1, 2, 3})
   575  
   576  	type config struct {
   577  		Port     int
   578  		Name     string
   579  		Duration time.Duration
   580  		Modes    []int
   581  	}
   582  
   583  	var C config
   584  
   585  	err := Unmarshal(&C)
   586  	if err != nil {
   587  		t.Fatalf("unable to decode into struct, %v", err)
   588  	}
   589  
   590  	assert.Equal(
   591  		t,
   592  		&config{
   593  			Name:     "Steve",
   594  			Port:     1313,
   595  			Duration: time.Second + time.Millisecond,
   596  			Modes:    []int{1, 2, 3},
   597  		},
   598  		&C,
   599  	)
   600  
   601  	Set("port", 1234)
   602  	err = Unmarshal(&C)
   603  	if err != nil {
   604  		t.Fatalf("unable to decode into struct, %v", err)
   605  	}
   606  
   607  	assert.Equal(
   608  		t,
   609  		&config{
   610  			Name:     "Steve",
   611  			Port:     1234,
   612  			Duration: time.Second + time.Millisecond,
   613  			Modes:    []int{1, 2, 3},
   614  		},
   615  		&C,
   616  	)
   617  }
   618  
   619  func TestUnmarshalWithDecoderOptions(t *testing.T) {
   620  	Set("credentials", "{\"foo\":\"bar\"}")
   621  
   622  	opt := DecodeHook(mapstructure.ComposeDecodeHookFunc(
   623  		mapstructure.StringToTimeDurationHookFunc(),
   624  		mapstructure.StringToSliceHookFunc(","),
   625  		// Custom Decode Hook Function
   626  		func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) {
   627  			if rf != reflect.String || rt != reflect.Map {
   628  				return data, nil
   629  			}
   630  			m := map[string]string{}
   631  			raw := data.(string)
   632  			if raw == "" {
   633  				return m, nil
   634  			}
   635  			return m, json.Unmarshal([]byte(raw), &m)
   636  		},
   637  	))
   638  
   639  	type config struct {
   640  		Credentials map[string]string
   641  	}
   642  
   643  	var C config
   644  
   645  	err := Unmarshal(&C, opt)
   646  	if err != nil {
   647  		t.Fatalf("unable to decode into struct, %v", err)
   648  	}
   649  
   650  	assert.Equal(t, &config{
   651  		Credentials: map[string]string{"foo": "bar"},
   652  	}, &C)
   653  }
   654  
   655  func TestBindPFlags(t *testing.T) {
   656  	v := New() // create independent Lung object
   657  	flagSet := gflag.NewFlagSet("test", gflag.ContinueOnError)
   658  
   659  	var testValues = map[string]*string{
   660  		"host":     nil,
   661  		"port":     nil,
   662  		"endpoint": nil,
   663  	}
   664  
   665  	var mutatedTestValues = map[string]string{
   666  		"host":     "localhost",
   667  		"port":     "6060",
   668  		"endpoint": "/public",
   669  	}
   670  
   671  	for name := range testValues {
   672  		testValues[name] = flagSet.String(name, "", "test")
   673  	}
   674  
   675  	err := v.BindPFlags(flagSet)
   676  	if err != nil {
   677  		t.Fatalf("error binding flag set, %v", err)
   678  	}
   679  
   680  	flagSet.VisitAll(func(flag *gflag.Flag) {
   681  		flag.Value.Set(mutatedTestValues[flag.Name])
   682  		flag.Changed = true
   683  	})
   684  
   685  	for name, expected := range mutatedTestValues {
   686  		assert.Equal(t, expected, v.Get(name))
   687  	}
   688  
   689  }
   690  
   691  func TestBindPFlagsStringSlice(t *testing.T) {
   692  	tests := []struct {
   693  		Expected []string
   694  		Value    string
   695  	}{
   696  		{nil, ""},
   697  		{[]string{"jeden"}, "jeden"},
   698  		{[]string{"dwa", "trzy"}, "dwa,trzy"},
   699  		{[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""},
   700  	}
   701  
   702  	v := New() // create independent Lung object
   703  	defaultVal := []string{"default"}
   704  	v.SetDefault("stringslice", defaultVal)
   705  
   706  	for _, testValue := range tests {
   707  		flagSet := gflag.NewFlagSet("test", gflag.ContinueOnError)
   708  		flagSet.StringSlice("stringslice", testValue.Expected, "test")
   709  
   710  		for _, changed := range []bool{true, false} {
   711  			flagSet.VisitAll(func(f *gflag.Flag) {
   712  				f.Value.Set(testValue.Value)
   713  				f.Changed = changed
   714  			})
   715  
   716  			err := v.BindPFlags(flagSet)
   717  			if err != nil {
   718  				t.Fatalf("error binding flag set, %v", err)
   719  			}
   720  
   721  			type TestStr struct {
   722  				StringSlice []string
   723  			}
   724  			val := &TestStr{}
   725  			if err := v.Unmarshal(val); err != nil {
   726  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
   727  			}
   728  			if changed {
   729  				assert.Equal(t, testValue.Expected, val.StringSlice)
   730  			} else {
   731  				assert.Equal(t, defaultVal, val.StringSlice)
   732  			}
   733  		}
   734  	}
   735  }
   736  
   737  func TestBindPFlagsIntSlice(t *testing.T) {
   738  	tests := []struct {
   739  		Expected []int
   740  		Value    string
   741  	}{
   742  		{nil, ""},
   743  		{[]int{1}, "1"},
   744  		{[]int{2, 3}, "2,3"},
   745  	}
   746  
   747  	v := New() // create independent Lung object
   748  	defaultVal := []int{0}
   749  	v.SetDefault("intslice", defaultVal)
   750  
   751  	for _, testValue := range tests {
   752  		flagSet := gflag.NewFlagSet("test", gflag.ContinueOnError)
   753  		flagSet.IntSlice("intslice", testValue.Expected, "test")
   754  
   755  		for _, changed := range []bool{true, false} {
   756  			flagSet.VisitAll(func(f *gflag.Flag) {
   757  				f.Value.Set(testValue.Value)
   758  				f.Changed = changed
   759  			})
   760  
   761  			err := v.BindPFlags(flagSet)
   762  			if err != nil {
   763  				t.Fatalf("error binding flag set, %v", err)
   764  			}
   765  
   766  			type TestInt struct {
   767  				IntSlice []int
   768  			}
   769  			val := &TestInt{}
   770  			if err := v.Unmarshal(val); err != nil {
   771  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
   772  			}
   773  			if changed {
   774  				assert.Equal(t, testValue.Expected, val.IntSlice)
   775  			} else {
   776  				assert.Equal(t, defaultVal, val.IntSlice)
   777  			}
   778  		}
   779  	}
   780  }
   781  
   782  func TestBindPFlag(t *testing.T) {
   783  	var testString = "testing"
   784  	var testValue = newStringValue(testString, &testString)
   785  
   786  	flag := &gflag.Flag{
   787  		Name:    "testflag",
   788  		Value:   testValue,
   789  		Changed: false,
   790  	}
   791  
   792  	BindPFlag("testvalue", flag)
   793  
   794  	assert.Equal(t, testString, Get("testvalue"))
   795  
   796  	flag.Value.Set("testing_mutate")
   797  	flag.Changed = true // hack for gflag usage
   798  
   799  	assert.Equal(t, "testing_mutate", Get("testvalue"))
   800  
   801  }
   802  
   803  func TestBoundCaseSensitivity(t *testing.T) {
   804  	assert.Equal(t, "brown", Get("eyes"))
   805  
   806  	BindEnv("eYEs", "TURTLE_EYES")
   807  	os.Setenv("TURTLE_EYES", "blue")
   808  
   809  	assert.Equal(t, "blue", Get("eyes"))
   810  
   811  	var testString = "green"
   812  	var testValue = newStringValue(testString, &testString)
   813  
   814  	flag := &gflag.Flag{
   815  		Name:    "eyeballs",
   816  		Value:   testValue,
   817  		Changed: true,
   818  	}
   819  
   820  	BindPFlag("eYEs", flag)
   821  	assert.Equal(t, "green", Get("eyes"))
   822  
   823  }
   824  
   825  func TestSizeInBytes(t *testing.T) {
   826  	input := map[string]uint{
   827  		"":               0,
   828  		"b":              0,
   829  		"12 bytes":       0,
   830  		"200000000000gb": 0,
   831  		"12 b":           12,
   832  		"43 MB":          43 * (1 << 20),
   833  		"10mb":           10 * (1 << 20),
   834  		"1gb":            1 << 30,
   835  	}
   836  
   837  	for str, expected := range input {
   838  		assert.Equal(t, expected, parseSizeInBytes(str), str)
   839  	}
   840  }
   841  
   842  func TestFindsNestedKeys(t *testing.T) {
   843  	initConfigs()
   844  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
   845  
   846  	Set("super", map[string]interface{}{
   847  		"deep": map[string]interface{}{
   848  			"nested": "value",
   849  		},
   850  	})
   851  
   852  	expected := map[string]interface{}{
   853  		"super": map[string]interface{}{
   854  			"deep": map[string]interface{}{
   855  				"nested": "value",
   856  			},
   857  		},
   858  		"super.deep": map[string]interface{}{
   859  			"nested": "value",
   860  		},
   861  		"super.deep.nested":  "value",
   862  		"owner.organization": "MongoDB",
   863  		"batters.batter": []interface{}{
   864  			map[string]interface{}{
   865  				"type": "Regular",
   866  			},
   867  			map[string]interface{}{
   868  				"type": "Chocolate",
   869  			},
   870  			map[string]interface{}{
   871  				"type": "Blueberry",
   872  			},
   873  			map[string]interface{}{
   874  				"type": "Devil's Food",
   875  			},
   876  		},
   877  		"hobbies": []interface{}{
   878  			"skateboarding", "snowboarding", "go",
   879  		},
   880  		"TITLE_DOTENV": "DotEnv Example",
   881  		"TYPE_DOTENV":  "donut",
   882  		"NAME_DOTENV":  "Cake",
   883  		"title":        "TOML Example",
   884  		"newkey":       "remote",
   885  		"batters": map[string]interface{}{
   886  			"batter": []interface{}{
   887  				map[string]interface{}{
   888  					"type": "Regular",
   889  				},
   890  				map[string]interface{}{
   891  					"type": "Chocolate",
   892  				}, map[string]interface{}{
   893  					"type": "Blueberry",
   894  				}, map[string]interface{}{
   895  					"type": "Devil's Food",
   896  				},
   897  			},
   898  		},
   899  		"eyes": "brown",
   900  		"age":  35,
   901  		"owner": map[string]interface{}{
   902  			"organization": "MongoDB",
   903  			"bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
   904  			"dob":          dob,
   905  		},
   906  		"owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large",
   907  		"type":      "donut",
   908  		"id":        "0001",
   909  		"name":      "Cake",
   910  		"hacker":    true,
   911  		"ppu":       0.55,
   912  		"clothing": map[string]interface{}{
   913  			"jacket":   "leather",
   914  			"trousers": "denim",
   915  			"pants": map[string]interface{}{
   916  				"size": "large",
   917  			},
   918  		},
   919  		"clothing.jacket":     "leather",
   920  		"clothing.pants.size": "large",
   921  		"clothing.trousers":   "denim",
   922  		"owner.dob":           dob,
   923  		"beard":               true,
   924  	}
   925  
   926  	for key, expectedValue := range expected {
   927  
   928  		assert.Equal(t, expectedValue, l.Get(key))
   929  	}
   930  
   931  }
   932  
   933  func TestReadBufConfig(t *testing.T) {
   934  	v := New()
   935  	v.SetConfigType("yaml")
   936  	v.ReadConfig(bytes.NewBuffer(yamlExample))
   937  	t.Log(v.AllKeys())
   938  
   939  	assert.True(t, v.InConfig("name"))
   940  	assert.False(t, v.InConfig("state"))
   941  	assert.Equal(t, "steve", v.Get("name"))
   942  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
   943  	assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
   944  	assert.Equal(t, 35, v.Get("age"))
   945  }
   946  
   947  func TestIsSet(t *testing.T) {
   948  	v := New()
   949  	v.SetConfigType("yaml")
   950  	v.ReadConfig(bytes.NewBuffer(yamlExample))
   951  	assert.True(t, v.IsSet("clothing.jacket"))
   952  	assert.False(t, v.IsSet("clothing.jackets"))
   953  	assert.False(t, v.IsSet("helloworld"))
   954  	v.Set("helloworld", "fubar")
   955  	assert.True(t, v.IsSet("helloworld"))
   956  }
   957  
   958  func TestDirsSearch(t *testing.T) {
   959  
   960  	root, config, cleanup := initDirs(t)
   961  	defer cleanup()
   962  
   963  	v := New()
   964  	v.SetConfigName(config)
   965  	v.SetDefault(`key`, `default`)
   966  
   967  	entries, err := ioutil.ReadDir(root)
   968  	assert.Nil(t, err)
   969  	for _, e := range entries {
   970  		if e.IsDir() {
   971  			v.AddConfigPath(e.Name())
   972  		}
   973  	}
   974  
   975  	err = v.ReadInConfig()
   976  	assert.Nil(t, err)
   977  
   978  	assert.Equal(t, `value is `+filepath.Base(v.configPaths[0]), v.GetString(`key`))
   979  }
   980  
   981  func TestWrongDirsSearchNotFound(t *testing.T) {
   982  
   983  	_, config, cleanup := initDirs(t)
   984  	defer cleanup()
   985  
   986  	v := New()
   987  	v.SetConfigName(config)
   988  	v.SetDefault(`key`, `default`)
   989  
   990  	v.AddConfigPath(`whattayoutalkingbout`)
   991  	v.AddConfigPath(`thispathaintthere`)
   992  
   993  	err := v.ReadInConfig()
   994  	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
   995  
   996  	// Even though config did not load and the error might have
   997  	// been ignored by the client, the default still loads
   998  	assert.Equal(t, `default`, v.GetString(`key`))
   999  }
  1000  
  1001  func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
  1002  
  1003  	_, config, cleanup := initDirs(t)
  1004  	defer cleanup()
  1005  
  1006  	v := New()
  1007  	v.SetConfigName(config)
  1008  	v.SetDefault(`key`, `default`)
  1009  
  1010  	v.AddConfigPath(`whattayoutalkingbout`)
  1011  	v.AddConfigPath(`thispathaintthere`)
  1012  
  1013  	err := v.MergeInConfig()
  1014  	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
  1015  
  1016  	// Even though config did not load and the error might have
  1017  	// been ignored by the client, the default still loads
  1018  	assert.Equal(t, `default`, v.GetString(`key`))
  1019  }
  1020  
  1021  func TestSub(t *testing.T) {
  1022  	v := New()
  1023  	v.SetConfigType("yaml")
  1024  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1025  
  1026  	subv := v.Sub("clothing")
  1027  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
  1028  
  1029  	subv = v.Sub("clothing.pants")
  1030  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size"))
  1031  
  1032  	subv = v.Sub("clothing.pants.size")
  1033  	assert.Equal(t, (*Lung)(nil), subv)
  1034  
  1035  	subv = v.Sub("missing.key")
  1036  	assert.Equal(t, (*Lung)(nil), subv)
  1037  }
  1038  
  1039  var jsonWriteExpected = []byte(`{
  1040    "batters": {
  1041      "batter": [
  1042        {
  1043          "type": "Regular"
  1044        },
  1045        {
  1046          "type": "Chocolate"
  1047        },
  1048        {
  1049          "type": "Blueberry"
  1050        },
  1051        {
  1052          "type": "Devil's Food"
  1053        }
  1054      ]
  1055    },
  1056    "id": "0001",
  1057    "name": "Cake",
  1058    "ppu": 0.55,
  1059    "type": "donut"
  1060  }`)
  1061  
  1062  func TestWriteConfigJson(t *testing.T) {
  1063  	v := New()
  1064  	fs := felix.NewMemVfs()
  1065  	v.SetFs(fs)
  1066  	v.SetConfigName("c")
  1067  	v.SetConfigType("json")
  1068  	err := v.ReadConfig(bytes.NewBuffer(jsonExample))
  1069  	if err != nil {
  1070  		t.Fatal(err)
  1071  	}
  1072  	if err := v.WriteConfigAs("c.json"); err != nil {
  1073  		t.Fatal(err)
  1074  	}
  1075  	read, err := fs.ReadFile("c.json")
  1076  	if err != nil {
  1077  		t.Fatal(err)
  1078  	}
  1079  	assert.Equal(t, jsonWriteExpected, read)
  1080  }
  1081  
  1082  var propertiesWriteExpected = []byte(`p_id = 0001
  1083  p_type = donut
  1084  p_name = Cake
  1085  p_ppu = 0.55
  1086  p_batters.batter.type = Regular
  1087  `)
  1088  
  1089  func TestWriteConfigProperties(t *testing.T) {
  1090  	v := New()
  1091  	fs := felix.NewMemVfs()
  1092  	v.SetFs(fs)
  1093  	v.SetConfigName("c")
  1094  	v.SetConfigType("properties")
  1095  	err := v.ReadConfig(bytes.NewBuffer(propertiesExample))
  1096  	if err != nil {
  1097  		t.Fatal(err)
  1098  	}
  1099  	if err := v.WriteConfigAs("c.properties"); err != nil {
  1100  		t.Fatal(err)
  1101  	}
  1102  	read, err := fs.ReadFile("c.properties")
  1103  	if err != nil {
  1104  		t.Fatal(err)
  1105  	}
  1106  	assert.Equal(t, propertiesWriteExpected, read)
  1107  }
  1108  
  1109  func TestWriteConfigTOML(t *testing.T) {
  1110  	fs := felix.NewMemVfs()
  1111  	v := New()
  1112  	v.SetFs(fs)
  1113  	v.SetConfigName("c")
  1114  	v.SetConfigType("toml")
  1115  	err := v.ReadConfig(bytes.NewBuffer(tomlExample))
  1116  	if err != nil {
  1117  		t.Fatal(err)
  1118  	}
  1119  	if err := v.WriteConfigAs("c.toml"); err != nil {
  1120  		t.Fatal(err)
  1121  	}
  1122  
  1123  	// The TOML String method does not order the contents.
  1124  	// Therefore, we must read the generated file and compare the data.
  1125  	v2 := New()
  1126  	v2.SetFs(fs)
  1127  	v2.SetConfigName("c")
  1128  	v2.SetConfigType("toml")
  1129  	v2.SetConfigFile("c.toml")
  1130  	err = v2.ReadInConfig()
  1131  	if err != nil {
  1132  		t.Fatal(err)
  1133  	}
  1134  
  1135  	assert.Equal(t, v.GetString("title"), v2.GetString("title"))
  1136  	assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio"))
  1137  	assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob"))
  1138  	assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization"))
  1139  }
  1140  
  1141  var dotenvWriteExpected = []byte(`
  1142  TITLE="DotEnv Write Example"
  1143  NAME=Oreo
  1144  KIND=Biscuit
  1145  `)
  1146  
  1147  func TestWriteConfigDotEnv(t *testing.T) {
  1148  	fs := felix.NewMemVfs()
  1149  	v := New()
  1150  	v.SetFs(fs)
  1151  	v.SetConfigName("c")
  1152  	v.SetConfigType("env")
  1153  	err := v.ReadConfig(bytes.NewBuffer(dotenvWriteExpected))
  1154  	if err != nil {
  1155  		t.Fatal(err)
  1156  	}
  1157  	if err := v.WriteConfigAs("c.env"); err != nil {
  1158  		t.Fatal(err)
  1159  	}
  1160  
  1161  	// The TOML String method does not order the contents.
  1162  	// Therefore, we must read the generated file and compare the data.
  1163  	v2 := New()
  1164  	v2.SetFs(fs)
  1165  	v2.SetConfigName("c")
  1166  	v2.SetConfigType("env")
  1167  	v2.SetConfigFile("c.env")
  1168  	err = v2.ReadInConfig()
  1169  	if err != nil {
  1170  		t.Fatal(err)
  1171  	}
  1172  
  1173  	assert.Equal(t, v.GetString("title"), v2.GetString("title"))
  1174  	assert.Equal(t, v.GetString("type"), v2.GetString("type"))
  1175  	assert.Equal(t, v.GetString("kind"), v2.GetString("kind"))
  1176  }
  1177  
  1178  var yamlWriteExpected = []byte(`age: 35
  1179  beard: true
  1180  clothing:
  1181    jacket: leather
  1182    pants:
  1183      size: large
  1184    trousers: denim
  1185  eyes: brown
  1186  hacker: true
  1187  hobbies:
  1188  - skateboarding
  1189  - snowboarding
  1190  - go
  1191  name: steve
  1192  `)
  1193  
  1194  func TestWriteConfigYAML(t *testing.T) {
  1195  	v := New()
  1196  	fs := felix.NewMemVfs()
  1197  	v.SetFs(fs)
  1198  	v.SetConfigName("c")
  1199  	v.SetConfigType("yaml")
  1200  	err := v.ReadConfig(bytes.NewBuffer(yamlExample))
  1201  	if err != nil {
  1202  		t.Fatal(err)
  1203  	}
  1204  	if err := v.WriteConfigAs("c.yaml"); err != nil {
  1205  		t.Fatal(err)
  1206  	}
  1207  	read, err := fs.ReadFile("c.yaml")
  1208  	if err != nil {
  1209  		t.Fatal(err)
  1210  	}
  1211  	assert.Equal(t, yamlWriteExpected, read)
  1212  }
  1213  
  1214  var yamlMergeExampleTgt = []byte(`
  1215  hello:
  1216      pop: 37890
  1217      largenum: 765432101234567
  1218      num2pow63: 9223372036854775808
  1219      world:
  1220      - us
  1221      - uk
  1222      - fr
  1223      - de
  1224  `)
  1225  
  1226  var yamlMergeExampleSrc = []byte(`
  1227  hello:
  1228      pop: 45000
  1229      largenum: 7654321001234567
  1230      universe:
  1231      - mw
  1232      - ad
  1233      ints:
  1234      - 1
  1235      - 2
  1236  fu: bar
  1237  `)
  1238  
  1239  func TestMergeConfig(t *testing.T) {
  1240  	v := New()
  1241  	v.SetConfigType("yml")
  1242  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1243  		t.Fatal(err)
  1244  	}
  1245  
  1246  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  1247  		t.Fatalf("pop != 37890, = %d", pop)
  1248  	}
  1249  
  1250  	if pop := v.GetInt32("hello.pop"); pop != int32(37890) {
  1251  		t.Fatalf("pop != 37890, = %d", pop)
  1252  	}
  1253  
  1254  	if pop := v.GetInt64("hello.largenum"); pop != int64(765432101234567) {
  1255  		t.Fatalf("int64 largenum != 765432101234567, = %d", pop)
  1256  	}
  1257  
  1258  	if pop := v.GetUint("hello.pop"); pop != 37890 {
  1259  		t.Fatalf("uint pop != 37890, = %d", pop)
  1260  	}
  1261  
  1262  	if pop := v.GetUint32("hello.pop"); pop != 37890 {
  1263  		t.Fatalf("uint32 pop != 37890, = %d", pop)
  1264  	}
  1265  
  1266  	if pop := v.GetUint64("hello.num2pow63"); pop != 9223372036854775808 {
  1267  		t.Fatalf("uint64 num2pow63 != 9223372036854775808, = %d", pop)
  1268  	}
  1269  
  1270  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1271  		t.Fatalf("len(world) != 4, = %d", len(world))
  1272  	}
  1273  
  1274  	if fu := v.GetString("fu"); fu != "" {
  1275  		t.Fatalf("fu != \"\", = %s", fu)
  1276  	}
  1277  
  1278  	if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  1279  		t.Fatal(err)
  1280  	}
  1281  
  1282  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  1283  		t.Fatalf("pop != 45000, = %d", pop)
  1284  	}
  1285  
  1286  	if pop := v.GetInt32("hello.pop"); pop != int32(45000) {
  1287  		t.Fatalf("pop != 45000, = %d", pop)
  1288  	}
  1289  
  1290  	if pop := v.GetInt64("hello.largenum"); pop != int64(7654321001234567) {
  1291  		t.Fatalf("int64 largenum != 7654321001234567, = %d", pop)
  1292  	}
  1293  
  1294  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1295  		t.Fatalf("len(world) != 4, = %d", len(world))
  1296  	}
  1297  
  1298  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  1299  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  1300  	}
  1301  
  1302  	if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
  1303  		t.Fatalf("len(ints) != 2, = %d", len(ints))
  1304  	}
  1305  
  1306  	if fu := v.GetString("fu"); fu != "bar" {
  1307  		t.Fatalf("fu != \"bar\", = %s", fu)
  1308  	}
  1309  }
  1310  
  1311  func TestMergeConfigNoMerge(t *testing.T) {
  1312  	v := New()
  1313  	v.SetConfigType("yml")
  1314  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1315  		t.Fatal(err)
  1316  	}
  1317  
  1318  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  1319  		t.Fatalf("pop != 37890, = %d", pop)
  1320  	}
  1321  
  1322  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  1323  		t.Fatalf("len(world) != 4, = %d", len(world))
  1324  	}
  1325  
  1326  	if fu := v.GetString("fu"); fu != "" {
  1327  		t.Fatalf("fu != \"\", = %s", fu)
  1328  	}
  1329  
  1330  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  1331  		t.Fatal(err)
  1332  	}
  1333  
  1334  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  1335  		t.Fatalf("pop != 45000, = %d", pop)
  1336  	}
  1337  
  1338  	if world := v.GetStringSlice("hello.world"); len(world) != 0 {
  1339  		t.Fatalf("len(world) != 0, = %d", len(world))
  1340  	}
  1341  
  1342  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  1343  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  1344  	}
  1345  
  1346  	if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
  1347  		t.Fatalf("len(ints) != 2, = %d", len(ints))
  1348  	}
  1349  
  1350  	if fu := v.GetString("fu"); fu != "bar" {
  1351  		t.Fatalf("fu != \"bar\", = %s", fu)
  1352  	}
  1353  }
  1354  
  1355  func TestMergeConfigMap(t *testing.T) {
  1356  	v := New()
  1357  	v.SetConfigType("yml")
  1358  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1359  		t.Fatal(err)
  1360  	}
  1361  
  1362  	assert := func(i int) {
  1363  		large := v.GetInt64("hello.largenum")
  1364  		pop := v.GetInt("hello.pop")
  1365  		if large != 765432101234567 {
  1366  			t.Fatal("Got large num:", large)
  1367  		}
  1368  
  1369  		if pop != i {
  1370  			t.Fatal("Got pop:", pop)
  1371  		}
  1372  	}
  1373  
  1374  	assert(37890)
  1375  
  1376  	update := map[string]interface{}{
  1377  		"Hello": map[string]interface{}{
  1378  			"Pop": 1234,
  1379  		},
  1380  		"World": map[interface{}]interface{}{
  1381  			"Rock": 345,
  1382  		},
  1383  	}
  1384  
  1385  	if err := v.MergeConfigMap(update); err != nil {
  1386  		t.Fatal(err)
  1387  	}
  1388  
  1389  	if rock := v.GetInt("world.rock"); rock != 345 {
  1390  		t.Fatal("Got rock:", rock)
  1391  	}
  1392  
  1393  	assert(1234)
  1394  
  1395  }
  1396  
  1397  func TestUnmarshalingWithAliases(t *testing.T) {
  1398  	v := New()
  1399  	v.SetDefault("ID", 1)
  1400  	v.Set("name", "Steve")
  1401  	v.Set("lastname", "Owen")
  1402  
  1403  	v.RegisterAlias("UserID", "ID")
  1404  	v.RegisterAlias("Firstname", "name")
  1405  	v.RegisterAlias("Surname", "lastname")
  1406  
  1407  	type config struct {
  1408  		ID        int
  1409  		FirstName string
  1410  		Surname   string
  1411  	}
  1412  
  1413  	var C config
  1414  	err := v.Unmarshal(&C)
  1415  	if err != nil {
  1416  		t.Fatalf("unable to decode into struct, %v", err)
  1417  	}
  1418  
  1419  	assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C)
  1420  }
  1421  
  1422  func TestSetConfigNameClearsFileCache(t *testing.T) {
  1423  	SetConfigFile("/tmp/config.yaml")
  1424  	SetConfigName("default")
  1425  	f, err := l.getConfigFile()
  1426  	if err == nil {
  1427  		t.Fatalf("config file cache should have been cleared")
  1428  	}
  1429  	assert.Empty(t, f)
  1430  }
  1431  
  1432  func TestShadowedNestedValue(t *testing.T) {
  1433  
  1434  	config := `name: steve
  1435  clothing:
  1436    jacket: leather
  1437    trousers: denim
  1438    pants:
  1439      size: large
  1440  `
  1441  	initConfig("yaml", config)
  1442  
  1443  	assert.Equal(t, "steve", GetString("name"))
  1444  
  1445  	polyester := "polyester"
  1446  	SetDefault("clothing.shirt", polyester)
  1447  	SetDefault("clothing.jacket.price", 100)
  1448  
  1449  	assert.Equal(t, "leather", GetString("clothing.jacket"))
  1450  	assert.Nil(t, Get("clothing.jacket.price"))
  1451  	assert.Equal(t, polyester, GetString("clothing.shirt"))
  1452  
  1453  	clothingSettings := AllSettings()["clothing"].(map[string]interface{})
  1454  	assert.Equal(t, "leather", clothingSettings["jacket"])
  1455  	assert.Equal(t, polyester, clothingSettings["shirt"])
  1456  }
  1457  
  1458  func TestDotParameter(t *testing.T) {
  1459  	initJSON()
  1460  	// shoud take precedence over batters defined in jsonExample
  1461  	r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
  1462  	unmarshalReader(r, l.config)
  1463  
  1464  	actual := Get("batters.batter")
  1465  	expected := []interface{}{map[string]interface{}{"type": "Small"}}
  1466  	assert.Equal(t, expected, actual)
  1467  }
  1468  
  1469  func TestCaseInsensitive(t *testing.T) {
  1470  	for _, config := range []struct {
  1471  		typ     string
  1472  		content string
  1473  	}{
  1474  		{"yaml", `
  1475  aBcD: 1
  1476  eF:
  1477    gH: 2
  1478    iJk: 3
  1479    Lm:
  1480      nO: 4
  1481      P:
  1482        Q: 5
  1483        R: 6
  1484  `},
  1485  		{"json", `{
  1486    "aBcD": 1,
  1487    "eF": {
  1488      "iJk": 3,
  1489      "Lm": {
  1490        "P": {
  1491          "Q": 5,
  1492          "R": 6
  1493        },
  1494        "nO": 4
  1495      },
  1496      "gH": 2
  1497    }
  1498  }`},
  1499  		{"toml", `aBcD = 1
  1500  [eF]
  1501  gH = 2
  1502  iJk = 3
  1503  [eF.Lm]
  1504  nO = 4
  1505  [eF.Lm.P]
  1506  Q = 5
  1507  R = 6
  1508  `},
  1509  	} {
  1510  		doTestCaseInsensitive(t, config.typ, config.content)
  1511  	}
  1512  }
  1513  
  1514  func TestCaseInsensitiveSet(t *testing.T) {
  1515  	Reset()
  1516  	m1 := map[string]interface{}{
  1517  		"Foo": 32,
  1518  		"Bar": map[interface{}]interface {
  1519  		}{
  1520  			"ABc": "A",
  1521  			"cDE": "B"},
  1522  	}
  1523  
  1524  	m2 := map[string]interface{}{
  1525  		"Foo": 52,
  1526  		"Bar": map[interface{}]interface {
  1527  		}{
  1528  			"bCd": "A",
  1529  			"eFG": "B"},
  1530  	}
  1531  
  1532  	Set("Given1", m1)
  1533  	Set("Number1", 42)
  1534  
  1535  	SetDefault("Given2", m2)
  1536  	SetDefault("Number2", 52)
  1537  
  1538  	// Verify SetDefault
  1539  	if v := Get("number2"); v != 52 {
  1540  		t.Fatalf("Expected 52 got %q", v)
  1541  	}
  1542  
  1543  	if v := Get("given2.foo"); v != 52 {
  1544  		t.Fatalf("Expected 52 got %q", v)
  1545  	}
  1546  
  1547  	if v := Get("given2.bar.bcd"); v != "A" {
  1548  		t.Fatalf("Expected A got %q", v)
  1549  	}
  1550  
  1551  	if _, ok := m2["Foo"]; !ok {
  1552  		t.Fatal("Input map changed")
  1553  	}
  1554  
  1555  	// Verify Set
  1556  	if v := Get("number1"); v != 42 {
  1557  		t.Fatalf("Expected 42 got %q", v)
  1558  	}
  1559  
  1560  	if v := Get("given1.foo"); v != 32 {
  1561  		t.Fatalf("Expected 32 got %q", v)
  1562  	}
  1563  
  1564  	if v := Get("given1.bar.abc"); v != "A" {
  1565  		t.Fatalf("Expected A got %q", v)
  1566  	}
  1567  
  1568  	if _, ok := m1["Foo"]; !ok {
  1569  		t.Fatal("Input map changed")
  1570  	}
  1571  }
  1572  
  1573  func TestParseNested(t *testing.T) {
  1574  	type duration struct {
  1575  		Delay time.Duration
  1576  	}
  1577  
  1578  	type item struct {
  1579  		Name   string
  1580  		Delay  time.Duration
  1581  		Nested duration
  1582  	}
  1583  
  1584  	config := `[[parent]]
  1585  	delay="100ms"
  1586  	[parent.nested]
  1587  	delay="200ms"
  1588  `
  1589  	initConfig("toml", config)
  1590  
  1591  	var items []item
  1592  	err := l.UnmarshalKey("parent", &items)
  1593  	if err != nil {
  1594  		t.Fatalf("unable to decode into struct, %v", err)
  1595  	}
  1596  
  1597  	assert.Equal(t, 1, len(items))
  1598  	assert.Equal(t, 100*time.Millisecond, items[0].Delay)
  1599  	assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
  1600  }
  1601  
  1602  func doTestCaseInsensitive(t *testing.T, typ, config string) {
  1603  	initConfig(typ, config)
  1604  	Set("RfD", true)
  1605  	assert.Equal(t, true, Get("rfd"))
  1606  	assert.Equal(t, true, Get("rFD"))
  1607  	assert.Equal(t, 1, cast.ToInt(Get("abcd")))
  1608  	assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
  1609  	assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
  1610  	assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
  1611  	assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
  1612  	assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
  1613  
  1614  }
  1615  
  1616  func newLungWithConfigFile(t *testing.T) (*Lung, string, func()) {
  1617  	watchDir, err := ioutil.TempDir("", "")
  1618  	require.Nil(t, err)
  1619  	configFile := path.Join(watchDir, "config.yaml")
  1620  	err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640)
  1621  	require.Nil(t, err)
  1622  	cleanup := func() {
  1623  		os.RemoveAll(watchDir)
  1624  	}
  1625  	v := New()
  1626  	v.SetConfigFile(configFile)
  1627  	err = v.ReadInConfig()
  1628  	require.Nil(t, err)
  1629  	require.Equal(t, "bar", v.Get("foo"))
  1630  	return v, configFile, cleanup
  1631  }
  1632  
  1633  func newLungWithSymlinkedConfigFile(t *testing.T) (*Lung, string, string, func()) {
  1634  	watchDir, err := ioutil.TempDir("", "")
  1635  	require.Nil(t, err)
  1636  	dataDir1 := path.Join(watchDir, "data1")
  1637  	err = os.Mkdir(dataDir1, 0777)
  1638  	require.Nil(t, err)
  1639  	realConfigFile := path.Join(dataDir1, "config.yaml")
  1640  	t.Logf("Real config file location: %s\n", realConfigFile)
  1641  	err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640)
  1642  	require.Nil(t, err)
  1643  	cleanup := func() {
  1644  		os.RemoveAll(watchDir)
  1645  	}
  1646  	// now, symlink the tm `data1` dir to `data` in the baseDir
  1647  	os.Symlink(dataDir1, path.Join(watchDir, "data"))
  1648  	// and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`
  1649  	configFile := path.Join(watchDir, "config.yaml")
  1650  	os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile)
  1651  	t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml"))
  1652  	// init Lung
  1653  	v := New()
  1654  	v.SetConfigFile(configFile)
  1655  	err = v.ReadInConfig()
  1656  	require.Nil(t, err)
  1657  	require.Equal(t, "bar", v.Get("foo"))
  1658  	return v, watchDir, configFile, cleanup
  1659  }
  1660  
  1661  func TestWatchFile(t *testing.T) {
  1662  	if runtime.GOOS == "linux" {
  1663  		// TODO(bep) FIX ME
  1664  		t.Skip("Skip test on Linux ...")
  1665  	}
  1666  
  1667  	t.Run("file content changed", func(t *testing.T) {
  1668  		// given a `config.yaml` file being watched
  1669  		v, configFile, cleanup := newLungWithConfigFile(t)
  1670  		defer cleanup()
  1671  		_, err := os.Stat(configFile)
  1672  		require.NoError(t, err)
  1673  		t.Logf("test config file: %s\n", configFile)
  1674  		wg := sync.WaitGroup{}
  1675  		wg.Add(1)
  1676  		v.OnConfigChange(func(in fsnotify.Event) {
  1677  			t.Logf("config file changed")
  1678  			wg.Done()
  1679  		})
  1680  		v.WatchConfig()
  1681  		// when overwriting the file and waiting for the custom change notification handler to be triggered
  1682  		err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640)
  1683  		wg.Wait()
  1684  		// then the config value should have changed
  1685  		require.Nil(t, err)
  1686  		assert.Equal(t, "baz", v.Get("foo"))
  1687  	})
  1688  
  1689  	t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) {
  1690  		// skip if not executed on Linux
  1691  		if runtime.GOOS != "linux" {
  1692  			t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
  1693  		}
  1694  		v, watchDir, _, _ := newLungWithSymlinkedConfigFile(t)
  1695  		// defer cleanup()
  1696  		wg := sync.WaitGroup{}
  1697  		v.WatchConfig()
  1698  		v.OnConfigChange(func(in fsnotify.Event) {
  1699  			t.Logf("config file changed")
  1700  			wg.Done()
  1701  		})
  1702  		wg.Add(1)
  1703  		// when link to another `config.yaml` file
  1704  		dataDir2 := path.Join(watchDir, "data2")
  1705  		err := os.Mkdir(dataDir2, 0777)
  1706  		require.Nil(t, err)
  1707  		configFile2 := path.Join(dataDir2, "config.yaml")
  1708  		err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640)
  1709  		require.Nil(t, err)
  1710  		// change the symlink using the `ln -sfn` command
  1711  		err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
  1712  		require.Nil(t, err)
  1713  		wg.Wait()
  1714  		// then
  1715  		require.Nil(t, err)
  1716  		assert.Equal(t, "baz", v.Get("foo"))
  1717  	})
  1718  
  1719  }
  1720  
  1721  func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
  1722  	flags := gflag.NewFlagSet("test", gflag.ContinueOnError)
  1723  	flags.String("foo.bar", "cobra_flag", "")
  1724  
  1725  	v := New()
  1726  	assert.NoError(t, v.BindPFlags(flags))
  1727  
  1728  	config := &struct {
  1729  		Foo struct {
  1730  			Bar string
  1731  		}
  1732  	}{}
  1733  
  1734  	assert.NoError(t, v.Unmarshal(config))
  1735  	assert.Equal(t, "cobra_flag", config.Foo.Bar)
  1736  }
  1737  
  1738  func BenchmarkGetBool(b *testing.B) {
  1739  	key := "BenchmarkGetBool"
  1740  	l = New()
  1741  	l.Set(key, true)
  1742  
  1743  	for i := 0; i < b.N; i++ {
  1744  		if !l.GetBool(key) {
  1745  			b.Fatal("GetBool returned false")
  1746  		}
  1747  	}
  1748  }
  1749  
  1750  func BenchmarkGet(b *testing.B) {
  1751  	key := "BenchmarkGet"
  1752  	l = New()
  1753  	l.Set(key, true)
  1754  
  1755  	for i := 0; i < b.N; i++ {
  1756  		if !l.Get(key).(bool) {
  1757  			b.Fatal("Get returned false")
  1758  		}
  1759  	}
  1760  }
  1761  
  1762  // BenchmarkGetBoolFromMap is the "perfect result" for the above.
  1763  func BenchmarkGetBoolFromMap(b *testing.B) {
  1764  	m := make(map[string]bool)
  1765  	key := "BenchmarkGetBool"
  1766  	m[key] = true
  1767  
  1768  	for i := 0; i < b.N; i++ {
  1769  		if !m[key] {
  1770  			b.Fatal("Map value was false")
  1771  		}
  1772  	}
  1773  }