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