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