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