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