gopkg.in/necryin/viper.v1@v1.0.2/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 TestSetEnvKeyReplacer(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 v.ReadConfig(bytes.NewBuffer(yamlExample)) 763 assert.True(t, v.IsSet("clothing.jacket")) 764 assert.False(t, v.IsSet("clothing.jackets")) 765 assert.False(t, v.IsSet("helloworld")) 766 v.Set("helloworld", "fubar") 767 assert.True(t, v.IsSet("helloworld")) 768 } 769 770 func TestDirsSearch(t *testing.T) { 771 772 root, config, cleanup := initDirs(t) 773 defer cleanup() 774 775 v := New() 776 v.SetConfigName(config) 777 v.SetDefault(`key`, `default`) 778 779 entries, err := ioutil.ReadDir(root) 780 for _, e := range entries { 781 if e.IsDir() { 782 v.AddConfigPath(e.Name()) 783 } 784 } 785 786 err = v.ReadInConfig() 787 assert.Nil(t, err) 788 789 assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`)) 790 } 791 792 func TestWrongDirsSearchNotFound(t *testing.T) { 793 794 _, config, cleanup := initDirs(t) 795 defer cleanup() 796 797 v := New() 798 v.SetConfigName(config) 799 v.SetDefault(`key`, `default`) 800 801 v.AddConfigPath(`whattayoutalkingbout`) 802 v.AddConfigPath(`thispathaintthere`) 803 804 err := v.ReadInConfig() 805 assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) 806 807 // Even though config did not load and the error might have 808 // been ignored by the client, the default still loads 809 assert.Equal(t, `default`, v.GetString(`key`)) 810 } 811 812 func TestWrongDirsSearchNotFoundForMerge(t *testing.T) { 813 814 _, config, cleanup := initDirs(t) 815 defer cleanup() 816 817 v := New() 818 v.SetConfigName(config) 819 v.SetDefault(`key`, `default`) 820 821 v.AddConfigPath(`whattayoutalkingbout`) 822 v.AddConfigPath(`thispathaintthere`) 823 824 err := v.MergeInConfig() 825 assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) 826 827 // Even though config did not load and the error might have 828 // been ignored by the client, the default still loads 829 assert.Equal(t, `default`, v.GetString(`key`)) 830 } 831 832 func TestSub(t *testing.T) { 833 v := New() 834 v.SetConfigType("yaml") 835 v.ReadConfig(bytes.NewBuffer(yamlExample)) 836 837 subv := v.Sub("clothing") 838 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size")) 839 840 subv = v.Sub("clothing.pants") 841 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size")) 842 843 subv = v.Sub("clothing.pants.size") 844 assert.Equal(t, (*Viper)(nil), subv) 845 846 subv = v.Sub("missing.key") 847 assert.Equal(t, (*Viper)(nil), subv) 848 } 849 850 var yamlMergeExampleTgt = []byte(` 851 hello: 852 pop: 37890 853 lagrenum: 765432101234567 854 world: 855 - us 856 - uk 857 - fr 858 - de 859 `) 860 861 var yamlMergeExampleSrc = []byte(` 862 hello: 863 pop: 45000 864 lagrenum: 7654321001234567 865 universe: 866 - mw 867 - ad 868 fu: bar 869 `) 870 871 func TestMergeConfig(t *testing.T) { 872 v := New() 873 v.SetConfigType("yml") 874 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 875 t.Fatal(err) 876 } 877 878 if pop := v.GetInt("hello.pop"); pop != 37890 { 879 t.Fatalf("pop != 37890, = %d", pop) 880 } 881 882 if pop := v.GetInt("hello.lagrenum"); pop != 765432101234567 { 883 t.Fatalf("lagrenum != 765432101234567, = %d", pop) 884 } 885 886 if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) { 887 t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop) 888 } 889 890 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 891 t.Fatalf("len(world) != 4, = %d", len(world)) 892 } 893 894 if fu := v.GetString("fu"); fu != "" { 895 t.Fatalf("fu != \"\", = %s", fu) 896 } 897 898 if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 899 t.Fatal(err) 900 } 901 902 if pop := v.GetInt("hello.pop"); pop != 45000 { 903 t.Fatalf("pop != 45000, = %d", pop) 904 } 905 906 if pop := v.GetInt("hello.lagrenum"); pop != 7654321001234567 { 907 t.Fatalf("lagrenum != 7654321001234567, = %d", pop) 908 } 909 910 if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) { 911 t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop) 912 } 913 914 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 915 t.Fatalf("len(world) != 4, = %d", len(world)) 916 } 917 918 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 919 t.Fatalf("len(universe) != 2, = %d", len(universe)) 920 } 921 922 if fu := v.GetString("fu"); fu != "bar" { 923 t.Fatalf("fu != \"bar\", = %s", fu) 924 } 925 } 926 927 func TestMergeConfigNoMerge(t *testing.T) { 928 v := New() 929 v.SetConfigType("yml") 930 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 931 t.Fatal(err) 932 } 933 934 if pop := v.GetInt("hello.pop"); pop != 37890 { 935 t.Fatalf("pop != 37890, = %d", pop) 936 } 937 938 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 939 t.Fatalf("len(world) != 4, = %d", len(world)) 940 } 941 942 if fu := v.GetString("fu"); fu != "" { 943 t.Fatalf("fu != \"\", = %s", fu) 944 } 945 946 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 947 t.Fatal(err) 948 } 949 950 if pop := v.GetInt("hello.pop"); pop != 45000 { 951 t.Fatalf("pop != 45000, = %d", pop) 952 } 953 954 if world := v.GetStringSlice("hello.world"); len(world) != 0 { 955 t.Fatalf("len(world) != 0, = %d", len(world)) 956 } 957 958 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 959 t.Fatalf("len(universe) != 2, = %d", len(universe)) 960 } 961 962 if fu := v.GetString("fu"); fu != "bar" { 963 t.Fatalf("fu != \"bar\", = %s", fu) 964 } 965 } 966 967 func TestUnmarshalingWithAliases(t *testing.T) { 968 v := New() 969 v.SetDefault("ID", 1) 970 v.Set("name", "Steve") 971 v.Set("lastname", "Owen") 972 973 v.RegisterAlias("UserID", "ID") 974 v.RegisterAlias("Firstname", "name") 975 v.RegisterAlias("Surname", "lastname") 976 977 type config struct { 978 ID int 979 FirstName string 980 Surname string 981 } 982 983 var C config 984 err := v.Unmarshal(&C) 985 if err != nil { 986 t.Fatalf("unable to decode into struct, %v", err) 987 } 988 989 assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C) 990 } 991 992 func TestSetConfigNameClearsFileCache(t *testing.T) { 993 SetConfigFile("/tmp/config.yaml") 994 SetConfigName("default") 995 f, err := v.getConfigFile() 996 if err == nil { 997 t.Fatalf("config file cache should have been cleared") 998 } 999 assert.Empty(t, f) 1000 } 1001 1002 func TestShadowedNestedValue(t *testing.T) { 1003 1004 config := `name: steve 1005 clothing: 1006 jacket: leather 1007 trousers: denim 1008 pants: 1009 size: large 1010 ` 1011 initConfig("yaml", config) 1012 1013 assert.Equal(t, "steve", GetString("name")) 1014 1015 polyester := "polyester" 1016 SetDefault("clothing.shirt", polyester) 1017 SetDefault("clothing.jacket.price", 100) 1018 1019 assert.Equal(t, "leather", GetString("clothing.jacket")) 1020 assert.Nil(t, Get("clothing.jacket.price")) 1021 assert.Equal(t, polyester, GetString("clothing.shirt")) 1022 1023 clothingSettings := AllSettings()["clothing"].(map[string]interface{}) 1024 assert.Equal(t, "leather", clothingSettings["jacket"]) 1025 assert.Equal(t, polyester, clothingSettings["shirt"]) 1026 } 1027 1028 func TestDotParameter(t *testing.T) { 1029 initJSON() 1030 // shoud take precedence over batters defined in jsonExample 1031 r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`)) 1032 unmarshalReader(r, v.config) 1033 1034 actual := Get("batters.batter") 1035 expected := []interface{}{map[string]interface{}{"type": "Small"}} 1036 assert.Equal(t, expected, actual) 1037 } 1038 1039 func TestCaseInsensitive(t *testing.T) { 1040 for _, config := range []struct { 1041 typ string 1042 content string 1043 }{ 1044 {"yaml", ` 1045 aBcD: 1 1046 eF: 1047 gH: 2 1048 iJk: 3 1049 Lm: 1050 nO: 4 1051 P: 1052 Q: 5 1053 R: 6 1054 `}, 1055 {"json", `{ 1056 "aBcD": 1, 1057 "eF": { 1058 "iJk": 3, 1059 "Lm": { 1060 "P": { 1061 "Q": 5, 1062 "R": 6 1063 }, 1064 "nO": 4 1065 }, 1066 "gH": 2 1067 } 1068 }`}, 1069 {"toml", `aBcD = 1 1070 [eF] 1071 gH = 2 1072 iJk = 3 1073 [eF.Lm] 1074 nO = 4 1075 [eF.Lm.P] 1076 Q = 5 1077 R = 6 1078 `}, 1079 } { 1080 doTestCaseInsensitive(t, config.typ, config.content) 1081 } 1082 } 1083 1084 func TestCaseInsensitiveSet(t *testing.T) { 1085 Reset() 1086 m1 := map[string]interface{}{ 1087 "Foo": 32, 1088 "Bar": map[interface{}]interface { 1089 }{ 1090 "ABc": "A", 1091 "cDE": "B"}, 1092 } 1093 1094 m2 := map[string]interface{}{ 1095 "Foo": 52, 1096 "Bar": map[interface{}]interface { 1097 }{ 1098 "bCd": "A", 1099 "eFG": "B"}, 1100 } 1101 1102 Set("Given1", m1) 1103 Set("Number1", 42) 1104 1105 SetDefault("Given2", m2) 1106 SetDefault("Number2", 52) 1107 1108 // Verify SetDefault 1109 if v := Get("number2"); v != 52 { 1110 t.Fatalf("Expected 52 got %q", v) 1111 } 1112 1113 if v := Get("given2.foo"); v != 52 { 1114 t.Fatalf("Expected 52 got %q", v) 1115 } 1116 1117 if v := Get("given2.bar.bcd"); v != "A" { 1118 t.Fatalf("Expected A got %q", v) 1119 } 1120 1121 if _, ok := m2["Foo"]; !ok { 1122 t.Fatal("Input map changed") 1123 } 1124 1125 // Verify Set 1126 if v := Get("number1"); v != 42 { 1127 t.Fatalf("Expected 42 got %q", v) 1128 } 1129 1130 if v := Get("given1.foo"); v != 32 { 1131 t.Fatalf("Expected 32 got %q", v) 1132 } 1133 1134 if v := Get("given1.bar.abc"); v != "A" { 1135 t.Fatalf("Expected A got %q", v) 1136 } 1137 1138 if _, ok := m1["Foo"]; !ok { 1139 t.Fatal("Input map changed") 1140 } 1141 } 1142 1143 func TestParseNested(t *testing.T) { 1144 type duration struct { 1145 Delay time.Duration 1146 } 1147 1148 type item struct { 1149 Name string 1150 Delay time.Duration 1151 Nested duration 1152 } 1153 1154 config := `[[parent]] 1155 delay="100ms" 1156 [parent.nested] 1157 delay="200ms" 1158 ` 1159 initConfig("toml", config) 1160 1161 var items []item 1162 err := v.UnmarshalKey("parent", &items) 1163 if err != nil { 1164 t.Fatalf("unable to decode into struct, %v", err) 1165 } 1166 1167 assert.Equal(t, 1, len(items)) 1168 assert.Equal(t, 100*time.Millisecond, items[0].Delay) 1169 assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay) 1170 } 1171 1172 func doTestCaseInsensitive(t *testing.T, typ, config string) { 1173 initConfig(typ, config) 1174 Set("RfD", true) 1175 assert.Equal(t, true, Get("rfd")) 1176 assert.Equal(t, true, Get("rFD")) 1177 assert.Equal(t, 1, cast.ToInt(Get("abcd"))) 1178 assert.Equal(t, 1, cast.ToInt(Get("Abcd"))) 1179 assert.Equal(t, 2, cast.ToInt(Get("ef.gh"))) 1180 assert.Equal(t, 3, cast.ToInt(Get("ef.ijk"))) 1181 assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no"))) 1182 assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q"))) 1183 1184 } 1185 1186 func BenchmarkGetBool(b *testing.B) { 1187 key := "BenchmarkGetBool" 1188 v = New() 1189 v.Set(key, true) 1190 1191 for i := 0; i < b.N; i++ { 1192 if !v.GetBool(key) { 1193 b.Fatal("GetBool returned false") 1194 } 1195 } 1196 } 1197 1198 func BenchmarkGet(b *testing.B) { 1199 key := "BenchmarkGet" 1200 v = New() 1201 v.Set(key, true) 1202 1203 for i := 0; i < b.N; i++ { 1204 if !v.Get(key).(bool) { 1205 b.Fatal("Get returned false") 1206 } 1207 } 1208 } 1209 1210 // This is the "perfect result" for the above. 1211 func BenchmarkGetBoolFromMap(b *testing.B) { 1212 m := make(map[string]bool) 1213 key := "BenchmarkGetBool" 1214 m[key] = true 1215 1216 for i := 0; i < b.N; i++ { 1217 if !m[key] { 1218 b.Fatal("Map value was false") 1219 } 1220 } 1221 }