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