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