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