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