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