github.com/kusti8/viper@v0.0.0-20170830183736-1c64bdc5d63b/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 ExampleUnmarshalWithMeta() { 280 v := New() 281 type Lib struct { 282 Name string 283 Awesome bool 284 Useful bool 285 } 286 var libsCfg struct { 287 Libs map[string]Lib 288 } 289 v.config = map[string]interface{}{ 290 "libs": map[string]interface{}{ 291 "viper": map[string]interface{}{ 292 "name": "Viper", 293 "awesome": true, 294 "tagline": "Go configuration with fangs!", 295 }, 296 }, 297 "numbers": []int{4, 8, 15, 16, 23, 42}, 298 } 299 meta, _ := v.UnmarshalWithMeta(&libsCfg) 300 // The keys slice is not stable. Sorting them in lexicographical 301 // order allows comparison. 302 sort.Strings(meta.Keys) 303 sort.Strings(meta.Unused) 304 fmt.Printf("%#v\n", meta.Keys) 305 fmt.Printf("%#v", meta.Unused) 306 // Output: 307 // []string{"Libs", "Libs[viper]", "Libs[viper]", "Libs[viper].Awesome", "Libs[viper].Name"} 308 // []string{"Libs[viper].tagline", "numbers"} 309 } 310 311 func ExampleUnmarshalKeyWithMeta() { 312 v := New() 313 var lib struct { 314 Name string 315 Awesome bool 316 Useful bool 317 } 318 v.config = map[string]interface{}{ 319 "libs": map[string]interface{}{ 320 "viper": map[string]interface{}{ 321 "name": "Viper", 322 "awesome": true, 323 "tagline": "Go configuration with fangs!", 324 }, 325 }, 326 } 327 meta, _ := v.UnmarshalKeyWithMeta("libs.viper", &lib) 328 fmt.Println(meta.Keys) 329 fmt.Println(meta.Unused) 330 // Output: 331 // [Name Awesome] 332 // [tagline] 333 } 334 335 func TestUnmarshalKeyWithMeta(t *testing.T) { 336 v := New() 337 v.SetConfigType("yaml") 338 err := v.ReadConfig(bytes.NewBuffer(yamlExample)) 339 if err != nil { 340 t.Fatal(err) 341 } 342 var clothes struct { 343 Jacket string 344 Pants interface{} 345 } 346 meta, err := v.UnmarshalKeyWithMeta("clothing", &clothes) 347 if err != nil { 348 t.Fatal(err) 349 } 350 keys := []string{"Jacket", "Pants"} 351 unused := []string{"trousers"} 352 sort.Strings(meta.Keys) 353 sort.Strings(meta.Unused) 354 assert.Equal(t, keys, meta.Keys, "keys decoded") 355 assert.Equal(t, unused, meta.Unused, "keys in config not in struct") 356 } 357 358 func TestUnmarshalExact(t *testing.T) { 359 vip := New() 360 target := &testUnmarshalExtra{} 361 vip.SetConfigType("yaml") 362 r := bytes.NewReader(yamlExampleWithExtras) 363 vip.ReadConfig(r) 364 err := vip.UnmarshalExact(target) 365 if err == nil { 366 t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields") 367 } 368 } 369 370 func TestOverrides(t *testing.T) { 371 Set("age", 40) 372 assert.Equal(t, 40, Get("age")) 373 } 374 375 func TestDefaultPost(t *testing.T) { 376 assert.NotEqual(t, "NYC", Get("state")) 377 SetDefault("state", "NYC") 378 assert.Equal(t, "NYC", Get("state")) 379 } 380 381 func TestAliases(t *testing.T) { 382 RegisterAlias("years", "age") 383 assert.Equal(t, 40, Get("years")) 384 Set("years", 45) 385 assert.Equal(t, 45, Get("age")) 386 } 387 388 func TestAliasInConfigFile(t *testing.T) { 389 // the config file specifies "beard". If we make this an alias for 390 // "hasbeard", we still want the old config file to work with beard. 391 RegisterAlias("beard", "hasbeard") 392 assert.Equal(t, true, Get("hasbeard")) 393 Set("hasbeard", false) 394 assert.Equal(t, false, Get("beard")) 395 } 396 397 func TestYML(t *testing.T) { 398 initYAML() 399 assert.Equal(t, "steve", Get("name")) 400 } 401 402 func TestJSON(t *testing.T) { 403 initJSON() 404 assert.Equal(t, "0001", Get("id")) 405 } 406 407 func TestProperties(t *testing.T) { 408 initProperties() 409 assert.Equal(t, "0001", Get("p_id")) 410 } 411 412 func TestTOML(t *testing.T) { 413 initTOML() 414 assert.Equal(t, "TOML Example", Get("title")) 415 } 416 417 func TestHCL(t *testing.T) { 418 initHcl() 419 assert.Equal(t, "0001", Get("id")) 420 assert.Equal(t, 0.55, Get("ppu")) 421 assert.Equal(t, "donut", Get("type")) 422 assert.Equal(t, "Cake", Get("name")) 423 Set("id", "0002") 424 assert.Equal(t, "0002", Get("id")) 425 assert.NotEqual(t, "cronut", Get("type")) 426 } 427 428 func TestRemotePrecedence(t *testing.T) { 429 initJSON() 430 431 remote := bytes.NewReader(remoteExample) 432 assert.Equal(t, "0001", Get("id")) 433 unmarshalReader(remote, v.kvstore) 434 assert.Equal(t, "0001", Get("id")) 435 assert.NotEqual(t, "cronut", Get("type")) 436 assert.Equal(t, "remote", Get("newkey")) 437 Set("newkey", "newvalue") 438 assert.NotEqual(t, "remote", Get("newkey")) 439 assert.Equal(t, "newvalue", Get("newkey")) 440 Set("newkey", "remote") 441 } 442 443 func TestEnv(t *testing.T) { 444 initJSON() 445 446 BindEnv("id") 447 BindEnv("f", "FOOD") 448 449 os.Setenv("ID", "13") 450 os.Setenv("FOOD", "apple") 451 os.Setenv("NAME", "crunk") 452 453 assert.Equal(t, "13", Get("id")) 454 assert.Equal(t, "apple", Get("f")) 455 assert.Equal(t, "Cake", Get("name")) 456 457 AutomaticEnv() 458 459 assert.Equal(t, "crunk", Get("name")) 460 461 } 462 463 func TestEnvPrefix(t *testing.T) { 464 initJSON() 465 466 SetEnvPrefix("foo") // will be uppercased automatically 467 BindEnv("id") 468 BindEnv("f", "FOOD") // not using prefix 469 470 os.Setenv("FOO_ID", "13") 471 os.Setenv("FOOD", "apple") 472 os.Setenv("FOO_NAME", "crunk") 473 474 assert.Equal(t, "13", Get("id")) 475 assert.Equal(t, "apple", Get("f")) 476 assert.Equal(t, "Cake", Get("name")) 477 478 AutomaticEnv() 479 480 assert.Equal(t, "crunk", Get("name")) 481 } 482 483 func TestAutoEnv(t *testing.T) { 484 Reset() 485 486 AutomaticEnv() 487 os.Setenv("FOO_BAR", "13") 488 assert.Equal(t, "13", Get("foo_bar")) 489 } 490 491 func TestAutoEnvWithPrefix(t *testing.T) { 492 Reset() 493 494 AutomaticEnv() 495 SetEnvPrefix("Baz") 496 os.Setenv("BAZ_BAR", "13") 497 assert.Equal(t, "13", Get("bar")) 498 } 499 500 func TestSetEnvReplacer(t *testing.T) { 501 Reset() 502 503 AutomaticEnv() 504 os.Setenv("REFRESH_INTERVAL", "30s") 505 506 replacer := strings.NewReplacer("-", "_") 507 SetEnvKeyReplacer(replacer) 508 509 assert.Equal(t, "30s", Get("refresh-interval")) 510 } 511 512 func TestAllKeys(t *testing.T) { 513 initConfigs() 514 515 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"} 516 dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 517 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}}}}} 518 519 var allkeys sort.StringSlice 520 allkeys = AllKeys() 521 allkeys.Sort() 522 ks.Sort() 523 524 assert.Equal(t, ks, allkeys) 525 assert.Equal(t, all, AllSettings()) 526 } 527 528 func TestAllKeysWithEnv(t *testing.T) { 529 v := New() 530 531 // bind and define environment variables (including a nested one) 532 v.BindEnv("id") 533 v.BindEnv("foo.bar") 534 v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) 535 os.Setenv("ID", "13") 536 os.Setenv("FOO_BAR", "baz") 537 538 expectedKeys := sort.StringSlice{"id", "foo.bar"} 539 expectedKeys.Sort() 540 keys := sort.StringSlice(v.AllKeys()) 541 keys.Sort() 542 assert.Equal(t, expectedKeys, keys) 543 } 544 545 func TestAliasesOfAliases(t *testing.T) { 546 Set("Title", "Checking Case") 547 RegisterAlias("Foo", "Bar") 548 RegisterAlias("Bar", "Title") 549 assert.Equal(t, "Checking Case", Get("FOO")) 550 } 551 552 func TestRecursiveAliases(t *testing.T) { 553 RegisterAlias("Baz", "Roo") 554 RegisterAlias("Roo", "baz") 555 } 556 557 func TestUnmarshal(t *testing.T) { 558 SetDefault("port", 1313) 559 Set("name", "Steve") 560 Set("duration", "1s1ms") 561 562 type config struct { 563 Port int 564 Name string 565 Duration time.Duration 566 } 567 568 var C config 569 570 err := Unmarshal(&C) 571 if err != nil { 572 t.Fatalf("unable to decode into struct, %v", err) 573 } 574 575 assert.Equal(t, &config{Name: "Steve", Port: 1313, Duration: time.Second + time.Millisecond}, &C) 576 577 Set("port", 1234) 578 err = Unmarshal(&C) 579 if err != nil { 580 t.Fatalf("unable to decode into struct, %v", err) 581 } 582 assert.Equal(t, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond}, &C) 583 } 584 585 func TestBindPFlags(t *testing.T) { 586 v := New() // create independent Viper object 587 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) 588 589 var testValues = map[string]*string{ 590 "host": nil, 591 "port": nil, 592 "endpoint": nil, 593 } 594 595 var mutatedTestValues = map[string]string{ 596 "host": "localhost", 597 "port": "6060", 598 "endpoint": "/public", 599 } 600 601 for name := range testValues { 602 testValues[name] = flagSet.String(name, "", "test") 603 } 604 605 err := v.BindPFlags(flagSet) 606 if err != nil { 607 t.Fatalf("error binding flag set, %v", err) 608 } 609 610 flagSet.VisitAll(func(flag *pflag.Flag) { 611 flag.Value.Set(mutatedTestValues[flag.Name]) 612 flag.Changed = true 613 }) 614 615 for name, expected := range mutatedTestValues { 616 assert.Equal(t, expected, v.Get(name)) 617 } 618 619 } 620 621 func TestBindPFlagsStringSlice(t *testing.T) { 622 for _, testValue := range []struct { 623 Expected []string 624 Value string 625 }{ 626 {[]string{}, ""}, 627 {[]string{"jeden"}, "jeden"}, 628 {[]string{"dwa", "trzy"}, "dwa,trzy"}, 629 {[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""}} { 630 631 for _, changed := range []bool{true, false} { 632 v := New() // create independent Viper object 633 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) 634 flagSet.StringSlice("stringslice", testValue.Expected, "test") 635 flagSet.Visit(func(f *pflag.Flag) { 636 if len(testValue.Value) > 0 { 637 f.Value.Set(testValue.Value) 638 f.Changed = changed 639 } 640 }) 641 642 err := v.BindPFlags(flagSet) 643 if err != nil { 644 t.Fatalf("error binding flag set, %v", err) 645 } 646 647 type TestStr struct { 648 StringSlice []string 649 } 650 val := &TestStr{} 651 if err := v.Unmarshal(val); err != nil { 652 t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err) 653 } 654 assert.Equal(t, testValue.Expected, val.StringSlice) 655 } 656 } 657 } 658 659 func TestBindPFlag(t *testing.T) { 660 var testString = "testing" 661 var testValue = newStringValue(testString, &testString) 662 663 flag := &pflag.Flag{ 664 Name: "testflag", 665 Value: testValue, 666 Changed: false, 667 } 668 669 BindPFlag("testvalue", flag) 670 671 assert.Equal(t, testString, Get("testvalue")) 672 673 flag.Value.Set("testing_mutate") 674 flag.Changed = true //hack for pflag usage 675 676 assert.Equal(t, "testing_mutate", Get("testvalue")) 677 678 } 679 680 func TestBoundCaseSensitivity(t *testing.T) { 681 assert.Equal(t, "brown", Get("eyes")) 682 683 BindEnv("eYEs", "TURTLE_EYES") 684 os.Setenv("TURTLE_EYES", "blue") 685 686 assert.Equal(t, "blue", Get("eyes")) 687 688 var testString = "green" 689 var testValue = newStringValue(testString, &testString) 690 691 flag := &pflag.Flag{ 692 Name: "eyeballs", 693 Value: testValue, 694 Changed: true, 695 } 696 697 BindPFlag("eYEs", flag) 698 assert.Equal(t, "green", Get("eyes")) 699 700 } 701 702 func TestSizeInBytes(t *testing.T) { 703 input := map[string]uint{ 704 "": 0, 705 "b": 0, 706 "12 bytes": 0, 707 "200000000000gb": 0, 708 "12 b": 12, 709 "43 MB": 43 * (1 << 20), 710 "10mb": 10 * (1 << 20), 711 "1gb": 1 << 30, 712 } 713 714 for str, expected := range input { 715 assert.Equal(t, expected, parseSizeInBytes(str), str) 716 } 717 } 718 719 func TestFindsNestedKeys(t *testing.T) { 720 initConfigs() 721 dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 722 723 Set("super", map[string]interface{}{ 724 "deep": map[string]interface{}{ 725 "nested": "value", 726 }, 727 }) 728 729 expected := map[string]interface{}{ 730 "super": map[string]interface{}{ 731 "deep": map[string]interface{}{ 732 "nested": "value", 733 }, 734 }, 735 "super.deep": map[string]interface{}{ 736 "nested": "value", 737 }, 738 "super.deep.nested": "value", 739 "owner.organization": "MongoDB", 740 "batters.batter": []interface{}{ 741 map[string]interface{}{ 742 "type": "Regular", 743 }, 744 map[string]interface{}{ 745 "type": "Chocolate", 746 }, 747 map[string]interface{}{ 748 "type": "Blueberry", 749 }, 750 map[string]interface{}{ 751 "type": "Devil's Food", 752 }, 753 }, 754 "hobbies": []interface{}{ 755 "skateboarding", "snowboarding", "go", 756 }, 757 "title": "TOML Example", 758 "newkey": "remote", 759 "batters": map[string]interface{}{ 760 "batter": []interface{}{ 761 map[string]interface{}{ 762 "type": "Regular", 763 }, 764 map[string]interface{}{ 765 "type": "Chocolate", 766 }, map[string]interface{}{ 767 "type": "Blueberry", 768 }, map[string]interface{}{ 769 "type": "Devil's Food", 770 }, 771 }, 772 }, 773 "eyes": "brown", 774 "age": 35, 775 "owner": map[string]interface{}{ 776 "organization": "MongoDB", 777 "bio": "MongoDB Chief Developer Advocate & Hacker at Large", 778 "dob": dob, 779 }, 780 "owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large", 781 "type": "donut", 782 "id": "0001", 783 "name": "Cake", 784 "hacker": true, 785 "ppu": 0.55, 786 "clothing": map[string]interface{}{ 787 "jacket": "leather", 788 "trousers": "denim", 789 "pants": map[string]interface{}{ 790 "size": "large", 791 }, 792 }, 793 "clothing.jacket": "leather", 794 "clothing.pants.size": "large", 795 "clothing.trousers": "denim", 796 "owner.dob": dob, 797 "beard": true, 798 "foos": []map[string]interface{}{ 799 map[string]interface{}{ 800 "foo": []map[string]interface{}{ 801 map[string]interface{}{ 802 "key": 1, 803 }, 804 map[string]interface{}{ 805 "key": 2, 806 }, 807 map[string]interface{}{ 808 "key": 3, 809 }, 810 map[string]interface{}{ 811 "key": 4, 812 }, 813 }, 814 }, 815 }, 816 } 817 818 for key, expectedValue := range expected { 819 820 assert.Equal(t, expectedValue, v.Get(key)) 821 } 822 823 } 824 825 func TestReadBufConfig(t *testing.T) { 826 v := New() 827 v.SetConfigType("yaml") 828 v.ReadConfig(bytes.NewBuffer(yamlExample)) 829 t.Log(v.AllKeys()) 830 831 assert.True(t, v.InConfig("name")) 832 assert.False(t, v.InConfig("state")) 833 assert.Equal(t, "steve", v.Get("name")) 834 assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies")) 835 assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing")) 836 assert.Equal(t, 35, v.Get("age")) 837 } 838 839 func TestIsSet(t *testing.T) { 840 v := New() 841 v.SetConfigType("yaml") 842 v.ReadConfig(bytes.NewBuffer(yamlExample)) 843 assert.True(t, v.IsSet("clothing.jacket")) 844 assert.False(t, v.IsSet("clothing.jackets")) 845 assert.False(t, v.IsSet("helloworld")) 846 v.Set("helloworld", "fubar") 847 assert.True(t, v.IsSet("helloworld")) 848 } 849 850 func TestDirsSearch(t *testing.T) { 851 852 root, config, cleanup := initDirs(t) 853 defer cleanup() 854 855 v := New() 856 v.SetConfigName(config) 857 v.SetDefault(`key`, `default`) 858 859 entries, err := ioutil.ReadDir(root) 860 for _, e := range entries { 861 if e.IsDir() { 862 v.AddConfigPath(e.Name()) 863 } 864 } 865 866 err = v.ReadInConfig() 867 assert.Nil(t, err) 868 869 assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`)) 870 } 871 872 func TestWrongDirsSearchNotFound(t *testing.T) { 873 874 _, config, cleanup := initDirs(t) 875 defer cleanup() 876 877 v := New() 878 v.SetConfigName(config) 879 v.SetDefault(`key`, `default`) 880 881 v.AddConfigPath(`whattayoutalkingbout`) 882 v.AddConfigPath(`thispathaintthere`) 883 884 err := v.ReadInConfig() 885 assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) 886 887 // Even though config did not load and the error might have 888 // been ignored by the client, the default still loads 889 assert.Equal(t, `default`, v.GetString(`key`)) 890 } 891 892 func TestWrongDirsSearchNotFoundForMerge(t *testing.T) { 893 894 _, config, cleanup := initDirs(t) 895 defer cleanup() 896 897 v := New() 898 v.SetConfigName(config) 899 v.SetDefault(`key`, `default`) 900 901 v.AddConfigPath(`whattayoutalkingbout`) 902 v.AddConfigPath(`thispathaintthere`) 903 904 err := v.MergeInConfig() 905 assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) 906 907 // Even though config did not load and the error might have 908 // been ignored by the client, the default still loads 909 assert.Equal(t, `default`, v.GetString(`key`)) 910 } 911 912 func TestSub(t *testing.T) { 913 v := New() 914 v.SetConfigType("yaml") 915 v.ReadConfig(bytes.NewBuffer(yamlExample)) 916 917 subv := v.Sub("clothing") 918 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size")) 919 920 subv = v.Sub("clothing.pants") 921 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size")) 922 923 subv = v.Sub("clothing.pants.size") 924 assert.Equal(t, (*Viper)(nil), subv) 925 926 subv = v.Sub("missing.key") 927 assert.Equal(t, (*Viper)(nil), subv) 928 } 929 930 var hclWriteExpected = []byte(`"foos" = { 931 "foo" = { 932 "key" = 1 933 } 934 935 "foo" = { 936 "key" = 2 937 } 938 939 "foo" = { 940 "key" = 3 941 } 942 943 "foo" = { 944 "key" = 4 945 } 946 } 947 948 "id" = "0001" 949 950 "name" = "Cake" 951 952 "ppu" = 0.55 953 954 "type" = "donut"`) 955 956 func TestWriteConfigHCL(t *testing.T) { 957 v := New() 958 fs := afero.NewMemMapFs() 959 v.SetFs(fs) 960 v.SetConfigName("c") 961 v.SetConfigType("hcl") 962 err := v.ReadConfig(bytes.NewBuffer(hclExample)) 963 if err != nil { 964 t.Fatal(err) 965 } 966 if err := v.WriteConfigAs("c.hcl"); err != nil { 967 t.Fatal(err) 968 } 969 read, err := afero.ReadFile(fs, "c.hcl") 970 if err != nil { 971 t.Fatal(err) 972 } 973 assert.Equal(t, hclWriteExpected, read) 974 } 975 976 var jsonWriteExpected = []byte(`{ 977 "batters": { 978 "batter": [ 979 { 980 "type": "Regular" 981 }, 982 { 983 "type": "Chocolate" 984 }, 985 { 986 "type": "Blueberry" 987 }, 988 { 989 "type": "Devil's Food" 990 } 991 ] 992 }, 993 "id": "0001", 994 "name": "Cake", 995 "ppu": 0.55, 996 "type": "donut" 997 }`) 998 999 func TestWriteConfigJson(t *testing.T) { 1000 v := New() 1001 fs := afero.NewMemMapFs() 1002 v.SetFs(fs) 1003 v.SetConfigName("c") 1004 v.SetConfigType("json") 1005 err := v.ReadConfig(bytes.NewBuffer(jsonExample)) 1006 if err != nil { 1007 t.Fatal(err) 1008 } 1009 if err := v.WriteConfigAs("c.json"); err != nil { 1010 t.Fatal(err) 1011 } 1012 read, err := afero.ReadFile(fs, "c.json") 1013 if err != nil { 1014 t.Fatal(err) 1015 } 1016 assert.Equal(t, jsonWriteExpected, read) 1017 } 1018 1019 var propertiesWriteExpected = []byte(`p_id = 0001 1020 p_type = donut 1021 p_name = Cake 1022 p_ppu = 0.55 1023 p_batters.batter.type = Regular 1024 `) 1025 1026 func TestWriteConfigProperties(t *testing.T) { 1027 v := New() 1028 fs := afero.NewMemMapFs() 1029 v.SetFs(fs) 1030 v.SetConfigName("c") 1031 v.SetConfigType("properties") 1032 err := v.ReadConfig(bytes.NewBuffer(propertiesExample)) 1033 if err != nil { 1034 t.Fatal(err) 1035 } 1036 if err := v.WriteConfigAs("c.properties"); err != nil { 1037 t.Fatal(err) 1038 } 1039 read, err := afero.ReadFile(fs, "c.properties") 1040 if err != nil { 1041 t.Fatal(err) 1042 } 1043 assert.Equal(t, propertiesWriteExpected, read) 1044 } 1045 1046 func TestWriteConfigTOML(t *testing.T) { 1047 fs := afero.NewMemMapFs() 1048 v := New() 1049 v.SetFs(fs) 1050 v.SetConfigName("c") 1051 v.SetConfigType("toml") 1052 err := v.ReadConfig(bytes.NewBuffer(tomlExample)) 1053 if err != nil { 1054 t.Fatal(err) 1055 } 1056 if err := v.WriteConfigAs("c.toml"); err != nil { 1057 t.Fatal(err) 1058 } 1059 1060 // The TOML String method does not order the contents. 1061 // Therefore, we must read the generated file and compare the data. 1062 v2 := New() 1063 v2.SetFs(fs) 1064 v2.SetConfigName("c") 1065 v2.SetConfigType("toml") 1066 v2.SetConfigFile("c.toml") 1067 err = v2.ReadInConfig() 1068 if err != nil { 1069 t.Fatal(err) 1070 } 1071 1072 assert.Equal(t, v.GetString("title"), v2.GetString("title")) 1073 assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio")) 1074 assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob")) 1075 assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization")) 1076 } 1077 1078 var yamlWriteExpected = []byte(`age: 35 1079 beard: true 1080 clothing: 1081 jacket: leather 1082 pants: 1083 size: large 1084 trousers: denim 1085 eyes: brown 1086 hacker: true 1087 hobbies: 1088 - skateboarding 1089 - snowboarding 1090 - go 1091 name: steve 1092 `) 1093 1094 func TestWriteConfigYAML(t *testing.T) { 1095 v := New() 1096 fs := afero.NewMemMapFs() 1097 v.SetFs(fs) 1098 v.SetConfigName("c") 1099 v.SetConfigType("yaml") 1100 err := v.ReadConfig(bytes.NewBuffer(yamlExample)) 1101 if err != nil { 1102 t.Fatal(err) 1103 } 1104 if err := v.WriteConfigAs("c.yaml"); err != nil { 1105 t.Fatal(err) 1106 } 1107 read, err := afero.ReadFile(fs, "c.yaml") 1108 if err != nil { 1109 t.Fatal(err) 1110 } 1111 assert.Equal(t, yamlWriteExpected, read) 1112 } 1113 1114 var yamlMergeExampleTgt = []byte(` 1115 hello: 1116 pop: 37890 1117 lagrenum: 765432101234567 1118 world: 1119 - us 1120 - uk 1121 - fr 1122 - de 1123 `) 1124 1125 var yamlMergeExampleSrc = []byte(` 1126 hello: 1127 pop: 45000 1128 lagrenum: 7654321001234567 1129 universe: 1130 - mw 1131 - ad 1132 fu: bar 1133 `) 1134 1135 func TestMergeConfig(t *testing.T) { 1136 v := New() 1137 v.SetConfigType("yml") 1138 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1139 t.Fatal(err) 1140 } 1141 1142 if pop := v.GetInt("hello.pop"); pop != 37890 { 1143 t.Fatalf("pop != 37890, = %d", pop) 1144 } 1145 1146 if pop := v.GetInt("hello.lagrenum"); pop != 765432101234567 { 1147 t.Fatalf("lagrenum != 765432101234567, = %d", pop) 1148 } 1149 1150 if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) { 1151 t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop) 1152 } 1153 1154 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1155 t.Fatalf("len(world) != 4, = %d", len(world)) 1156 } 1157 1158 if fu := v.GetString("fu"); fu != "" { 1159 t.Fatalf("fu != \"\", = %s", fu) 1160 } 1161 1162 if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 1163 t.Fatal(err) 1164 } 1165 1166 if pop := v.GetInt("hello.pop"); pop != 45000 { 1167 t.Fatalf("pop != 45000, = %d", pop) 1168 } 1169 1170 if pop := v.GetInt("hello.lagrenum"); pop != 7654321001234567 { 1171 t.Fatalf("lagrenum != 7654321001234567, = %d", pop) 1172 } 1173 1174 if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) { 1175 t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop) 1176 } 1177 1178 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1179 t.Fatalf("len(world) != 4, = %d", len(world)) 1180 } 1181 1182 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 1183 t.Fatalf("len(universe) != 2, = %d", len(universe)) 1184 } 1185 1186 if fu := v.GetString("fu"); fu != "bar" { 1187 t.Fatalf("fu != \"bar\", = %s", fu) 1188 } 1189 } 1190 1191 func TestMergeConfigNoMerge(t *testing.T) { 1192 v := New() 1193 v.SetConfigType("yml") 1194 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1195 t.Fatal(err) 1196 } 1197 1198 if pop := v.GetInt("hello.pop"); pop != 37890 { 1199 t.Fatalf("pop != 37890, = %d", pop) 1200 } 1201 1202 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1203 t.Fatalf("len(world) != 4, = %d", len(world)) 1204 } 1205 1206 if fu := v.GetString("fu"); fu != "" { 1207 t.Fatalf("fu != \"\", = %s", fu) 1208 } 1209 1210 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 1211 t.Fatal(err) 1212 } 1213 1214 if pop := v.GetInt("hello.pop"); pop != 45000 { 1215 t.Fatalf("pop != 45000, = %d", pop) 1216 } 1217 1218 if world := v.GetStringSlice("hello.world"); len(world) != 0 { 1219 t.Fatalf("len(world) != 0, = %d", len(world)) 1220 } 1221 1222 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 1223 t.Fatalf("len(universe) != 2, = %d", len(universe)) 1224 } 1225 1226 if fu := v.GetString("fu"); fu != "bar" { 1227 t.Fatalf("fu != \"bar\", = %s", fu) 1228 } 1229 } 1230 1231 func TestUnmarshalingWithAliases(t *testing.T) { 1232 v := New() 1233 v.SetDefault("ID", 1) 1234 v.Set("name", "Steve") 1235 v.Set("lastname", "Owen") 1236 1237 v.RegisterAlias("UserID", "ID") 1238 v.RegisterAlias("Firstname", "name") 1239 v.RegisterAlias("Surname", "lastname") 1240 1241 type config struct { 1242 ID int 1243 FirstName string 1244 Surname string 1245 } 1246 1247 var C config 1248 err := v.Unmarshal(&C) 1249 if err != nil { 1250 t.Fatalf("unable to decode into struct, %v", err) 1251 } 1252 1253 assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C) 1254 } 1255 1256 func TestSetConfigNameClearsFileCache(t *testing.T) { 1257 SetConfigFile("/tmp/config.yaml") 1258 SetConfigName("default") 1259 f, err := v.getConfigFile() 1260 if err == nil { 1261 t.Fatalf("config file cache should have been cleared") 1262 } 1263 assert.Empty(t, f) 1264 } 1265 1266 func TestShadowedNestedValue(t *testing.T) { 1267 1268 config := `name: steve 1269 clothing: 1270 jacket: leather 1271 trousers: denim 1272 pants: 1273 size: large 1274 ` 1275 initConfig("yaml", config) 1276 1277 assert.Equal(t, "steve", GetString("name")) 1278 1279 polyester := "polyester" 1280 SetDefault("clothing.shirt", polyester) 1281 SetDefault("clothing.jacket.price", 100) 1282 1283 assert.Equal(t, "leather", GetString("clothing.jacket")) 1284 assert.Nil(t, Get("clothing.jacket.price")) 1285 assert.Equal(t, polyester, GetString("clothing.shirt")) 1286 1287 clothingSettings := AllSettings()["clothing"].(map[string]interface{}) 1288 assert.Equal(t, "leather", clothingSettings["jacket"]) 1289 assert.Equal(t, polyester, clothingSettings["shirt"]) 1290 } 1291 1292 func TestDotParameter(t *testing.T) { 1293 initJSON() 1294 // shoud take precedence over batters defined in jsonExample 1295 r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`)) 1296 unmarshalReader(r, v.config) 1297 1298 actual := Get("batters.batter") 1299 expected := []interface{}{map[string]interface{}{"type": "Small"}} 1300 assert.Equal(t, expected, actual) 1301 } 1302 1303 func TestCaseInsensitive(t *testing.T) { 1304 for _, config := range []struct { 1305 typ string 1306 content string 1307 }{ 1308 {"yaml", ` 1309 aBcD: 1 1310 eF: 1311 gH: 2 1312 iJk: 3 1313 Lm: 1314 nO: 4 1315 P: 1316 Q: 5 1317 R: 6 1318 `}, 1319 {"json", `{ 1320 "aBcD": 1, 1321 "eF": { 1322 "iJk": 3, 1323 "Lm": { 1324 "P": { 1325 "Q": 5, 1326 "R": 6 1327 }, 1328 "nO": 4 1329 }, 1330 "gH": 2 1331 } 1332 }`}, 1333 {"toml", `aBcD = 1 1334 [eF] 1335 gH = 2 1336 iJk = 3 1337 [eF.Lm] 1338 nO = 4 1339 [eF.Lm.P] 1340 Q = 5 1341 R = 6 1342 `}, 1343 } { 1344 doTestCaseInsensitive(t, config.typ, config.content) 1345 } 1346 } 1347 1348 func TestCaseInsensitiveSet(t *testing.T) { 1349 Reset() 1350 m1 := map[string]interface{}{ 1351 "Foo": 32, 1352 "Bar": map[interface{}]interface { 1353 }{ 1354 "ABc": "A", 1355 "cDE": "B"}, 1356 } 1357 1358 m2 := map[string]interface{}{ 1359 "Foo": 52, 1360 "Bar": map[interface{}]interface { 1361 }{ 1362 "bCd": "A", 1363 "eFG": "B"}, 1364 } 1365 1366 Set("Given1", m1) 1367 Set("Number1", 42) 1368 1369 SetDefault("Given2", m2) 1370 SetDefault("Number2", 52) 1371 1372 // Verify SetDefault 1373 if v := Get("number2"); v != 52 { 1374 t.Fatalf("Expected 52 got %q", v) 1375 } 1376 1377 if v := Get("given2.foo"); v != 52 { 1378 t.Fatalf("Expected 52 got %q", v) 1379 } 1380 1381 if v := Get("given2.bar.bcd"); v != "A" { 1382 t.Fatalf("Expected A got %q", v) 1383 } 1384 1385 if _, ok := m2["Foo"]; !ok { 1386 t.Fatal("Input map changed") 1387 } 1388 1389 // Verify Set 1390 if v := Get("number1"); v != 42 { 1391 t.Fatalf("Expected 42 got %q", v) 1392 } 1393 1394 if v := Get("given1.foo"); v != 32 { 1395 t.Fatalf("Expected 32 got %q", v) 1396 } 1397 1398 if v := Get("given1.bar.abc"); v != "A" { 1399 t.Fatalf("Expected A got %q", v) 1400 } 1401 1402 if _, ok := m1["Foo"]; !ok { 1403 t.Fatal("Input map changed") 1404 } 1405 } 1406 1407 func TestParseNested(t *testing.T) { 1408 type duration struct { 1409 Delay time.Duration 1410 } 1411 1412 type item struct { 1413 Name string 1414 Delay time.Duration 1415 Nested duration 1416 } 1417 1418 config := `[[parent]] 1419 delay="100ms" 1420 [parent.nested] 1421 delay="200ms" 1422 ` 1423 initConfig("toml", config) 1424 1425 var items []item 1426 err := v.UnmarshalKey("parent", &items) 1427 if err != nil { 1428 t.Fatalf("unable to decode into struct, %v", err) 1429 } 1430 1431 assert.Equal(t, 1, len(items)) 1432 assert.Equal(t, 100*time.Millisecond, items[0].Delay) 1433 assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay) 1434 } 1435 1436 func doTestCaseInsensitive(t *testing.T, typ, config string) { 1437 initConfig(typ, config) 1438 Set("RfD", true) 1439 assert.Equal(t, true, Get("rfd")) 1440 assert.Equal(t, true, Get("rFD")) 1441 assert.Equal(t, 1, cast.ToInt(Get("abcd"))) 1442 assert.Equal(t, 1, cast.ToInt(Get("Abcd"))) 1443 assert.Equal(t, 2, cast.ToInt(Get("ef.gh"))) 1444 assert.Equal(t, 3, cast.ToInt(Get("ef.ijk"))) 1445 assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no"))) 1446 assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q"))) 1447 1448 } 1449 1450 func BenchmarkGetBool(b *testing.B) { 1451 key := "BenchmarkGetBool" 1452 v = New() 1453 v.Set(key, true) 1454 1455 for i := 0; i < b.N; i++ { 1456 if !v.GetBool(key) { 1457 b.Fatal("GetBool returned false") 1458 } 1459 } 1460 } 1461 1462 func BenchmarkGet(b *testing.B) { 1463 key := "BenchmarkGet" 1464 v = New() 1465 v.Set(key, true) 1466 1467 for i := 0; i < b.N; i++ { 1468 if !v.Get(key).(bool) { 1469 b.Fatal("Get returned false") 1470 } 1471 } 1472 } 1473 1474 // This is the "perfect result" for the above. 1475 func BenchmarkGetBoolFromMap(b *testing.B) { 1476 m := make(map[string]bool) 1477 key := "BenchmarkGetBool" 1478 m[key] = true 1479 1480 for i := 0; i < b.N; i++ { 1481 if !m[key] { 1482 b.Fatal("Map value was false") 1483 } 1484 } 1485 }