github.com/jyny/viper@v1.4.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 "io" 12 "io/ioutil" 13 "os" 14 "os/exec" 15 "path" 16 "reflect" 17 "runtime" 18 "sort" 19 "strings" 20 "sync" 21 "testing" 22 "time" 23 24 "github.com/fsnotify/fsnotify" 25 "github.com/mitchellh/mapstructure" 26 "github.com/spf13/afero" 27 "github.com/spf13/cast" 28 29 "github.com/spf13/pflag" 30 "github.com/stretchr/testify/assert" 31 "github.com/stretchr/testify/require" 32 ) 33 34 var yamlExample = []byte(`Hacker: true 35 name: steve 36 hobbies: 37 - skateboarding 38 - snowboarding 39 - go 40 clothing: 41 jacket: leather 42 trousers: denim 43 pants: 44 size: large 45 age: 35 46 eyes : brown 47 beard: true 48 `) 49 50 var yamlExampleWithExtras = []byte(`Existing: true 51 Bogus: true 52 `) 53 54 type testUnmarshalExtra struct { 55 Existing bool 56 } 57 58 var tomlExample = []byte(` 59 title = "TOML Example" 60 61 [owner] 62 organization = "MongoDB" 63 Bio = "MongoDB Chief Developer Advocate & Hacker at Large" 64 dob = 1979-05-27T07:32:00Z # First class dates? Why not?`) 65 66 var dotenvExample = []byte(` 67 TITLE_DOTENV="DotEnv Example" 68 TYPE_DOTENV=donut 69 NAME_DOTENV=Cake`) 70 71 var jsonExample = []byte(`{ 72 "id": "0001", 73 "type": "donut", 74 "name": "Cake", 75 "ppu": 0.55, 76 "batters": { 77 "batter": [ 78 { "type": "Regular" }, 79 { "type": "Chocolate" }, 80 { "type": "Blueberry" }, 81 { "type": "Devil's Food" } 82 ] 83 } 84 }`) 85 86 var hclExample = []byte(` 87 id = "0001" 88 type = "donut" 89 name = "Cake" 90 ppu = 0.55 91 foos { 92 foo { 93 key = 1 94 } 95 foo { 96 key = 2 97 } 98 foo { 99 key = 3 100 } 101 foo { 102 key = 4 103 } 104 }`) 105 106 var propertiesExample = []byte(` 107 p_id: 0001 108 p_type: donut 109 p_name: Cake 110 p_ppu: 0.55 111 p_batters.batter.type: Regular 112 `) 113 114 var remoteExample = []byte(`{ 115 "id":"0002", 116 "type":"cronut", 117 "newkey":"remote" 118 }`) 119 120 func initConfigs() { 121 Reset() 122 var r io.Reader 123 SetConfigType("yaml") 124 r = bytes.NewReader(yamlExample) 125 unmarshalReader(r, v.config) 126 127 SetConfigType("json") 128 r = bytes.NewReader(jsonExample) 129 unmarshalReader(r, v.config) 130 131 SetConfigType("hcl") 132 r = bytes.NewReader(hclExample) 133 unmarshalReader(r, v.config) 134 135 SetConfigType("properties") 136 r = bytes.NewReader(propertiesExample) 137 unmarshalReader(r, v.config) 138 139 SetConfigType("toml") 140 r = bytes.NewReader(tomlExample) 141 unmarshalReader(r, v.config) 142 143 SetConfigType("env") 144 r = bytes.NewReader(dotenvExample) 145 unmarshalReader(r, v.config) 146 147 SetConfigType("json") 148 remote := bytes.NewReader(remoteExample) 149 unmarshalReader(remote, v.kvstore) 150 } 151 152 func initConfig(typ, config string) { 153 Reset() 154 SetConfigType(typ) 155 r := strings.NewReader(config) 156 157 if err := unmarshalReader(r, v.config); err != nil { 158 panic(err) 159 } 160 } 161 162 func initYAML() { 163 initConfig("yaml", string(yamlExample)) 164 } 165 166 func initJSON() { 167 Reset() 168 SetConfigType("json") 169 r := bytes.NewReader(jsonExample) 170 171 unmarshalReader(r, v.config) 172 } 173 174 func initProperties() { 175 Reset() 176 SetConfigType("properties") 177 r := bytes.NewReader(propertiesExample) 178 179 unmarshalReader(r, v.config) 180 } 181 182 func initTOML() { 183 Reset() 184 SetConfigType("toml") 185 r := bytes.NewReader(tomlExample) 186 187 unmarshalReader(r, v.config) 188 } 189 190 func initDotEnv() { 191 Reset() 192 SetConfigType("env") 193 r := bytes.NewReader(dotenvExample) 194 195 unmarshalReader(r, v.config) 196 } 197 198 func initHcl() { 199 Reset() 200 SetConfigType("hcl") 201 r := bytes.NewReader(hclExample) 202 203 unmarshalReader(r, v.config) 204 } 205 206 // make directories for testing 207 func initDirs(t *testing.T) (string, string, func()) { 208 209 var ( 210 testDirs = []string{`a a`, `b`, `c\c`, `D_`} 211 config = `improbable` 212 ) 213 214 root, err := ioutil.TempDir("", "") 215 216 cleanup := true 217 defer func() { 218 if cleanup { 219 os.Chdir("..") 220 os.RemoveAll(root) 221 } 222 }() 223 224 assert.Nil(t, err) 225 226 err = os.Chdir(root) 227 assert.Nil(t, err) 228 229 for _, dir := range testDirs { 230 err = os.Mkdir(dir, 0750) 231 assert.Nil(t, err) 232 233 err = ioutil.WriteFile( 234 path.Join(dir, config+".toml"), 235 []byte("key = \"value is "+dir+"\"\n"), 236 0640) 237 assert.Nil(t, err) 238 } 239 240 cleanup = false 241 return root, config, func() { 242 os.Chdir("..") 243 os.RemoveAll(root) 244 } 245 } 246 247 //stubs for PFlag Values 248 type stringValue string 249 250 func newStringValue(val string, p *string) *stringValue { 251 *p = val 252 return (*stringValue)(p) 253 } 254 255 func (s *stringValue) Set(val string) error { 256 *s = stringValue(val) 257 return nil 258 } 259 260 func (s *stringValue) Type() string { 261 return "string" 262 } 263 264 func (s *stringValue) String() string { 265 return string(*s) 266 } 267 268 func TestBasics(t *testing.T) { 269 SetConfigFile("/tmp/config.yaml") 270 filename, err := v.getConfigFile() 271 assert.Equal(t, "/tmp/config.yaml", filename) 272 assert.NoError(t, err) 273 } 274 275 func TestDefault(t *testing.T) { 276 SetDefault("age", 45) 277 assert.Equal(t, 45, Get("age")) 278 279 SetDefault("clothing.jacket", "slacks") 280 assert.Equal(t, "slacks", Get("clothing.jacket")) 281 282 SetConfigType("yaml") 283 err := ReadConfig(bytes.NewBuffer(yamlExample)) 284 285 assert.NoError(t, err) 286 assert.Equal(t, "leather", Get("clothing.jacket")) 287 } 288 289 func TestUnmarshaling(t *testing.T) { 290 SetConfigType("yaml") 291 r := bytes.NewReader(yamlExample) 292 293 unmarshalReader(r, v.config) 294 assert.True(t, InConfig("name")) 295 assert.False(t, InConfig("state")) 296 assert.Equal(t, "steve", Get("name")) 297 assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies")) 298 assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing")) 299 assert.Equal(t, 35, Get("age")) 300 } 301 302 func TestUnmarshalExact(t *testing.T) { 303 vip := New() 304 target := &testUnmarshalExtra{} 305 vip.SetConfigType("yaml") 306 r := bytes.NewReader(yamlExampleWithExtras) 307 vip.ReadConfig(r) 308 err := vip.UnmarshalExact(target) 309 if err == nil { 310 t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields") 311 } 312 } 313 314 func TestOverrides(t *testing.T) { 315 Set("age", 40) 316 assert.Equal(t, 40, Get("age")) 317 } 318 319 func TestDefaultPost(t *testing.T) { 320 assert.NotEqual(t, "NYC", Get("state")) 321 SetDefault("state", "NYC") 322 assert.Equal(t, "NYC", Get("state")) 323 } 324 325 func TestAliases(t *testing.T) { 326 RegisterAlias("years", "age") 327 assert.Equal(t, 40, Get("years")) 328 Set("years", 45) 329 assert.Equal(t, 45, Get("age")) 330 } 331 332 func TestAliasInConfigFile(t *testing.T) { 333 // the config file specifies "beard". If we make this an alias for 334 // "hasbeard", we still want the old config file to work with beard. 335 RegisterAlias("beard", "hasbeard") 336 assert.Equal(t, true, Get("hasbeard")) 337 Set("hasbeard", false) 338 assert.Equal(t, false, Get("beard")) 339 } 340 341 func TestYML(t *testing.T) { 342 initYAML() 343 assert.Equal(t, "steve", Get("name")) 344 } 345 346 func TestJSON(t *testing.T) { 347 initJSON() 348 assert.Equal(t, "0001", Get("id")) 349 } 350 351 func TestProperties(t *testing.T) { 352 initProperties() 353 assert.Equal(t, "0001", Get("p_id")) 354 } 355 356 func TestTOML(t *testing.T) { 357 initTOML() 358 assert.Equal(t, "TOML Example", Get("title")) 359 } 360 361 func TestDotEnv(t *testing.T) { 362 initDotEnv() 363 assert.Equal(t, "DotEnv Example", Get("title_dotenv")) 364 } 365 366 func TestHCL(t *testing.T) { 367 initHcl() 368 assert.Equal(t, "0001", Get("id")) 369 assert.Equal(t, 0.55, Get("ppu")) 370 assert.Equal(t, "donut", Get("type")) 371 assert.Equal(t, "Cake", Get("name")) 372 Set("id", "0002") 373 assert.Equal(t, "0002", Get("id")) 374 assert.NotEqual(t, "cronut", Get("type")) 375 } 376 377 func TestRemotePrecedence(t *testing.T) { 378 initJSON() 379 380 remote := bytes.NewReader(remoteExample) 381 assert.Equal(t, "0001", Get("id")) 382 unmarshalReader(remote, v.kvstore) 383 assert.Equal(t, "0001", Get("id")) 384 assert.NotEqual(t, "cronut", Get("type")) 385 assert.Equal(t, "remote", Get("newkey")) 386 Set("newkey", "newvalue") 387 assert.NotEqual(t, "remote", Get("newkey")) 388 assert.Equal(t, "newvalue", Get("newkey")) 389 Set("newkey", "remote") 390 } 391 392 func TestEnv(t *testing.T) { 393 initJSON() 394 395 BindEnv("id") 396 BindEnv("f", "FOOD") 397 398 os.Setenv("ID", "13") 399 os.Setenv("FOOD", "apple") 400 os.Setenv("NAME", "crunk") 401 402 assert.Equal(t, "13", Get("id")) 403 assert.Equal(t, "apple", Get("f")) 404 assert.Equal(t, "Cake", Get("name")) 405 406 AutomaticEnv() 407 408 assert.Equal(t, "crunk", Get("name")) 409 410 } 411 412 func TestEmptyEnv(t *testing.T) { 413 initJSON() 414 415 BindEnv("type") // Empty environment variable 416 BindEnv("name") // Bound, but not set environment variable 417 418 os.Clearenv() 419 420 os.Setenv("TYPE", "") 421 422 assert.Equal(t, "donut", Get("type")) 423 assert.Equal(t, "Cake", Get("name")) 424 } 425 426 func TestEmptyEnv_Allowed(t *testing.T) { 427 initJSON() 428 429 AllowEmptyEnv(true) 430 431 BindEnv("type") // Empty environment variable 432 BindEnv("name") // Bound, but not set environment variable 433 434 os.Clearenv() 435 436 os.Setenv("TYPE", "") 437 438 assert.Equal(t, "", Get("type")) 439 assert.Equal(t, "Cake", Get("name")) 440 } 441 442 func TestEnvPrefix(t *testing.T) { 443 initJSON() 444 445 SetEnvPrefix("foo") // will be uppercased automatically 446 BindEnv("id") 447 BindEnv("f", "FOOD") // not using prefix 448 449 os.Setenv("FOO_ID", "13") 450 os.Setenv("FOOD", "apple") 451 os.Setenv("FOO_NAME", "crunk") 452 453 assert.Equal(t, "13", Get("id")) 454 assert.Equal(t, "apple", Get("f")) 455 assert.Equal(t, "Cake", Get("name")) 456 457 AutomaticEnv() 458 459 assert.Equal(t, "crunk", Get("name")) 460 } 461 462 func TestAutoEnv(t *testing.T) { 463 Reset() 464 465 AutomaticEnv() 466 os.Setenv("FOO_BAR", "13") 467 assert.Equal(t, "13", Get("foo_bar")) 468 } 469 470 func TestAutoEnvWithPrefix(t *testing.T) { 471 Reset() 472 473 AutomaticEnv() 474 SetEnvPrefix("Baz") 475 os.Setenv("BAZ_BAR", "13") 476 assert.Equal(t, "13", Get("bar")) 477 } 478 479 func TestSetEnvKeyReplacer(t *testing.T) { 480 Reset() 481 482 AutomaticEnv() 483 os.Setenv("REFRESH_INTERVAL", "30s") 484 485 replacer := strings.NewReplacer("-", "_") 486 SetEnvKeyReplacer(replacer) 487 488 assert.Equal(t, "30s", Get("refresh-interval")) 489 } 490 491 func TestAllKeys(t *testing.T) { 492 initConfigs() 493 494 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", 495 "title_dotenv", "type_dotenv", "name_dotenv", 496 } 497 dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 498 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}}}}, "title_dotenv": "DotEnv Example", "type_dotenv": "donut", "name_dotenv": "Cake"} 499 500 allkeys := sort.StringSlice(AllKeys()) 501 allkeys.Sort() 502 ks.Sort() 503 504 assert.Equal(t, ks, allkeys) 505 assert.Equal(t, all, AllSettings()) 506 } 507 508 func TestAllKeysWithEnv(t *testing.T) { 509 v := New() 510 511 // bind and define environment variables (including a nested one) 512 v.BindEnv("id") 513 v.BindEnv("foo.bar") 514 v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) 515 os.Setenv("ID", "13") 516 os.Setenv("FOO_BAR", "baz") 517 518 expectedKeys := sort.StringSlice{"id", "foo.bar"} 519 expectedKeys.Sort() 520 keys := sort.StringSlice(v.AllKeys()) 521 keys.Sort() 522 assert.Equal(t, expectedKeys, keys) 523 } 524 525 func TestAliasesOfAliases(t *testing.T) { 526 Set("Title", "Checking Case") 527 RegisterAlias("Foo", "Bar") 528 RegisterAlias("Bar", "Title") 529 assert.Equal(t, "Checking Case", Get("FOO")) 530 } 531 532 func TestRecursiveAliases(t *testing.T) { 533 RegisterAlias("Baz", "Roo") 534 RegisterAlias("Roo", "baz") 535 } 536 537 func TestUnmarshal(t *testing.T) { 538 SetDefault("port", 1313) 539 Set("name", "Steve") 540 Set("duration", "1s1ms") 541 Set("modes", []int{1, 2, 3}) 542 543 type config struct { 544 Port int 545 Name string 546 Duration time.Duration 547 Modes []int 548 } 549 550 var C config 551 552 err := Unmarshal(&C) 553 if err != nil { 554 t.Fatalf("unable to decode into struct, %v", err) 555 } 556 557 assert.Equal( 558 t, 559 &config{ 560 Name: "Steve", 561 Port: 1313, 562 Duration: time.Second + time.Millisecond, 563 Modes: []int{1, 2, 3}, 564 }, 565 &C, 566 ) 567 568 Set("port", 1234) 569 err = Unmarshal(&C) 570 if err != nil { 571 t.Fatalf("unable to decode into struct, %v", err) 572 } 573 574 assert.Equal( 575 t, 576 &config{ 577 Name: "Steve", 578 Port: 1234, 579 Duration: time.Second + time.Millisecond, 580 Modes: []int{1, 2, 3}, 581 }, 582 &C, 583 ) 584 } 585 586 func TestUnmarshalWithDecoderOptions(t *testing.T) { 587 Set("credentials", "{\"foo\":\"bar\"}") 588 589 opt := DecodeHook(mapstructure.ComposeDecodeHookFunc( 590 mapstructure.StringToTimeDurationHookFunc(), 591 mapstructure.StringToSliceHookFunc(","), 592 // Custom Decode Hook Function 593 func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) { 594 if rf != reflect.String || rt != reflect.Map { 595 return data, nil 596 } 597 m := map[string]string{} 598 raw := data.(string) 599 if raw == "" { 600 return m, nil 601 } 602 return m, json.Unmarshal([]byte(raw), &m) 603 }, 604 )) 605 606 type config struct { 607 Credentials map[string]string 608 } 609 610 var C config 611 612 err := Unmarshal(&C, opt) 613 if err != nil { 614 t.Fatalf("unable to decode into struct, %v", err) 615 } 616 617 assert.Equal(t, &config{ 618 Credentials: map[string]string{"foo": "bar"}, 619 }, &C) 620 } 621 622 func TestBindPFlags(t *testing.T) { 623 v := New() // create independent Viper object 624 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) 625 626 var testValues = map[string]*string{ 627 "host": nil, 628 "port": nil, 629 "endpoint": nil, 630 } 631 632 var mutatedTestValues = map[string]string{ 633 "host": "localhost", 634 "port": "6060", 635 "endpoint": "/public", 636 } 637 638 for name := range testValues { 639 testValues[name] = flagSet.String(name, "", "test") 640 } 641 642 err := v.BindPFlags(flagSet) 643 if err != nil { 644 t.Fatalf("error binding flag set, %v", err) 645 } 646 647 flagSet.VisitAll(func(flag *pflag.Flag) { 648 flag.Value.Set(mutatedTestValues[flag.Name]) 649 flag.Changed = true 650 }) 651 652 for name, expected := range mutatedTestValues { 653 assert.Equal(t, expected, v.Get(name)) 654 } 655 656 } 657 658 func TestBindPFlagsStringSlice(t *testing.T) { 659 tests := []struct { 660 Expected []string 661 Value string 662 }{ 663 {nil, ""}, 664 {[]string{"jeden"}, "jeden"}, 665 {[]string{"dwa", "trzy"}, "dwa,trzy"}, 666 {[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""}, 667 } 668 669 v := New() // create independent Viper object 670 defaultVal := []string{"default"} 671 v.SetDefault("stringslice", defaultVal) 672 673 for _, testValue := range tests { 674 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) 675 flagSet.StringSlice("stringslice", testValue.Expected, "test") 676 677 for _, changed := range []bool{true, false} { 678 flagSet.VisitAll(func(f *pflag.Flag) { 679 f.Value.Set(testValue.Value) 680 f.Changed = changed 681 }) 682 683 err := v.BindPFlags(flagSet) 684 if err != nil { 685 t.Fatalf("error binding flag set, %v", err) 686 } 687 688 type TestStr struct { 689 StringSlice []string 690 } 691 val := &TestStr{} 692 if err := v.Unmarshal(val); err != nil { 693 t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err) 694 } 695 if changed { 696 assert.Equal(t, testValue.Expected, val.StringSlice) 697 } else { 698 assert.Equal(t, defaultVal, val.StringSlice) 699 } 700 } 701 } 702 } 703 704 func TestBindPFlagsIntSlice(t *testing.T) { 705 tests := []struct { 706 Expected []int 707 Value string 708 }{ 709 {nil, ""}, 710 {[]int{1}, "1"}, 711 {[]int{2, 3}, "2,3"}, 712 } 713 714 v := New() // create independent Viper object 715 defaultVal := []int{0} 716 v.SetDefault("intslice", defaultVal) 717 718 for _, testValue := range tests { 719 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) 720 flagSet.IntSlice("intslice", testValue.Expected, "test") 721 722 for _, changed := range []bool{true, false} { 723 flagSet.VisitAll(func(f *pflag.Flag) { 724 f.Value.Set(testValue.Value) 725 f.Changed = changed 726 }) 727 728 err := v.BindPFlags(flagSet) 729 if err != nil { 730 t.Fatalf("error binding flag set, %v", err) 731 } 732 733 type TestInt struct { 734 IntSlice []int 735 } 736 val := &TestInt{} 737 if err := v.Unmarshal(val); err != nil { 738 t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err) 739 } 740 if changed { 741 assert.Equal(t, testValue.Expected, val.IntSlice) 742 } else { 743 assert.Equal(t, defaultVal, val.IntSlice) 744 } 745 } 746 } 747 } 748 749 func TestBindPFlag(t *testing.T) { 750 var testString = "testing" 751 var testValue = newStringValue(testString, &testString) 752 753 flag := &pflag.Flag{ 754 Name: "testflag", 755 Value: testValue, 756 Changed: false, 757 } 758 759 BindPFlag("testvalue", flag) 760 761 assert.Equal(t, testString, Get("testvalue")) 762 763 flag.Value.Set("testing_mutate") 764 flag.Changed = true //hack for pflag usage 765 766 assert.Equal(t, "testing_mutate", Get("testvalue")) 767 768 } 769 770 func TestBoundCaseSensitivity(t *testing.T) { 771 assert.Equal(t, "brown", Get("eyes")) 772 773 BindEnv("eYEs", "TURTLE_EYES") 774 os.Setenv("TURTLE_EYES", "blue") 775 776 assert.Equal(t, "blue", Get("eyes")) 777 778 var testString = "green" 779 var testValue = newStringValue(testString, &testString) 780 781 flag := &pflag.Flag{ 782 Name: "eyeballs", 783 Value: testValue, 784 Changed: true, 785 } 786 787 BindPFlag("eYEs", flag) 788 assert.Equal(t, "green", Get("eyes")) 789 790 } 791 792 func TestSizeInBytes(t *testing.T) { 793 input := map[string]uint{ 794 "": 0, 795 "b": 0, 796 "12 bytes": 0, 797 "200000000000gb": 0, 798 "12 b": 12, 799 "43 MB": 43 * (1 << 20), 800 "10mb": 10 * (1 << 20), 801 "1gb": 1 << 30, 802 } 803 804 for str, expected := range input { 805 assert.Equal(t, expected, parseSizeInBytes(str), str) 806 } 807 } 808 809 func TestFindsNestedKeys(t *testing.T) { 810 initConfigs() 811 dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 812 813 Set("super", map[string]interface{}{ 814 "deep": map[string]interface{}{ 815 "nested": "value", 816 }, 817 }) 818 819 expected := map[string]interface{}{ 820 "super": map[string]interface{}{ 821 "deep": map[string]interface{}{ 822 "nested": "value", 823 }, 824 }, 825 "super.deep": map[string]interface{}{ 826 "nested": "value", 827 }, 828 "super.deep.nested": "value", 829 "owner.organization": "MongoDB", 830 "batters.batter": []interface{}{ 831 map[string]interface{}{ 832 "type": "Regular", 833 }, 834 map[string]interface{}{ 835 "type": "Chocolate", 836 }, 837 map[string]interface{}{ 838 "type": "Blueberry", 839 }, 840 map[string]interface{}{ 841 "type": "Devil's Food", 842 }, 843 }, 844 "hobbies": []interface{}{ 845 "skateboarding", "snowboarding", "go", 846 }, 847 "TITLE_DOTENV": "DotEnv Example", 848 "TYPE_DOTENV": "donut", 849 "NAME_DOTENV": "Cake", 850 "title": "TOML Example", 851 "newkey": "remote", 852 "batters": map[string]interface{}{ 853 "batter": []interface{}{ 854 map[string]interface{}{ 855 "type": "Regular", 856 }, 857 map[string]interface{}{ 858 "type": "Chocolate", 859 }, map[string]interface{}{ 860 "type": "Blueberry", 861 }, map[string]interface{}{ 862 "type": "Devil's Food", 863 }, 864 }, 865 }, 866 "eyes": "brown", 867 "age": 35, 868 "owner": map[string]interface{}{ 869 "organization": "MongoDB", 870 "bio": "MongoDB Chief Developer Advocate & Hacker at Large", 871 "dob": dob, 872 }, 873 "owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large", 874 "type": "donut", 875 "id": "0001", 876 "name": "Cake", 877 "hacker": true, 878 "ppu": 0.55, 879 "clothing": map[string]interface{}{ 880 "jacket": "leather", 881 "trousers": "denim", 882 "pants": map[string]interface{}{ 883 "size": "large", 884 }, 885 }, 886 "clothing.jacket": "leather", 887 "clothing.pants.size": "large", 888 "clothing.trousers": "denim", 889 "owner.dob": dob, 890 "beard": true, 891 "foos": []map[string]interface{}{ 892 map[string]interface{}{ 893 "foo": []map[string]interface{}{ 894 map[string]interface{}{ 895 "key": 1, 896 }, 897 map[string]interface{}{ 898 "key": 2, 899 }, 900 map[string]interface{}{ 901 "key": 3, 902 }, 903 map[string]interface{}{ 904 "key": 4, 905 }, 906 }, 907 }, 908 }, 909 } 910 911 for key, expectedValue := range expected { 912 913 assert.Equal(t, expectedValue, v.Get(key)) 914 } 915 916 } 917 918 func TestReadBufConfig(t *testing.T) { 919 v := New() 920 v.SetConfigType("yaml") 921 v.ReadConfig(bytes.NewBuffer(yamlExample)) 922 t.Log(v.AllKeys()) 923 924 assert.True(t, v.InConfig("name")) 925 assert.False(t, v.InConfig("state")) 926 assert.Equal(t, "steve", v.Get("name")) 927 assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies")) 928 assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing")) 929 assert.Equal(t, 35, v.Get("age")) 930 } 931 932 func TestIsSet(t *testing.T) { 933 v := New() 934 v.SetConfigType("yaml") 935 v.ReadConfig(bytes.NewBuffer(yamlExample)) 936 assert.True(t, v.IsSet("clothing.jacket")) 937 assert.False(t, v.IsSet("clothing.jackets")) 938 assert.False(t, v.IsSet("helloworld")) 939 v.Set("helloworld", "fubar") 940 assert.True(t, v.IsSet("helloworld")) 941 } 942 943 func TestDirsSearch(t *testing.T) { 944 945 root, config, cleanup := initDirs(t) 946 defer cleanup() 947 948 v := New() 949 v.SetConfigName(config) 950 v.SetDefault(`key`, `default`) 951 952 entries, err := ioutil.ReadDir(root) 953 assert.Nil(t, err) 954 for _, e := range entries { 955 if e.IsDir() { 956 v.AddConfigPath(e.Name()) 957 } 958 } 959 960 err = v.ReadInConfig() 961 assert.Nil(t, err) 962 963 assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`)) 964 } 965 966 func TestWrongDirsSearchNotFound(t *testing.T) { 967 968 _, config, cleanup := initDirs(t) 969 defer cleanup() 970 971 v := New() 972 v.SetConfigName(config) 973 v.SetDefault(`key`, `default`) 974 975 v.AddConfigPath(`whattayoutalkingbout`) 976 v.AddConfigPath(`thispathaintthere`) 977 978 err := v.ReadInConfig() 979 assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) 980 981 // Even though config did not load and the error might have 982 // been ignored by the client, the default still loads 983 assert.Equal(t, `default`, v.GetString(`key`)) 984 } 985 986 func TestWrongDirsSearchNotFoundForMerge(t *testing.T) { 987 988 _, config, cleanup := initDirs(t) 989 defer cleanup() 990 991 v := New() 992 v.SetConfigName(config) 993 v.SetDefault(`key`, `default`) 994 995 v.AddConfigPath(`whattayoutalkingbout`) 996 v.AddConfigPath(`thispathaintthere`) 997 998 err := v.MergeInConfig() 999 assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) 1000 1001 // Even though config did not load and the error might have 1002 // been ignored by the client, the default still loads 1003 assert.Equal(t, `default`, v.GetString(`key`)) 1004 } 1005 1006 func TestSub(t *testing.T) { 1007 v := New() 1008 v.SetConfigType("yaml") 1009 v.ReadConfig(bytes.NewBuffer(yamlExample)) 1010 1011 subv := v.Sub("clothing") 1012 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size")) 1013 1014 subv = v.Sub("clothing.pants") 1015 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size")) 1016 1017 subv = v.Sub("clothing.pants.size") 1018 assert.Equal(t, (*Viper)(nil), subv) 1019 1020 subv = v.Sub("missing.key") 1021 assert.Equal(t, (*Viper)(nil), subv) 1022 } 1023 1024 var hclWriteExpected = []byte(`"foos" = { 1025 "foo" = { 1026 "key" = 1 1027 } 1028 1029 "foo" = { 1030 "key" = 2 1031 } 1032 1033 "foo" = { 1034 "key" = 3 1035 } 1036 1037 "foo" = { 1038 "key" = 4 1039 } 1040 } 1041 1042 "id" = "0001" 1043 1044 "name" = "Cake" 1045 1046 "ppu" = 0.55 1047 1048 "type" = "donut"`) 1049 1050 func TestWriteConfigHCL(t *testing.T) { 1051 v := New() 1052 fs := afero.NewMemMapFs() 1053 v.SetFs(fs) 1054 v.SetConfigName("c") 1055 v.SetConfigType("hcl") 1056 err := v.ReadConfig(bytes.NewBuffer(hclExample)) 1057 if err != nil { 1058 t.Fatal(err) 1059 } 1060 if err := v.WriteConfigAs("c.hcl"); err != nil { 1061 t.Fatal(err) 1062 } 1063 read, err := afero.ReadFile(fs, "c.hcl") 1064 if err != nil { 1065 t.Fatal(err) 1066 } 1067 assert.Equal(t, hclWriteExpected, read) 1068 } 1069 1070 var jsonWriteExpected = []byte(`{ 1071 "batters": { 1072 "batter": [ 1073 { 1074 "type": "Regular" 1075 }, 1076 { 1077 "type": "Chocolate" 1078 }, 1079 { 1080 "type": "Blueberry" 1081 }, 1082 { 1083 "type": "Devil's Food" 1084 } 1085 ] 1086 }, 1087 "id": "0001", 1088 "name": "Cake", 1089 "ppu": 0.55, 1090 "type": "donut" 1091 }`) 1092 1093 func TestWriteConfigJson(t *testing.T) { 1094 v := New() 1095 fs := afero.NewMemMapFs() 1096 v.SetFs(fs) 1097 v.SetConfigName("c") 1098 v.SetConfigType("json") 1099 err := v.ReadConfig(bytes.NewBuffer(jsonExample)) 1100 if err != nil { 1101 t.Fatal(err) 1102 } 1103 if err := v.WriteConfigAs("c.json"); err != nil { 1104 t.Fatal(err) 1105 } 1106 read, err := afero.ReadFile(fs, "c.json") 1107 if err != nil { 1108 t.Fatal(err) 1109 } 1110 assert.Equal(t, jsonWriteExpected, read) 1111 } 1112 1113 var propertiesWriteExpected = []byte(`p_id = 0001 1114 p_type = donut 1115 p_name = Cake 1116 p_ppu = 0.55 1117 p_batters.batter.type = Regular 1118 `) 1119 1120 func TestWriteConfigProperties(t *testing.T) { 1121 v := New() 1122 fs := afero.NewMemMapFs() 1123 v.SetFs(fs) 1124 v.SetConfigName("c") 1125 v.SetConfigType("properties") 1126 err := v.ReadConfig(bytes.NewBuffer(propertiesExample)) 1127 if err != nil { 1128 t.Fatal(err) 1129 } 1130 if err := v.WriteConfigAs("c.properties"); err != nil { 1131 t.Fatal(err) 1132 } 1133 read, err := afero.ReadFile(fs, "c.properties") 1134 if err != nil { 1135 t.Fatal(err) 1136 } 1137 assert.Equal(t, propertiesWriteExpected, read) 1138 } 1139 1140 func TestWriteConfigTOML(t *testing.T) { 1141 fs := afero.NewMemMapFs() 1142 v := New() 1143 v.SetFs(fs) 1144 v.SetConfigName("c") 1145 v.SetConfigType("toml") 1146 err := v.ReadConfig(bytes.NewBuffer(tomlExample)) 1147 if err != nil { 1148 t.Fatal(err) 1149 } 1150 if err := v.WriteConfigAs("c.toml"); err != nil { 1151 t.Fatal(err) 1152 } 1153 1154 // The TOML String method does not order the contents. 1155 // Therefore, we must read the generated file and compare the data. 1156 v2 := New() 1157 v2.SetFs(fs) 1158 v2.SetConfigName("c") 1159 v2.SetConfigType("toml") 1160 v2.SetConfigFile("c.toml") 1161 err = v2.ReadInConfig() 1162 if err != nil { 1163 t.Fatal(err) 1164 } 1165 1166 assert.Equal(t, v.GetString("title"), v2.GetString("title")) 1167 assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio")) 1168 assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob")) 1169 assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization")) 1170 } 1171 1172 var dotenvWriteExpected = []byte(` 1173 TITLE="DotEnv Write Example" 1174 NAME=Oreo 1175 KIND=Biscuit 1176 `) 1177 1178 func TestWriteConfigDotEnv(t *testing.T) { 1179 fs := afero.NewMemMapFs() 1180 v := New() 1181 v.SetFs(fs) 1182 v.SetConfigName("c") 1183 v.SetConfigType("env") 1184 err := v.ReadConfig(bytes.NewBuffer(dotenvWriteExpected)) 1185 if err != nil { 1186 t.Fatal(err) 1187 } 1188 if err := v.WriteConfigAs("c.env"); err != nil { 1189 t.Fatal(err) 1190 } 1191 1192 // The TOML String method does not order the contents. 1193 // Therefore, we must read the generated file and compare the data. 1194 v2 := New() 1195 v2.SetFs(fs) 1196 v2.SetConfigName("c") 1197 v2.SetConfigType("env") 1198 v2.SetConfigFile("c.env") 1199 err = v2.ReadInConfig() 1200 if err != nil { 1201 t.Fatal(err) 1202 } 1203 1204 assert.Equal(t, v.GetString("title"), v2.GetString("title")) 1205 assert.Equal(t, v.GetString("type"), v2.GetString("type")) 1206 assert.Equal(t, v.GetString("kind"), v2.GetString("kind")) 1207 } 1208 1209 var yamlWriteExpected = []byte(`age: 35 1210 beard: true 1211 clothing: 1212 jacket: leather 1213 pants: 1214 size: large 1215 trousers: denim 1216 eyes: brown 1217 hacker: true 1218 hobbies: 1219 - skateboarding 1220 - snowboarding 1221 - go 1222 name: steve 1223 `) 1224 1225 func TestWriteConfigYAML(t *testing.T) { 1226 v := New() 1227 fs := afero.NewMemMapFs() 1228 v.SetFs(fs) 1229 v.SetConfigName("c") 1230 v.SetConfigType("yaml") 1231 err := v.ReadConfig(bytes.NewBuffer(yamlExample)) 1232 if err != nil { 1233 t.Fatal(err) 1234 } 1235 if err := v.WriteConfigAs("c.yaml"); err != nil { 1236 t.Fatal(err) 1237 } 1238 read, err := afero.ReadFile(fs, "c.yaml") 1239 if err != nil { 1240 t.Fatal(err) 1241 } 1242 assert.Equal(t, yamlWriteExpected, read) 1243 } 1244 1245 var yamlMergeExampleTgt = []byte(` 1246 hello: 1247 pop: 37890 1248 largenum: 765432101234567 1249 num2pow63: 9223372036854775808 1250 world: 1251 - us 1252 - uk 1253 - fr 1254 - de 1255 `) 1256 1257 var yamlMergeExampleSrc = []byte(` 1258 hello: 1259 pop: 45000 1260 largenum: 7654321001234567 1261 universe: 1262 - mw 1263 - ad 1264 ints: 1265 - 1 1266 - 2 1267 fu: bar 1268 `) 1269 1270 func TestMergeConfig(t *testing.T) { 1271 v := New() 1272 v.SetConfigType("yml") 1273 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1274 t.Fatal(err) 1275 } 1276 1277 if pop := v.GetInt("hello.pop"); pop != 37890 { 1278 t.Fatalf("pop != 37890, = %d", pop) 1279 } 1280 1281 if pop := v.GetInt32("hello.pop"); pop != int32(37890) { 1282 t.Fatalf("pop != 37890, = %d", pop) 1283 } 1284 1285 if pop := v.GetInt64("hello.largenum"); pop != int64(765432101234567) { 1286 t.Fatalf("int64 largenum != 765432101234567, = %d", pop) 1287 } 1288 1289 if pop := v.GetUint("hello.pop"); pop != 37890 { 1290 t.Fatalf("uint pop != 37890, = %d", pop) 1291 } 1292 1293 if pop := v.GetUint32("hello.pop"); pop != 37890 { 1294 t.Fatalf("uint32 pop != 37890, = %d", pop) 1295 } 1296 1297 if pop := v.GetUint64("hello.num2pow63"); pop != 9223372036854775808 { 1298 t.Fatalf("uint64 num2pow63 != 9223372036854775808, = %d", pop) 1299 } 1300 1301 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1302 t.Fatalf("len(world) != 4, = %d", len(world)) 1303 } 1304 1305 if fu := v.GetString("fu"); fu != "" { 1306 t.Fatalf("fu != \"\", = %s", fu) 1307 } 1308 1309 if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 1310 t.Fatal(err) 1311 } 1312 1313 if pop := v.GetInt("hello.pop"); pop != 45000 { 1314 t.Fatalf("pop != 45000, = %d", pop) 1315 } 1316 1317 if pop := v.GetInt32("hello.pop"); pop != int32(45000) { 1318 t.Fatalf("pop != 45000, = %d", pop) 1319 } 1320 1321 if pop := v.GetInt64("hello.largenum"); pop != int64(7654321001234567) { 1322 t.Fatalf("int64 largenum != 7654321001234567, = %d", pop) 1323 } 1324 1325 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1326 t.Fatalf("len(world) != 4, = %d", len(world)) 1327 } 1328 1329 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 1330 t.Fatalf("len(universe) != 2, = %d", len(universe)) 1331 } 1332 1333 if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 { 1334 t.Fatalf("len(ints) != 2, = %d", len(ints)) 1335 } 1336 1337 if fu := v.GetString("fu"); fu != "bar" { 1338 t.Fatalf("fu != \"bar\", = %s", fu) 1339 } 1340 } 1341 1342 func TestMergeConfigNoMerge(t *testing.T) { 1343 v := New() 1344 v.SetConfigType("yml") 1345 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1346 t.Fatal(err) 1347 } 1348 1349 if pop := v.GetInt("hello.pop"); pop != 37890 { 1350 t.Fatalf("pop != 37890, = %d", pop) 1351 } 1352 1353 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1354 t.Fatalf("len(world) != 4, = %d", len(world)) 1355 } 1356 1357 if fu := v.GetString("fu"); fu != "" { 1358 t.Fatalf("fu != \"\", = %s", fu) 1359 } 1360 1361 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 1362 t.Fatal(err) 1363 } 1364 1365 if pop := v.GetInt("hello.pop"); pop != 45000 { 1366 t.Fatalf("pop != 45000, = %d", pop) 1367 } 1368 1369 if world := v.GetStringSlice("hello.world"); len(world) != 0 { 1370 t.Fatalf("len(world) != 0, = %d", len(world)) 1371 } 1372 1373 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 1374 t.Fatalf("len(universe) != 2, = %d", len(universe)) 1375 } 1376 1377 if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 { 1378 t.Fatalf("len(ints) != 2, = %d", len(ints)) 1379 } 1380 1381 if fu := v.GetString("fu"); fu != "bar" { 1382 t.Fatalf("fu != \"bar\", = %s", fu) 1383 } 1384 } 1385 1386 func TestMergeConfigMap(t *testing.T) { 1387 v := New() 1388 v.SetConfigType("yml") 1389 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1390 t.Fatal(err) 1391 } 1392 1393 assert := func(i int) { 1394 large := v.GetInt64("hello.largenum") 1395 pop := v.GetInt("hello.pop") 1396 if large != 765432101234567 { 1397 t.Fatal("Got large num:", large) 1398 } 1399 1400 if pop != i { 1401 t.Fatal("Got pop:", pop) 1402 } 1403 } 1404 1405 assert(37890) 1406 1407 update := map[string]interface{}{ 1408 "Hello": map[string]interface{}{ 1409 "Pop": 1234, 1410 }, 1411 "World": map[interface{}]interface{}{ 1412 "Rock": 345, 1413 }, 1414 } 1415 1416 if err := v.MergeConfigMap(update); err != nil { 1417 t.Fatal(err) 1418 } 1419 1420 if rock := v.GetInt("world.rock"); rock != 345 { 1421 t.Fatal("Got rock:", rock) 1422 } 1423 1424 assert(1234) 1425 1426 } 1427 1428 func TestUnmarshalingWithAliases(t *testing.T) { 1429 v := New() 1430 v.SetDefault("ID", 1) 1431 v.Set("name", "Steve") 1432 v.Set("lastname", "Owen") 1433 1434 v.RegisterAlias("UserID", "ID") 1435 v.RegisterAlias("Firstname", "name") 1436 v.RegisterAlias("Surname", "lastname") 1437 1438 type config struct { 1439 ID int 1440 FirstName string 1441 Surname string 1442 } 1443 1444 var C config 1445 err := v.Unmarshal(&C) 1446 if err != nil { 1447 t.Fatalf("unable to decode into struct, %v", err) 1448 } 1449 1450 assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C) 1451 } 1452 1453 func TestSetConfigNameClearsFileCache(t *testing.T) { 1454 SetConfigFile("/tmp/config.yaml") 1455 SetConfigName("default") 1456 f, err := v.getConfigFile() 1457 if err == nil { 1458 t.Fatalf("config file cache should have been cleared") 1459 } 1460 assert.Empty(t, f) 1461 } 1462 1463 func TestShadowedNestedValue(t *testing.T) { 1464 1465 config := `name: steve 1466 clothing: 1467 jacket: leather 1468 trousers: denim 1469 pants: 1470 size: large 1471 ` 1472 initConfig("yaml", config) 1473 1474 assert.Equal(t, "steve", GetString("name")) 1475 1476 polyester := "polyester" 1477 SetDefault("clothing.shirt", polyester) 1478 SetDefault("clothing.jacket.price", 100) 1479 1480 assert.Equal(t, "leather", GetString("clothing.jacket")) 1481 assert.Nil(t, Get("clothing.jacket.price")) 1482 assert.Equal(t, polyester, GetString("clothing.shirt")) 1483 1484 clothingSettings := AllSettings()["clothing"].(map[string]interface{}) 1485 assert.Equal(t, "leather", clothingSettings["jacket"]) 1486 assert.Equal(t, polyester, clothingSettings["shirt"]) 1487 } 1488 1489 func TestDotParameter(t *testing.T) { 1490 initJSON() 1491 // shoud take precedence over batters defined in jsonExample 1492 r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`)) 1493 unmarshalReader(r, v.config) 1494 1495 actual := Get("batters.batter") 1496 expected := []interface{}{map[string]interface{}{"type": "Small"}} 1497 assert.Equal(t, expected, actual) 1498 } 1499 1500 func TestCaseInsensitive(t *testing.T) { 1501 for _, config := range []struct { 1502 typ string 1503 content string 1504 }{ 1505 {"yaml", ` 1506 aBcD: 1 1507 eF: 1508 gH: 2 1509 iJk: 3 1510 Lm: 1511 nO: 4 1512 P: 1513 Q: 5 1514 R: 6 1515 `}, 1516 {"json", `{ 1517 "aBcD": 1, 1518 "eF": { 1519 "iJk": 3, 1520 "Lm": { 1521 "P": { 1522 "Q": 5, 1523 "R": 6 1524 }, 1525 "nO": 4 1526 }, 1527 "gH": 2 1528 } 1529 }`}, 1530 {"toml", `aBcD = 1 1531 [eF] 1532 gH = 2 1533 iJk = 3 1534 [eF.Lm] 1535 nO = 4 1536 [eF.Lm.P] 1537 Q = 5 1538 R = 6 1539 `}, 1540 } { 1541 doTestCaseInsensitive(t, config.typ, config.content) 1542 } 1543 } 1544 1545 func TestCaseInsensitiveSet(t *testing.T) { 1546 Reset() 1547 m1 := map[string]interface{}{ 1548 "Foo": 32, 1549 "Bar": map[interface{}]interface { 1550 }{ 1551 "ABc": "A", 1552 "cDE": "B"}, 1553 } 1554 1555 m2 := map[string]interface{}{ 1556 "Foo": 52, 1557 "Bar": map[interface{}]interface { 1558 }{ 1559 "bCd": "A", 1560 "eFG": "B"}, 1561 } 1562 1563 Set("Given1", m1) 1564 Set("Number1", 42) 1565 1566 SetDefault("Given2", m2) 1567 SetDefault("Number2", 52) 1568 1569 // Verify SetDefault 1570 if v := Get("number2"); v != 52 { 1571 t.Fatalf("Expected 52 got %q", v) 1572 } 1573 1574 if v := Get("given2.foo"); v != 52 { 1575 t.Fatalf("Expected 52 got %q", v) 1576 } 1577 1578 if v := Get("given2.bar.bcd"); v != "A" { 1579 t.Fatalf("Expected A got %q", v) 1580 } 1581 1582 if _, ok := m2["Foo"]; !ok { 1583 t.Fatal("Input map changed") 1584 } 1585 1586 // Verify Set 1587 if v := Get("number1"); v != 42 { 1588 t.Fatalf("Expected 42 got %q", v) 1589 } 1590 1591 if v := Get("given1.foo"); v != 32 { 1592 t.Fatalf("Expected 32 got %q", v) 1593 } 1594 1595 if v := Get("given1.bar.abc"); v != "A" { 1596 t.Fatalf("Expected A got %q", v) 1597 } 1598 1599 if _, ok := m1["Foo"]; !ok { 1600 t.Fatal("Input map changed") 1601 } 1602 } 1603 1604 func TestParseNested(t *testing.T) { 1605 type duration struct { 1606 Delay time.Duration 1607 } 1608 1609 type item struct { 1610 Name string 1611 Delay time.Duration 1612 Nested duration 1613 } 1614 1615 config := `[[parent]] 1616 delay="100ms" 1617 [parent.nested] 1618 delay="200ms" 1619 ` 1620 initConfig("toml", config) 1621 1622 var items []item 1623 err := v.UnmarshalKey("parent", &items) 1624 if err != nil { 1625 t.Fatalf("unable to decode into struct, %v", err) 1626 } 1627 1628 assert.Equal(t, 1, len(items)) 1629 assert.Equal(t, 100*time.Millisecond, items[0].Delay) 1630 assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay) 1631 } 1632 1633 func doTestCaseInsensitive(t *testing.T, typ, config string) { 1634 initConfig(typ, config) 1635 Set("RfD", true) 1636 assert.Equal(t, true, Get("rfd")) 1637 assert.Equal(t, true, Get("rFD")) 1638 assert.Equal(t, 1, cast.ToInt(Get("abcd"))) 1639 assert.Equal(t, 1, cast.ToInt(Get("Abcd"))) 1640 assert.Equal(t, 2, cast.ToInt(Get("ef.gh"))) 1641 assert.Equal(t, 3, cast.ToInt(Get("ef.ijk"))) 1642 assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no"))) 1643 assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q"))) 1644 1645 } 1646 1647 func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) { 1648 watchDir, err := ioutil.TempDir("", "") 1649 require.Nil(t, err) 1650 configFile := path.Join(watchDir, "config.yaml") 1651 err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640) 1652 require.Nil(t, err) 1653 cleanup := func() { 1654 os.RemoveAll(watchDir) 1655 } 1656 v := New() 1657 v.SetConfigFile(configFile) 1658 err = v.ReadInConfig() 1659 require.Nil(t, err) 1660 require.Equal(t, "bar", v.Get("foo")) 1661 return v, configFile, cleanup 1662 } 1663 1664 func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) { 1665 watchDir, err := ioutil.TempDir("", "") 1666 require.Nil(t, err) 1667 dataDir1 := path.Join(watchDir, "data1") 1668 err = os.Mkdir(dataDir1, 0777) 1669 require.Nil(t, err) 1670 realConfigFile := path.Join(dataDir1, "config.yaml") 1671 t.Logf("Real config file location: %s\n", realConfigFile) 1672 err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640) 1673 require.Nil(t, err) 1674 cleanup := func() { 1675 os.RemoveAll(watchDir) 1676 } 1677 // now, symlink the tm `data1` dir to `data` in the baseDir 1678 os.Symlink(dataDir1, path.Join(watchDir, "data")) 1679 // and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml` 1680 configFile := path.Join(watchDir, "config.yaml") 1681 os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile) 1682 t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml")) 1683 // init Viper 1684 v := New() 1685 v.SetConfigFile(configFile) 1686 err = v.ReadInConfig() 1687 require.Nil(t, err) 1688 require.Equal(t, "bar", v.Get("foo")) 1689 return v, watchDir, configFile, cleanup 1690 } 1691 1692 func TestWatchFile(t *testing.T) { 1693 if runtime.GOOS == "linux" { 1694 // TODO(bep) FIX ME 1695 t.Skip("Skip test on Linux ...") 1696 } 1697 1698 t.Run("file content changed", func(t *testing.T) { 1699 // given a `config.yaml` file being watched 1700 v, configFile, cleanup := newViperWithConfigFile(t) 1701 defer cleanup() 1702 _, err := os.Stat(configFile) 1703 require.NoError(t, err) 1704 t.Logf("test config file: %s\n", configFile) 1705 wg := sync.WaitGroup{} 1706 wg.Add(1) 1707 v.OnConfigChange(func(in fsnotify.Event) { 1708 t.Logf("config file changed") 1709 wg.Done() 1710 }) 1711 v.WatchConfig() 1712 // when overwriting the file and waiting for the custom change notification handler to be triggered 1713 err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640) 1714 wg.Wait() 1715 // then the config value should have changed 1716 require.Nil(t, err) 1717 assert.Equal(t, "baz", v.Get("foo")) 1718 }) 1719 1720 t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) { 1721 // skip if not executed on Linux 1722 if runtime.GOOS != "linux" { 1723 t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...") 1724 } 1725 v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t) 1726 // defer cleanup() 1727 wg := sync.WaitGroup{} 1728 v.WatchConfig() 1729 v.OnConfigChange(func(in fsnotify.Event) { 1730 t.Logf("config file changed") 1731 wg.Done() 1732 }) 1733 wg.Add(1) 1734 // when link to another `config.yaml` file 1735 dataDir2 := path.Join(watchDir, "data2") 1736 err := os.Mkdir(dataDir2, 0777) 1737 require.Nil(t, err) 1738 configFile2 := path.Join(dataDir2, "config.yaml") 1739 err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640) 1740 require.Nil(t, err) 1741 // change the symlink using the `ln -sfn` command 1742 err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run() 1743 require.Nil(t, err) 1744 wg.Wait() 1745 // then 1746 require.Nil(t, err) 1747 assert.Equal(t, "baz", v.Get("foo")) 1748 }) 1749 1750 } 1751 1752 // make .env for testing 1753 func initDotEnvFile(t *testing.T) (string, func()) { 1754 1755 root, err := ioutil.TempDir("", "") 1756 1757 cleanup := true 1758 defer func() { 1759 if cleanup { 1760 os.Chdir("..") 1761 os.RemoveAll(root) 1762 } 1763 }() 1764 1765 assert.Nil(t, err) 1766 1767 err = os.Chdir(root) 1768 assert.Nil(t, err) 1769 1770 configfile := ".env" 1771 1772 err = ioutil.WriteFile( 1773 path.Join(root, configfile), 1774 dotenvWriteExpected, 1775 0640, 1776 ) 1777 assert.Nil(t, err) 1778 1779 cleanup = false 1780 return root, func() { 1781 os.Chdir("..") 1782 os.RemoveAll(root) 1783 } 1784 } 1785 1786 func TestFindDotEnvFile(t *testing.T) { 1787 v := New() 1788 v.SetConfigType("env") 1789 err := v.ReadConfig(bytes.NewBuffer(dotenvWriteExpected)) 1790 if err != nil { 1791 t.Fatal(err) 1792 } 1793 1794 root, cleanup := initDotEnvFile(t) 1795 defer cleanup() 1796 1797 v2 := New() 1798 v2.AddConfigPath(root) 1799 v2.SetConfigName("") 1800 v2.SetConfigType("env") 1801 err = v2.ReadInConfig() 1802 if err != nil { 1803 t.Fatal(err) 1804 } 1805 1806 assert.Equal(t, v.GetString("title"), v2.GetString("title")) 1807 assert.Equal(t, v.GetString("type"), v2.GetString("type")) 1808 assert.Equal(t, v.GetString("kind"), v2.GetString("kind")) 1809 } 1810 1811 func BenchmarkGetBool(b *testing.B) { 1812 key := "BenchmarkGetBool" 1813 v = New() 1814 v.Set(key, true) 1815 1816 for i := 0; i < b.N; i++ { 1817 if !v.GetBool(key) { 1818 b.Fatal("GetBool returned false") 1819 } 1820 } 1821 } 1822 1823 func BenchmarkGet(b *testing.B) { 1824 key := "BenchmarkGet" 1825 v = New() 1826 v.Set(key, true) 1827 1828 for i := 0; i < b.N; i++ { 1829 if !v.Get(key).(bool) { 1830 b.Fatal("Get returned false") 1831 } 1832 } 1833 } 1834 1835 // BenchmarkGetBoolFromMap is the "perfect result" for the above. 1836 func BenchmarkGetBoolFromMap(b *testing.B) { 1837 m := make(map[string]bool) 1838 key := "BenchmarkGetBool" 1839 m[key] = true 1840 1841 for i := 0; i < b.N; i++ { 1842 if !m[key] { 1843 b.Fatal("Map value was false") 1844 } 1845 } 1846 }