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