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	"fmt"
11	"io"
12	"io/ioutil"
13	"os"
14	"path"
15	"reflect"
16	"sort"
17	"strings"
18	"testing"
19	"time"
20
21	"github.com/spf13/cast"
22
23	"github.com/spf13/pflag"
24	"github.com/stretchr/testify/assert"
25)
26
27var yamlExample = []byte(`Hacker: true
28name: steve
29hobbies:
30- skateboarding
31- snowboarding
32- go
33clothing:
34  jacket: leather
35  trousers: denim
36  pants:
37    size: large
38age: 35
39eyes : brown
40beard: true
41`)
42
43var yamlExampleWithExtras = []byte(`Existing: true
44Bogus: true
45`)
46
47type testUnmarshalExtra struct {
48	Existing bool
49}
50
51var tomlExample = []byte(`
52title = "TOML Example"
53
54[owner]
55organization = "MongoDB"
56Bio = "MongoDB Chief Developer Advocate & Hacker at Large"
57dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)
58
59var jsonExample = []byte(`{
60"id": "0001",
61"type": "donut",
62"name": "Cake",
63"ppu": 0.55,
64"batters": {
65        "batter": [
66                { "type": "Regular" },
67                { "type": "Chocolate" },
68                { "type": "Blueberry" },
69                { "type": "Devil's Food" }
70            ]
71    }
72}`)
73
74var hclExample = []byte(`
75id = "0001"
76type = "donut"
77name = "Cake"
78ppu = 0.55
79foos {
80	foo {
81		key = 1
82	}
83	foo {
84		key = 2
85	}
86	foo {
87		key = 3
88	}
89	foo {
90		key = 4
91	}
92}`)
93
94var propertiesExample = []byte(`
95p_id: 0001
96p_type: donut
97p_name: Cake
98p_ppu: 0.55
99p_batters.batter.type: Regular
100`)
101
102var remoteExample = []byte(`{
103"id":"0002",
104"type":"cronut",
105"newkey":"remote"
106}`)
107
108func initConfigs() {
109	Reset()
110	var r io.Reader
111	SetConfigType("yaml")
112	r = bytes.NewReader(yamlExample)
113	unmarshalReader(r, v.config)
114
115	SetConfigType("json")
116	r = bytes.NewReader(jsonExample)
117	unmarshalReader(r, v.config)
118
119	SetConfigType("hcl")
120	r = bytes.NewReader(hclExample)
121	unmarshalReader(r, v.config)
122
123	SetConfigType("properties")
124	r = bytes.NewReader(propertiesExample)
125	unmarshalReader(r, v.config)
126
127	SetConfigType("toml")
128	r = bytes.NewReader(tomlExample)
129	unmarshalReader(r, v.config)
130
131	SetConfigType("json")
132	remote := bytes.NewReader(remoteExample)
133	unmarshalReader(remote, v.kvstore)
134}
135
136func initConfig(typ, config string) {
137	Reset()
138	SetConfigType(typ)
139	r := strings.NewReader(config)
140
141	if err := unmarshalReader(r, v.config); err != nil {
142		panic(err)
143	}
144}
145
146func initYAML() {
147	initConfig("yaml", string(yamlExample))
148}
149
150func initJSON() {
151	Reset()
152	SetConfigType("json")
153	r := bytes.NewReader(jsonExample)
154
155	unmarshalReader(r, v.config)
156}
157
158func initProperties() {
159	Reset()
160	SetConfigType("properties")
161	r := bytes.NewReader(propertiesExample)
162
163	unmarshalReader(r, v.config)
164}
165
166func initTOML() {
167	Reset()
168	SetConfigType("toml")
169	r := bytes.NewReader(tomlExample)
170
171	unmarshalReader(r, v.config)
172}
173
174func initHcl() {
175	Reset()
176	SetConfigType("hcl")
177	r := bytes.NewReader(hclExample)
178
179	unmarshalReader(r, v.config)
180}
181
182// make directories for testing
183func initDirs(t *testing.T) (string, string, func()) {
184
185	var (
186		testDirs = []string{`a a`, `b`, `c\c`, `D_`}
187		config   = `improbable`
188	)
189
190	root, err := ioutil.TempDir("", "")
191
192	cleanup := true
193	defer func() {
194		if cleanup {
195			os.Chdir("..")
196			os.RemoveAll(root)
197		}
198	}()
199
200	assert.Nil(t, err)
201
202	err = os.Chdir(root)
203	assert.Nil(t, err)
204
205	for _, dir := range testDirs {
206		err = os.Mkdir(dir, 0750)
207		assert.Nil(t, err)
208
209		err = ioutil.WriteFile(
210			path.Join(dir, config+".toml"),
211			[]byte("key = \"value is "+dir+"\"\n"),
212			0640)
213		assert.Nil(t, err)
214	}
215
216	cleanup = false
217	return root, config, func() {
218		os.Chdir("..")
219		os.RemoveAll(root)
220	}
221}
222
223//stubs for PFlag Values
224type stringValue string
225
226func newStringValue(val string, p *string) *stringValue {
227	*p = val
228	return (*stringValue)(p)
229}
230
231func (s *stringValue) Set(val string) error {
232	*s = stringValue(val)
233	return nil
234}
235
236func (s *stringValue) Type() string {
237	return "string"
238}
239
240func (s *stringValue) String() string {
241	return fmt.Sprintf("%s", *s)
242}
243
244func TestBasics(t *testing.T) {
245	SetConfigFile("/tmp/config.yaml")
246	filename, err := v.getConfigFile()
247	assert.Equal(t, "/tmp/config.yaml", filename)
248	assert.NoError(t, err)
249}
250
251func TestDefault(t *testing.T) {
252	SetDefault("age", 45)
253	assert.Equal(t, 45, Get("age"))
254
255	SetDefault("clothing.jacket", "slacks")
256	assert.Equal(t, "slacks", Get("clothing.jacket"))
257
258	SetConfigType("yaml")
259	err := ReadConfig(bytes.NewBuffer(yamlExample))
260
261	assert.NoError(t, err)
262	assert.Equal(t, "leather", Get("clothing.jacket"))
263}
264
265func TestUnmarshalling(t *testing.T) {
266	SetConfigType("yaml")
267	r := bytes.NewReader(yamlExample)
268
269	unmarshalReader(r, v.config)
270	assert.True(t, InConfig("name"))
271	assert.False(t, InConfig("state"))
272	assert.Equal(t, "steve", Get("name"))
273	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
274	assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
275	assert.Equal(t, 35, Get("age"))
276}
277
278func TestUnmarshalExact(t *testing.T) {
279	vip := New()
280	target := &testUnmarshalExtra{}
281	vip.SetConfigType("yaml")
282	r := bytes.NewReader(yamlExampleWithExtras)
283	vip.ReadConfig(r)
284	err := vip.UnmarshalExact(target)
285	if err == nil {
286		t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields")
287	}
288}
289
290func TestOverrides(t *testing.T) {
291	Set("age", 40)
292	assert.Equal(t, 40, Get("age"))
293}
294
295func TestDefaultPost(t *testing.T) {
296	assert.NotEqual(t, "NYC", Get("state"))
297	SetDefault("state", "NYC")
298	assert.Equal(t, "NYC", Get("state"))
299}
300
301func TestAliases(t *testing.T) {
302	RegisterAlias("years", "age")
303	assert.Equal(t, 40, Get("years"))
304	Set("years", 45)
305	assert.Equal(t, 45, Get("age"))
306}
307
308func TestAliasInConfigFile(t *testing.T) {
309	// the config file specifies "beard".  If we make this an alias for
310	// "hasbeard", we still want the old config file to work with beard.
311	RegisterAlias("beard", "hasbeard")
312	assert.Equal(t, true, Get("hasbeard"))
313	Set("hasbeard", false)
314	assert.Equal(t, false, Get("beard"))
315}
316
317func TestYML(t *testing.T) {
318	initYAML()
319	assert.Equal(t, "steve", Get("name"))
320}
321
322func TestJSON(t *testing.T) {
323	initJSON()
324	assert.Equal(t, "0001", Get("id"))
325}
326
327func TestProperties(t *testing.T) {
328	initProperties()
329	assert.Equal(t, "0001", Get("p_id"))
330}
331
332func TestTOML(t *testing.T) {
333	initTOML()
334	assert.Equal(t, "TOML Example", Get("title"))
335}
336
337func TestHCL(t *testing.T) {
338	initHcl()
339	assert.Equal(t, "0001", Get("id"))
340	assert.Equal(t, 0.55, Get("ppu"))
341	assert.Equal(t, "donut", Get("type"))
342	assert.Equal(t, "Cake", Get("name"))
343	Set("id", "0002")
344	assert.Equal(t, "0002", Get("id"))
345	assert.NotEqual(t, "cronut", Get("type"))
346}
347
348func TestRemotePrecedence(t *testing.T) {
349	initJSON()
350
351	remote := bytes.NewReader(remoteExample)
352	assert.Equal(t, "0001", Get("id"))
353	unmarshalReader(remote, v.kvstore)
354	assert.Equal(t, "0001", Get("id"))
355	assert.NotEqual(t, "cronut", Get("type"))
356	assert.Equal(t, "remote", Get("newkey"))
357	Set("newkey", "newvalue")
358	assert.NotEqual(t, "remote", Get("newkey"))
359	assert.Equal(t, "newvalue", Get("newkey"))
360	Set("newkey", "remote")
361}
362
363func TestEnv(t *testing.T) {
364	initJSON()
365
366	BindEnv("id")
367	BindEnv("f", "FOOD")
368
369	os.Setenv("ID", "13")
370	os.Setenv("FOOD", "apple")
371	os.Setenv("NAME", "crunk")
372
373	assert.Equal(t, "13", Get("id"))
374	assert.Equal(t, "apple", Get("f"))
375	assert.Equal(t, "Cake", Get("name"))
376
377	AutomaticEnv()
378
379	assert.Equal(t, "crunk", Get("name"))
380
381}
382
383func TestEnvPrefix(t *testing.T) {
384	initJSON()
385
386	SetEnvPrefix("foo") // will be uppercased automatically
387	BindEnv("id")
388	BindEnv("f", "FOOD") // not using prefix
389
390	os.Setenv("FOO_ID", "13")
391	os.Setenv("FOOD", "apple")
392	os.Setenv("FOO_NAME", "crunk")
393
394	assert.Equal(t, "13", Get("id"))
395	assert.Equal(t, "apple", Get("f"))
396	assert.Equal(t, "Cake", Get("name"))
397
398	AutomaticEnv()
399
400	assert.Equal(t, "crunk", Get("name"))
401}
402
403func TestAutoEnv(t *testing.T) {
404	Reset()
405
406	AutomaticEnv()
407	os.Setenv("FOO_BAR", "13")
408	assert.Equal(t, "13", Get("foo_bar"))
409}
410
411func TestAutoEnvWithPrefix(t *testing.T) {
412	Reset()
413
414	AutomaticEnv()
415	SetEnvPrefix("Baz")
416	os.Setenv("BAZ_BAR", "13")
417	assert.Equal(t, "13", Get("bar"))
418}
419
420func TestSetEnvReplacer(t *testing.T) {
421	Reset()
422
423	AutomaticEnv()
424	os.Setenv("REFRESH_INTERVAL", "30s")
425
426	replacer := strings.NewReplacer("-", "_")
427	SetEnvKeyReplacer(replacer)
428
429	assert.Equal(t, "30s", Get("refresh-interval"))
430}
431
432func TestAllKeys(t *testing.T) {
433	initConfigs()
434
435	ks := sort.StringSlice{"title", "newkey", "owner.organization", "owner.dob", "owner.bio", "name", "beard", "ppu", "batters.batter", "hobbies", "clothing.jacket", "clothing.trousers", "clothing.pants.size", "age", "hacker", "id", "type", "eyes", "p_id", "p_ppu", "p_batters.batter.type", "p_type", "p_name", "foos"}
436	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
437	all := map[string]interface{}{"owner": map[string]interface{}{"organization": "MongoDB", "bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "title": "TOML Example", "ppu": 0.55, "eyes": "brown", "clothing": map[string]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[string]interface{}{"size": "large"}}, "id": "0001", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hacker": true, "beard": true, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "age": 35, "type": "donut", "newkey": "remote", "name": "Cake", "p_id": "0001", "p_ppu": "0.55", "p_name": "Cake", "p_batters": map[string]interface{}{"batter": map[string]interface{}{"type": "Regular"}}, "p_type": "donut", "foos": []map[string]interface{}{map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"key": 1}, map[string]interface{}{"key": 2}, map[string]interface{}{"key": 3}, map[string]interface{}{"key": 4}}}}}
438
439	var allkeys sort.StringSlice
440	allkeys = AllKeys()
441	allkeys.Sort()
442	ks.Sort()
443
444	assert.Equal(t, ks, allkeys)
445	assert.Equal(t, all, AllSettings())
446}
447
448func TestAllKeysWithEnv(t *testing.T) {
449	v := New()
450
451	// bind and define environment variables (including a nested one)
452	v.BindEnv("id")
453	v.BindEnv("foo.bar")
454	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
455	os.Setenv("ID", "13")
456	os.Setenv("FOO_BAR", "baz")
457
458	expectedKeys := sort.StringSlice{"id", "foo.bar"}
459	expectedKeys.Sort()
460	keys := sort.StringSlice(v.AllKeys())
461	keys.Sort()
462	assert.Equal(t, expectedKeys, keys)
463}
464
465func TestAliasesOfAliases(t *testing.T) {
466	Set("Title", "Checking Case")
467	RegisterAlias("Foo", "Bar")
468	RegisterAlias("Bar", "Title")
469	assert.Equal(t, "Checking Case", Get("FOO"))
470}
471
472func TestRecursiveAliases(t *testing.T) {
473	RegisterAlias("Baz", "Roo")
474	RegisterAlias("Roo", "baz")
475}
476
477func TestUnmarshal(t *testing.T) {
478	SetDefault("port", 1313)
479	Set("name", "Steve")
480	Set("duration", "1s1ms")
481
482	type config struct {
483		Port     int
484		Name     string
485		Duration time.Duration
486	}
487
488	var C config
489
490	err := Unmarshal(&C)
491	if err != nil {
492		t.Fatalf("unable to decode into struct, %v", err)
493	}
494
495	assert.Equal(t, &config{Name: "Steve", Port: 1313, Duration: time.Second + time.Millisecond}, &C)
496
497	Set("port", 1234)
498	err = Unmarshal(&C)
499	if err != nil {
500		t.Fatalf("unable to decode into struct, %v", err)
501	}
502	assert.Equal(t, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond}, &C)
503}
504
505func TestBindPFlags(t *testing.T) {
506	v := New() // create independent Viper object
507	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
508
509	var testValues = map[string]*string{
510		"host":     nil,
511		"port":     nil,
512		"endpoint": nil,
513	}
514
515	var mutatedTestValues = map[string]string{
516		"host":     "localhost",
517		"port":     "6060",
518		"endpoint": "/public",
519	}
520
521	for name := range testValues {
522		testValues[name] = flagSet.String(name, "", "test")
523	}
524
525	err := v.BindPFlags(flagSet)
526	if err != nil {
527		t.Fatalf("error binding flag set, %v", err)
528	}
529
530	flagSet.VisitAll(func(flag *pflag.Flag) {
531		flag.Value.Set(mutatedTestValues[flag.Name])
532		flag.Changed = true
533	})
534
535	for name, expected := range mutatedTestValues {
536		assert.Equal(t, expected, v.Get(name))
537	}
538
539}
540
541func TestBindPFlagsStringSlice(t *testing.T) {
542	for _, testValue := range []struct {
543		Expected []string
544		Value    string
545	}{
546		{[]string{}, ""},
547		{[]string{"jeden"}, "jeden"},
548		{[]string{"dwa", "trzy"}, "dwa,trzy"},
549		{[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""}} {
550
551		for _, changed := range []bool{true, false} {
552			v := New() // create independent Viper object
553			flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
554			flagSet.StringSlice("stringslice", testValue.Expected, "test")
555			flagSet.Visit(func(f *pflag.Flag) {
556				if len(testValue.Value) > 0 {
557					f.Value.Set(testValue.Value)
558					f.Changed = changed
559				}
560			})
561
562			err := v.BindPFlags(flagSet)
563			if err != nil {
564				t.Fatalf("error binding flag set, %v", err)
565			}
566
567			type TestStr struct {
568				StringSlice []string
569			}
570			val := &TestStr{}
571			if err := v.Unmarshal(val); err != nil {
572				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
573			}
574			assert.Equal(t, testValue.Expected, val.StringSlice)
575		}
576	}
577}
578
579func TestBindPFlag(t *testing.T) {
580	var testString = "testing"
581	var testValue = newStringValue(testString, &testString)
582
583	flag := &pflag.Flag{
584		Name:    "testflag",
585		Value:   testValue,
586		Changed: false,
587	}
588
589	BindPFlag("testvalue", flag)
590
591	assert.Equal(t, testString, Get("testvalue"))
592
593	flag.Value.Set("testing_mutate")
594	flag.Changed = true //hack for pflag usage
595
596	assert.Equal(t, "testing_mutate", Get("testvalue"))
597
598}
599
600func TestBoundCaseSensitivity(t *testing.T) {
601	assert.Equal(t, "brown", Get("eyes"))
602
603	BindEnv("eYEs", "TURTLE_EYES")
604	os.Setenv("TURTLE_EYES", "blue")
605
606	assert.Equal(t, "blue", Get("eyes"))
607
608	var testString = "green"
609	var testValue = newStringValue(testString, &testString)
610
611	flag := &pflag.Flag{
612		Name:    "eyeballs",
613		Value:   testValue,
614		Changed: true,
615	}
616
617	BindPFlag("eYEs", flag)
618	assert.Equal(t, "green", Get("eyes"))
619
620}
621
622func TestSizeInBytes(t *testing.T) {
623	input := map[string]uint{
624		"":               0,
625		"b":              0,
626		"12 bytes":       0,
627		"200000000000gb": 0,
628		"12 b":           12,
629		"43 MB":          43 * (1 << 20),
630		"10mb":           10 * (1 << 20),
631		"1gb":            1 << 30,
632	}
633
634	for str, expected := range input {
635		assert.Equal(t, expected, parseSizeInBytes(str), str)
636	}
637}
638
639func TestFindsNestedKeys(t *testing.T) {
640	initConfigs()
641	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
642
643	Set("super", map[string]interface{}{
644		"deep": map[string]interface{}{
645			"nested": "value",
646		},
647	})
648
649	expected := map[string]interface{}{
650		"super": map[string]interface{}{
651			"deep": map[string]interface{}{
652				"nested": "value",
653			},
654		},
655		"super.deep": map[string]interface{}{
656			"nested": "value",
657		},
658		"super.deep.nested":  "value",
659		"owner.organization": "MongoDB",
660		"batters.batter": []interface{}{
661			map[string]interface{}{
662				"type": "Regular",
663			},
664			map[string]interface{}{
665				"type": "Chocolate",
666			},
667			map[string]interface{}{
668				"type": "Blueberry",
669			},
670			map[string]interface{}{
671				"type": "Devil's Food",
672			},
673		},
674		"hobbies": []interface{}{
675			"skateboarding", "snowboarding", "go",
676		},
677		"title":  "TOML Example",
678		"newkey": "remote",
679		"batters": map[string]interface{}{
680			"batter": []interface{}{
681				map[string]interface{}{
682					"type": "Regular",
683				},
684				map[string]interface{}{
685					"type": "Chocolate",
686				}, map[string]interface{}{
687					"type": "Blueberry",
688				}, map[string]interface{}{
689					"type": "Devil's Food",
690				},
691			},
692		},
693		"eyes": "brown",
694		"age":  35,
695		"owner": map[string]interface{}{
696			"organization": "MongoDB",
697			"bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
698			"dob":          dob,
699		},
700		"owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large",
701		"type":      "donut",
702		"id":        "0001",
703		"name":      "Cake",
704		"hacker":    true,
705		"ppu":       0.55,
706		"clothing": map[string]interface{}{
707			"jacket":   "leather",
708			"trousers": "denim",
709			"pants": map[string]interface{}{
710				"size": "large",
711			},
712		},
713		"clothing.jacket":     "leather",
714		"clothing.pants.size": "large",
715		"clothing.trousers":   "denim",
716		"owner.dob":           dob,
717		"beard":               true,
718		"foos": []map[string]interface{}{
719			map[string]interface{}{
720				"foo": []map[string]interface{}{
721					map[string]interface{}{
722						"key": 1,
723					},
724					map[string]interface{}{
725						"key": 2,
726					},
727					map[string]interface{}{
728						"key": 3,
729					},
730					map[string]interface{}{
731						"key": 4,
732					},
733				},
734			},
735		},
736	}
737
738	for key, expectedValue := range expected {
739
740		assert.Equal(t, expectedValue, v.Get(key))
741	}
742
743}
744
745func TestReadBufConfig(t *testing.T) {
746	v := New()
747	v.SetConfigType("yaml")
748	v.ReadConfig(bytes.NewBuffer(yamlExample))
749	t.Log(v.AllKeys())
750
751	assert.True(t, v.InConfig("name"))
752	assert.False(t, v.InConfig("state"))
753	assert.Equal(t, "steve", v.Get("name"))
754	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
755	assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
756	assert.Equal(t, 35, v.Get("age"))
757}
758
759func TestIsSet(t *testing.T) {
760	v := New()
761	v.SetConfigType("yaml")
762	v.ReadConfig(bytes.NewBuffer(yamlExample))
763	assert.True(t, v.IsSet("clothing.jacket"))
764	assert.False(t, v.IsSet("clothing.jackets"))
765	assert.False(t, v.IsSet("helloworld"))
766	v.Set("helloworld", "fubar")
767	assert.True(t, v.IsSet("helloworld"))
768}
769
770func TestDirsSearch(t *testing.T) {
771
772	root, config, cleanup := initDirs(t)
773	defer cleanup()
774
775	v := New()
776	v.SetConfigName(config)
777	v.SetDefault(`key`, `default`)
778
779	entries, err := ioutil.ReadDir(root)
780	for _, e := range entries {
781		if e.IsDir() {
782			v.AddConfigPath(e.Name())
783		}
784	}
785
786	err = v.ReadInConfig()
787	assert.Nil(t, err)
788
789	assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`))
790}
791
792func TestWrongDirsSearchNotFound(t *testing.T) {
793
794	_, config, cleanup := initDirs(t)
795	defer cleanup()
796
797	v := New()
798	v.SetConfigName(config)
799	v.SetDefault(`key`, `default`)
800
801	v.AddConfigPath(`whattayoutalkingbout`)
802	v.AddConfigPath(`thispathaintthere`)
803
804	err := v.ReadInConfig()
805	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
806
807	// Even though config did not load and the error might have
808	// been ignored by the client, the default still loads
809	assert.Equal(t, `default`, v.GetString(`key`))
810}
811
812func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
813
814	_, config, cleanup := initDirs(t)
815	defer cleanup()
816
817	v := New()
818	v.SetConfigName(config)
819	v.SetDefault(`key`, `default`)
820
821	v.AddConfigPath(`whattayoutalkingbout`)
822	v.AddConfigPath(`thispathaintthere`)
823
824	err := v.MergeInConfig()
825	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
826
827	// Even though config did not load and the error might have
828	// been ignored by the client, the default still loads
829	assert.Equal(t, `default`, v.GetString(`key`))
830}
831
832func TestSub(t *testing.T) {
833	v := New()
834	v.SetConfigType("yaml")
835	v.ReadConfig(bytes.NewBuffer(yamlExample))
836
837	subv := v.Sub("clothing")
838	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
839
840	subv = v.Sub("clothing.pants")
841	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size"))
842
843	subv = v.Sub("clothing.pants.size")
844	assert.Equal(t, (*Viper)(nil), subv)
845
846	subv = v.Sub("missing.key")
847	assert.Equal(t, (*Viper)(nil), subv)
848}
849
850var yamlMergeExampleTgt = []byte(`
851hello:
852    pop: 37890
853    lagrenum: 765432101234567
854    world:
855    - us
856    - uk
857    - fr
858    - de
859`)
860
861var yamlMergeExampleSrc = []byte(`
862hello:
863    pop: 45000
864    lagrenum: 7654321001234567
865    universe:
866    - mw
867    - ad
868fu: bar
869`)
870
871func TestMergeConfig(t *testing.T) {
872	v := New()
873	v.SetConfigType("yml")
874	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
875		t.Fatal(err)
876	}
877
878	if pop := v.GetInt("hello.pop"); pop != 37890 {
879		t.Fatalf("pop != 37890, = %d", pop)
880	}
881
882	if pop := v.GetInt("hello.lagrenum"); pop != 765432101234567 {
883		t.Fatalf("lagrenum != 765432101234567, = %d", pop)
884	}
885
886	if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) {
887		t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop)
888	}
889
890	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
891		t.Fatalf("len(world) != 4, = %d", len(world))
892	}
893
894	if fu := v.GetString("fu"); fu != "" {
895		t.Fatalf("fu != \"\", = %s", fu)
896	}
897
898	if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
899		t.Fatal(err)
900	}
901
902	if pop := v.GetInt("hello.pop"); pop != 45000 {
903		t.Fatalf("pop != 45000, = %d", pop)
904	}
905
906	if pop := v.GetInt("hello.lagrenum"); pop != 7654321001234567 {
907		t.Fatalf("lagrenum != 7654321001234567, = %d", pop)
908	}
909
910	if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) {
911		t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop)
912	}
913
914	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
915		t.Fatalf("len(world) != 4, = %d", len(world))
916	}
917
918	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
919		t.Fatalf("len(universe) != 2, = %d", len(universe))
920	}
921
922	if fu := v.GetString("fu"); fu != "bar" {
923		t.Fatalf("fu != \"bar\", = %s", fu)
924	}
925}
926
927func TestMergeConfigNoMerge(t *testing.T) {
928	v := New()
929	v.SetConfigType("yml")
930	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
931		t.Fatal(err)
932	}
933
934	if pop := v.GetInt("hello.pop"); pop != 37890 {
935		t.Fatalf("pop != 37890, = %d", pop)
936	}
937
938	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
939		t.Fatalf("len(world) != 4, = %d", len(world))
940	}
941
942	if fu := v.GetString("fu"); fu != "" {
943		t.Fatalf("fu != \"\", = %s", fu)
944	}
945
946	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
947		t.Fatal(err)
948	}
949
950	if pop := v.GetInt("hello.pop"); pop != 45000 {
951		t.Fatalf("pop != 45000, = %d", pop)
952	}
953
954	if world := v.GetStringSlice("hello.world"); len(world) != 0 {
955		t.Fatalf("len(world) != 0, = %d", len(world))
956	}
957
958	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
959		t.Fatalf("len(universe) != 2, = %d", len(universe))
960	}
961
962	if fu := v.GetString("fu"); fu != "bar" {
963		t.Fatalf("fu != \"bar\", = %s", fu)
964	}
965}
966
967func TestUnmarshalingWithAliases(t *testing.T) {
968	v := New()
969	v.SetDefault("ID", 1)
970	v.Set("name", "Steve")
971	v.Set("lastname", "Owen")
972
973	v.RegisterAlias("UserID", "ID")
974	v.RegisterAlias("Firstname", "name")
975	v.RegisterAlias("Surname", "lastname")
976
977	type config struct {
978		ID        int
979		FirstName string
980		Surname   string
981	}
982
983	var C config
984	err := v.Unmarshal(&C)
985	if err != nil {
986		t.Fatalf("unable to decode into struct, %v", err)
987	}
988
989	assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C)
990}
991
992func TestSetConfigNameClearsFileCache(t *testing.T) {
993	SetConfigFile("/tmp/config.yaml")
994	SetConfigName("default")
995	f, err := v.getConfigFile()
996	if err == nil {
997		t.Fatalf("config file cache should have been cleared")
998	}
999	assert.Empty(t, f)
1000}
1001
1002func TestShadowedNestedValue(t *testing.T) {
1003
1004	config := `name: steve
1005clothing:
1006  jacket: leather
1007  trousers: denim
1008  pants:
1009    size: large
1010`
1011	initConfig("yaml", config)
1012
1013	assert.Equal(t, "steve", GetString("name"))
1014
1015	polyester := "polyester"
1016	SetDefault("clothing.shirt", polyester)
1017	SetDefault("clothing.jacket.price", 100)
1018
1019	assert.Equal(t, "leather", GetString("clothing.jacket"))
1020	assert.Nil(t, Get("clothing.jacket.price"))
1021	assert.Equal(t, polyester, GetString("clothing.shirt"))
1022
1023	clothingSettings := AllSettings()["clothing"].(map[string]interface{})
1024	assert.Equal(t, "leather", clothingSettings["jacket"])
1025	assert.Equal(t, polyester, clothingSettings["shirt"])
1026}
1027
1028func TestDotParameter(t *testing.T) {
1029	initJSON()
1030	// shoud take precedence over batters defined in jsonExample
1031	r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
1032	unmarshalReader(r, v.config)
1033
1034	actual := Get("batters.batter")
1035	expected := []interface{}{map[string]interface{}{"type": "Small"}}
1036	assert.Equal(t, expected, actual)
1037}
1038
1039func TestCaseInsensitive(t *testing.T) {
1040	for _, config := range []struct {
1041		typ     string
1042		content string
1043	}{
1044		{"yaml", `
1045aBcD: 1
1046eF:
1047  gH: 2
1048  iJk: 3
1049  Lm:
1050    nO: 4
1051    P:
1052      Q: 5
1053      R: 6
1054`},
1055		{"json", `{
1056  "aBcD": 1,
1057  "eF": {
1058    "iJk": 3,
1059    "Lm": {
1060      "P": {
1061        "Q": 5,
1062        "R": 6
1063      },
1064      "nO": 4
1065    },
1066    "gH": 2
1067  }
1068}`},
1069		{"toml", `aBcD = 1
1070[eF]
1071gH = 2
1072iJk = 3
1073[eF.Lm]
1074nO = 4
1075[eF.Lm.P]
1076Q = 5
1077R = 6
1078`},
1079	} {
1080		doTestCaseInsensitive(t, config.typ, config.content)
1081	}
1082}
1083
1084func TestCaseInsensitiveSet(t *testing.T) {
1085	Reset()
1086	m1 := map[string]interface{}{
1087		"Foo": 32,
1088		"Bar": map[interface{}]interface {
1089		}{
1090			"ABc": "A",
1091			"cDE": "B"},
1092	}
1093
1094	m2 := map[string]interface{}{
1095		"Foo": 52,
1096		"Bar": map[interface{}]interface {
1097		}{
1098			"bCd": "A",
1099			"eFG": "B"},
1100	}
1101
1102	Set("Given1", m1)
1103	Set("Number1", 42)
1104
1105	SetDefault("Given2", m2)
1106	SetDefault("Number2", 52)
1107
1108	// Verify SetDefault
1109	if v := Get("number2"); v != 52 {
1110		t.Fatalf("Expected 52 got %q", v)
1111	}
1112
1113	if v := Get("given2.foo"); v != 52 {
1114		t.Fatalf("Expected 52 got %q", v)
1115	}
1116
1117	if v := Get("given2.bar.bcd"); v != "A" {
1118		t.Fatalf("Expected A got %q", v)
1119	}
1120
1121	if _, ok := m2["Foo"]; !ok {
1122		t.Fatal("Input map changed")
1123	}
1124
1125	// Verify Set
1126	if v := Get("number1"); v != 42 {
1127		t.Fatalf("Expected 42 got %q", v)
1128	}
1129
1130	if v := Get("given1.foo"); v != 32 {
1131		t.Fatalf("Expected 32 got %q", v)
1132	}
1133
1134	if v := Get("given1.bar.abc"); v != "A" {
1135		t.Fatalf("Expected A got %q", v)
1136	}
1137
1138	if _, ok := m1["Foo"]; !ok {
1139		t.Fatal("Input map changed")
1140	}
1141}
1142
1143func TestParseNested(t *testing.T) {
1144	type duration struct {
1145		Delay time.Duration
1146	}
1147
1148	type item struct {
1149		Name   string
1150		Delay  time.Duration
1151		Nested duration
1152	}
1153
1154	config := `[[parent]]
1155	delay="100ms"
1156	[parent.nested]
1157	delay="200ms"
1158`
1159	initConfig("toml", config)
1160
1161	var items []item
1162	err := v.UnmarshalKey("parent", &items)
1163	if err != nil {
1164		t.Fatalf("unable to decode into struct, %v", err)
1165	}
1166
1167	assert.Equal(t, 1, len(items))
1168	assert.Equal(t, 100*time.Millisecond, items[0].Delay)
1169	assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
1170}
1171
1172func doTestCaseInsensitive(t *testing.T, typ, config string) {
1173	initConfig(typ, config)
1174	Set("RfD", true)
1175	assert.Equal(t, true, Get("rfd"))
1176	assert.Equal(t, true, Get("rFD"))
1177	assert.Equal(t, 1, cast.ToInt(Get("abcd")))
1178	assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
1179	assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
1180	assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
1181	assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
1182	assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
1183
1184}
1185
1186func BenchmarkGetBool(b *testing.B) {
1187	key := "BenchmarkGetBool"
1188	v = New()
1189	v.Set(key, true)
1190
1191	for i := 0; i < b.N; i++ {
1192		if !v.GetBool(key) {
1193			b.Fatal("GetBool returned false")
1194		}
1195	}
1196}
1197
1198func BenchmarkGet(b *testing.B) {
1199	key := "BenchmarkGet"
1200	v = New()
1201	v.Set(key, true)
1202
1203	for i := 0; i < b.N; i++ {
1204		if !v.Get(key).(bool) {
1205			b.Fatal("Get returned false")
1206		}
1207	}
1208}
1209
1210// This is the "perfect result" for the above.
1211func BenchmarkGetBoolFromMap(b *testing.B) {
1212	m := make(map[string]bool)
1213	key := "BenchmarkGetBool"
1214	m[key] = true
1215
1216	for i := 0; i < b.N; i++ {
1217		if !m[key] {
1218			b.Fatal("Map value was false")
1219		}
1220	}
1221}
1222