github.com/kmhagan/viper@v1.6.3-0.20200410222425-d68b332001c2/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 "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 35 var yamlExample = []byte(`Hacker: true 36 name: steve 37 hobbies: 38 - skateboarding 39 - snowboarding 40 - go 41 clothing: 42 jacket: leather 43 trousers: denim 44 pants: 45 size: large 46 age: 35 47 eyes : brown 48 beard: true 49 `) 50 51 var yamlExampleWithExtras = []byte(`Existing: true 52 Bogus: true 53 `) 54 55 type testUnmarshalExtra struct { 56 Existing bool 57 } 58 59 var tomlExample = []byte(` 60 title = "TOML Example" 61 62 [owner] 63 organization = "MongoDB" 64 Bio = "MongoDB Chief Developer Advocate & Hacker at Large" 65 dob = 1979-05-27T07:32:00Z # First class dates? Why not?`) 66 67 var dotenvExample = []byte(` 68 TITLE_DOTENV="DotEnv Example" 69 TYPE_DOTENV=donut 70 NAME_DOTENV=Cake`) 71 72 var 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 87 var hclExample = []byte(` 88 id = "0001" 89 type = "donut" 90 name = "Cake" 91 ppu = 0.55 92 foos { 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 107 var propertiesExample = []byte(` 108 p_id: 0001 109 p_type: donut 110 p_name: Cake 111 p_ppu: 0.55 112 p_batters.batter.type: Regular 113 `) 114 115 var remoteExample = []byte(`{ 116 "id":"0002", 117 "type":"cronut", 118 "newkey":"remote" 119 }`) 120 121 var iniExample = []byte(`; Package name 122 NAME = ini 123 ; Package version 124 VERSION = v1 125 ; Package import path 126 IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s 127 128 # Information about package author 129 # Bio can be written in multiple lines. 130 [author] 131 NAME = Unknown ; Succeeding comment 132 E-MAIL = fake@localhost 133 GITHUB = https://github.com/%(NAME)s 134 BIO = """Gopher. 135 Coding addict. 136 Good man. 137 """ # Succeeding comment`) 138 139 func 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 175 func 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 185 func initYAML() { 186 initConfig("yaml", string(yamlExample)) 187 } 188 189 func initJSON() { 190 Reset() 191 SetConfigType("json") 192 r := bytes.NewReader(jsonExample) 193 194 unmarshalReader(r, v.config) 195 } 196 197 func initProperties() { 198 Reset() 199 SetConfigType("properties") 200 r := bytes.NewReader(propertiesExample) 201 202 unmarshalReader(r, v.config) 203 } 204 205 func initTOML() { 206 Reset() 207 SetConfigType("toml") 208 r := bytes.NewReader(tomlExample) 209 210 unmarshalReader(r, v.config) 211 } 212 213 func initDotEnv() { 214 Reset() 215 SetConfigType("env") 216 r := bytes.NewReader(dotenvExample) 217 218 unmarshalReader(r, v.config) 219 } 220 221 func initHcl() { 222 Reset() 223 SetConfigType("hcl") 224 r := bytes.NewReader(hclExample) 225 226 unmarshalReader(r, v.config) 227 } 228 229 func initIni() { 230 Reset() 231 SetConfigType("ini") 232 r := bytes.NewReader(iniExample) 233 234 unmarshalReader(r, v.config) 235 } 236 237 // make directories for testing 238 func 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 283 type stringValue string 284 285 func newStringValue(val string, p *string) *stringValue { 286 *p = val 287 return (*stringValue)(p) 288 } 289 290 func (s *stringValue) Set(val string) error { 291 *s = stringValue(val) 292 return nil 293 } 294 295 func (s *stringValue) Type() string { 296 return "string" 297 } 298 299 func (s *stringValue) String() string { 300 return string(*s) 301 } 302 303 func 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 310 func 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 327 func 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 344 func 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 364 func 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 378 func 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 391 func 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 403 func TestOverrides(t *testing.T) { 404 Set("age", 40) 405 assert.Equal(t, 40, Get("age")) 406 } 407 408 func TestDefaultPost(t *testing.T) { 409 assert.NotEqual(t, "NYC", Get("state")) 410 SetDefault("state", "NYC") 411 assert.Equal(t, "NYC", Get("state")) 412 } 413 414 func 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 421 func 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 430 func TestYML(t *testing.T) { 431 initYAML() 432 assert.Equal(t, "steve", Get("name")) 433 } 434 435 func TestJSON(t *testing.T) { 436 initJSON() 437 assert.Equal(t, "0001", Get("id")) 438 } 439 440 func TestProperties(t *testing.T) { 441 initProperties() 442 assert.Equal(t, "0001", Get("p_id")) 443 } 444 445 func TestTOML(t *testing.T) { 446 initTOML() 447 assert.Equal(t, "TOML Example", Get("title")) 448 } 449 450 func TestDotEnv(t *testing.T) { 451 initDotEnv() 452 assert.Equal(t, "DotEnv Example", Get("title_dotenv")) 453 } 454 455 func 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 466 func TestIni(t *testing.T) { 467 initIni() 468 assert.Equal(t, "ini", Get("default.name")) 469 } 470 471 func 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 486 func 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 505 func 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 522 func 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 541 func 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 561 func 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 569 func 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 578 func 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 590 func 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 599 func 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 713 func 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 730 func 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 737 func TestRecursiveAliases(t *testing.T) { 738 RegisterAlias("Baz", "Roo") 739 RegisterAlias("Roo", "baz") 740 } 741 742 func 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 791 func 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 827 func 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 862 func 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 908 func 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 953 func 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 973 func 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 994 func 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 1011 func 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 1118 func 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 1132 func 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 1178 func 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 1200 func 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 1219 func 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 1238 func 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 1256 var 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 1282 var jsonWriteExpected = []byte(`{ 1283 "batters": { 1284 "batter": [ 1285 { 1286 "type": "Regular" 1287 }, 1288 { 1289 "type": "Chocolate" 1290 }, 1291 { 1292 "type": "Blueberry" 1293 }, 1294 { 1295 "type": "Devil's Food" 1296 } 1297 ] 1298 }, 1299 "id": "0001", 1300 "name": "Cake", 1301 "ppu": 0.55, 1302 "type": "donut" 1303 }`) 1304 1305 var propertiesWriteExpected = []byte(`p_id = 0001 1306 p_type = donut 1307 p_name = Cake 1308 p_ppu = 0.55 1309 p_batters.batter.type = Regular 1310 `) 1311 1312 var yamlWriteExpected = []byte(`age: 35 1313 beard: true 1314 clothing: 1315 jacket: leather 1316 pants: 1317 size: large 1318 trousers: denim 1319 eyes: brown 1320 hacker: true 1321 hobbies: 1322 - skateboarding 1323 - snowboarding 1324 - go 1325 name: steve 1326 `) 1327 1328 func TestWriteConfig(t *testing.T) { 1329 fs := afero.NewMemMapFs() 1330 testCases := map[string]struct { 1331 configName string 1332 inConfigType string 1333 outConfigType string 1334 fileName string 1335 input []byte 1336 expectedContent []byte 1337 }{ 1338 "hcl with file extension": { 1339 configName: "c", 1340 inConfigType: "hcl", 1341 outConfigType: "hcl", 1342 fileName: "c.hcl", 1343 input: hclExample, 1344 expectedContent: hclWriteExpected, 1345 }, 1346 "hcl without file extension": { 1347 configName: "c", 1348 inConfigType: "hcl", 1349 outConfigType: "hcl", 1350 fileName: "c", 1351 input: hclExample, 1352 expectedContent: hclWriteExpected, 1353 }, 1354 "hcl with file extension and mismatch type": { 1355 configName: "c", 1356 inConfigType: "hcl", 1357 outConfigType: "json", 1358 fileName: "c.hcl", 1359 input: hclExample, 1360 expectedContent: hclWriteExpected, 1361 }, 1362 "json with file extension": { 1363 configName: "c", 1364 inConfigType: "json", 1365 outConfigType: "json", 1366 fileName: "c.json", 1367 input: jsonExample, 1368 expectedContent: jsonWriteExpected, 1369 }, 1370 "json without file extension": { 1371 configName: "c", 1372 inConfigType: "json", 1373 outConfigType: "json", 1374 fileName: "c", 1375 input: jsonExample, 1376 expectedContent: jsonWriteExpected, 1377 }, 1378 "json with file extension and mismatch type": { 1379 configName: "c", 1380 inConfigType: "json", 1381 outConfigType: "hcl", 1382 fileName: "c.json", 1383 input: jsonExample, 1384 expectedContent: jsonWriteExpected, 1385 }, 1386 "properties with file extension": { 1387 configName: "c", 1388 inConfigType: "properties", 1389 outConfigType: "properties", 1390 fileName: "c.properties", 1391 input: propertiesExample, 1392 expectedContent: propertiesWriteExpected, 1393 }, 1394 "properties without file extension": { 1395 configName: "c", 1396 inConfigType: "properties", 1397 outConfigType: "properties", 1398 fileName: "c", 1399 input: propertiesExample, 1400 expectedContent: propertiesWriteExpected, 1401 }, 1402 "yaml with file extension": { 1403 configName: "c", 1404 inConfigType: "yaml", 1405 outConfigType: "yaml", 1406 fileName: "c.yaml", 1407 input: yamlExample, 1408 expectedContent: yamlWriteExpected, 1409 }, 1410 "yaml without file extension": { 1411 configName: "c", 1412 inConfigType: "yaml", 1413 outConfigType: "yaml", 1414 fileName: "c", 1415 input: yamlExample, 1416 expectedContent: yamlWriteExpected, 1417 }, 1418 "yaml with file extension and mismatch type": { 1419 configName: "c", 1420 inConfigType: "yaml", 1421 outConfigType: "json", 1422 fileName: "c.yaml", 1423 input: yamlExample, 1424 expectedContent: yamlWriteExpected, 1425 }, 1426 } 1427 for name, tc := range testCases { 1428 t.Run(name, func(t *testing.T) { 1429 v := New() 1430 v.SetFs(fs) 1431 v.SetConfigName(tc.fileName) 1432 v.SetConfigType(tc.inConfigType) 1433 1434 err := v.ReadConfig(bytes.NewBuffer(tc.input)) 1435 if err != nil { 1436 t.Fatal(err) 1437 } 1438 v.SetConfigType(tc.outConfigType) 1439 if err := v.WriteConfigAs(tc.fileName); err != nil { 1440 t.Fatal(err) 1441 } 1442 read, err := afero.ReadFile(fs, tc.fileName) 1443 if err != nil { 1444 t.Fatal(err) 1445 } 1446 assert.Equal(t, tc.expectedContent, read) 1447 }) 1448 } 1449 } 1450 1451 func TestWriteConfigTOML(t *testing.T) { 1452 fs := afero.NewMemMapFs() 1453 1454 testCases := map[string]struct { 1455 configName string 1456 configType string 1457 fileName string 1458 input []byte 1459 }{ 1460 "with file extension": { 1461 configName: "c", 1462 configType: "toml", 1463 fileName: "c.toml", 1464 input: tomlExample, 1465 }, 1466 "without file extension": { 1467 configName: "c", 1468 configType: "toml", 1469 fileName: "c", 1470 input: tomlExample, 1471 }, 1472 } 1473 for name, tc := range testCases { 1474 t.Run(name, func(t *testing.T) { 1475 v := New() 1476 v.SetFs(fs) 1477 v.SetConfigName(tc.configName) 1478 v.SetConfigType(tc.configType) 1479 err := v.ReadConfig(bytes.NewBuffer(tc.input)) 1480 if err != nil { 1481 t.Fatal(err) 1482 } 1483 if err := v.WriteConfigAs(tc.fileName); err != nil { 1484 t.Fatal(err) 1485 } 1486 1487 // The TOML String method does not order the contents. 1488 // Therefore, we must read the generated file and compare the data. 1489 v2 := New() 1490 v2.SetFs(fs) 1491 v2.SetConfigName(tc.configName) 1492 v2.SetConfigType(tc.configType) 1493 v2.SetConfigFile(tc.fileName) 1494 err = v2.ReadInConfig() 1495 if err != nil { 1496 t.Fatal(err) 1497 } 1498 1499 assert.Equal(t, v.GetString("title"), v2.GetString("title")) 1500 assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio")) 1501 assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob")) 1502 assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization")) 1503 }) 1504 } 1505 } 1506 1507 func TestWriteConfigDotEnv(t *testing.T) { 1508 fs := afero.NewMemMapFs() 1509 testCases := map[string]struct { 1510 configName string 1511 configType string 1512 fileName string 1513 input []byte 1514 }{ 1515 "with file extension": { 1516 configName: "c", 1517 configType: "env", 1518 fileName: "c.env", 1519 input: dotenvExample, 1520 }, 1521 "without file extension": { 1522 configName: "c", 1523 configType: "env", 1524 fileName: "c", 1525 input: dotenvExample, 1526 }, 1527 } 1528 for name, tc := range testCases { 1529 t.Run(name, func(t *testing.T) { 1530 v := New() 1531 v.SetFs(fs) 1532 v.SetConfigName(tc.configName) 1533 v.SetConfigType(tc.configType) 1534 err := v.ReadConfig(bytes.NewBuffer(tc.input)) 1535 if err != nil { 1536 t.Fatal(err) 1537 } 1538 if err := v.WriteConfigAs(tc.fileName); err != nil { 1539 t.Fatal(err) 1540 } 1541 1542 // The TOML String method does not order the contents. 1543 // Therefore, we must read the generated file and compare the data. 1544 v2 := New() 1545 v2.SetFs(fs) 1546 v2.SetConfigName(tc.configName) 1547 v2.SetConfigType(tc.configType) 1548 v2.SetConfigFile(tc.fileName) 1549 err = v2.ReadInConfig() 1550 if err != nil { 1551 t.Fatal(err) 1552 } 1553 1554 assert.Equal(t, v.GetString("title_dotenv"), v2.GetString("title_dotenv")) 1555 assert.Equal(t, v.GetString("type_dotenv"), v2.GetString("type_dotenv")) 1556 assert.Equal(t, v.GetString("kind_dotenv"), v2.GetString("kind_dotenv")) 1557 }) 1558 } 1559 } 1560 1561 func TestSafeWriteConfig(t *testing.T) { 1562 v := New() 1563 fs := afero.NewMemMapFs() 1564 v.SetFs(fs) 1565 v.AddConfigPath("/test") 1566 v.SetConfigName("c") 1567 v.SetConfigType("yaml") 1568 require.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample))) 1569 require.NoError(t, v.SafeWriteConfig()) 1570 read, err := afero.ReadFile(fs, "/test/c.yaml") 1571 require.NoError(t, err) 1572 assert.Equal(t, yamlWriteExpected, read) 1573 } 1574 1575 func TestSafeWriteConfigWithMissingConfigPath(t *testing.T) { 1576 v := New() 1577 fs := afero.NewMemMapFs() 1578 v.SetFs(fs) 1579 v.SetConfigName("c") 1580 v.SetConfigType("yaml") 1581 require.EqualError(t, v.SafeWriteConfig(), "missing configuration for 'configPath'") 1582 } 1583 1584 func TestSafeWriteConfigWithExistingFile(t *testing.T) { 1585 v := New() 1586 fs := afero.NewMemMapFs() 1587 fs.Create("/test/c.yaml") 1588 v.SetFs(fs) 1589 v.AddConfigPath("/test") 1590 v.SetConfigName("c") 1591 v.SetConfigType("yaml") 1592 err := v.SafeWriteConfig() 1593 require.Error(t, err) 1594 _, ok := err.(ConfigFileAlreadyExistsError) 1595 assert.True(t, ok, "Expected ConfigFileAlreadyExistsError") 1596 } 1597 1598 func TestSafeWriteAsConfig(t *testing.T) { 1599 v := New() 1600 fs := afero.NewMemMapFs() 1601 v.SetFs(fs) 1602 err := v.ReadConfig(bytes.NewBuffer(yamlExample)) 1603 if err != nil { 1604 t.Fatal(err) 1605 } 1606 require.NoError(t, v.SafeWriteConfigAs("/test/c.yaml")) 1607 if _, err = afero.ReadFile(fs, "/test/c.yaml"); err != nil { 1608 t.Fatal(err) 1609 } 1610 } 1611 1612 func TestSafeWriteConfigAsWithExistingFile(t *testing.T) { 1613 v := New() 1614 fs := afero.NewMemMapFs() 1615 fs.Create("/test/c.yaml") 1616 v.SetFs(fs) 1617 err := v.SafeWriteConfigAs("/test/c.yaml") 1618 require.Error(t, err) 1619 _, ok := err.(ConfigFileAlreadyExistsError) 1620 assert.True(t, ok, "Expected ConfigFileAlreadyExistsError") 1621 } 1622 1623 var yamlMergeExampleTgt = []byte(` 1624 hello: 1625 pop: 37890 1626 largenum: 765432101234567 1627 num2pow63: 9223372036854775808 1628 world: 1629 - us 1630 - uk 1631 - fr 1632 - de 1633 `) 1634 1635 var yamlMergeExampleSrc = []byte(` 1636 hello: 1637 pop: 45000 1638 largenum: 7654321001234567 1639 universe: 1640 - mw 1641 - ad 1642 ints: 1643 - 1 1644 - 2 1645 fu: bar 1646 `) 1647 1648 func TestMergeConfig(t *testing.T) { 1649 v := New() 1650 v.SetConfigType("yml") 1651 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1652 t.Fatal(err) 1653 } 1654 1655 if pop := v.GetInt("hello.pop"); pop != 37890 { 1656 t.Fatalf("pop != 37890, = %d", pop) 1657 } 1658 1659 if pop := v.GetInt32("hello.pop"); pop != int32(37890) { 1660 t.Fatalf("pop != 37890, = %d", pop) 1661 } 1662 1663 if pop := v.GetInt64("hello.largenum"); pop != int64(765432101234567) { 1664 t.Fatalf("int64 largenum != 765432101234567, = %d", pop) 1665 } 1666 1667 if pop := v.GetUint("hello.pop"); pop != 37890 { 1668 t.Fatalf("uint pop != 37890, = %d", pop) 1669 } 1670 1671 if pop := v.GetUint32("hello.pop"); pop != 37890 { 1672 t.Fatalf("uint32 pop != 37890, = %d", pop) 1673 } 1674 1675 if pop := v.GetUint64("hello.num2pow63"); pop != 9223372036854775808 { 1676 t.Fatalf("uint64 num2pow63 != 9223372036854775808, = %d", pop) 1677 } 1678 1679 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1680 t.Fatalf("len(world) != 4, = %d", len(world)) 1681 } 1682 1683 if fu := v.GetString("fu"); fu != "" { 1684 t.Fatalf("fu != \"\", = %s", fu) 1685 } 1686 1687 if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 1688 t.Fatal(err) 1689 } 1690 1691 if pop := v.GetInt("hello.pop"); pop != 45000 { 1692 t.Fatalf("pop != 45000, = %d", pop) 1693 } 1694 1695 if pop := v.GetInt32("hello.pop"); pop != int32(45000) { 1696 t.Fatalf("pop != 45000, = %d", pop) 1697 } 1698 1699 if pop := v.GetInt64("hello.largenum"); pop != int64(7654321001234567) { 1700 t.Fatalf("int64 largenum != 7654321001234567, = %d", pop) 1701 } 1702 1703 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1704 t.Fatalf("len(world) != 4, = %d", len(world)) 1705 } 1706 1707 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 1708 t.Fatalf("len(universe) != 2, = %d", len(universe)) 1709 } 1710 1711 if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 { 1712 t.Fatalf("len(ints) != 2, = %d", len(ints)) 1713 } 1714 1715 if fu := v.GetString("fu"); fu != "bar" { 1716 t.Fatalf("fu != \"bar\", = %s", fu) 1717 } 1718 } 1719 1720 func TestMergeConfigNoMerge(t *testing.T) { 1721 v := New() 1722 v.SetConfigType("yml") 1723 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1724 t.Fatal(err) 1725 } 1726 1727 if pop := v.GetInt("hello.pop"); pop != 37890 { 1728 t.Fatalf("pop != 37890, = %d", pop) 1729 } 1730 1731 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1732 t.Fatalf("len(world) != 4, = %d", len(world)) 1733 } 1734 1735 if fu := v.GetString("fu"); fu != "" { 1736 t.Fatalf("fu != \"\", = %s", fu) 1737 } 1738 1739 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 1740 t.Fatal(err) 1741 } 1742 1743 if pop := v.GetInt("hello.pop"); pop != 45000 { 1744 t.Fatalf("pop != 45000, = %d", pop) 1745 } 1746 1747 if world := v.GetStringSlice("hello.world"); len(world) != 0 { 1748 t.Fatalf("len(world) != 0, = %d", len(world)) 1749 } 1750 1751 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 1752 t.Fatalf("len(universe) != 2, = %d", len(universe)) 1753 } 1754 1755 if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 { 1756 t.Fatalf("len(ints) != 2, = %d", len(ints)) 1757 } 1758 1759 if fu := v.GetString("fu"); fu != "bar" { 1760 t.Fatalf("fu != \"bar\", = %s", fu) 1761 } 1762 } 1763 1764 func TestMergeConfigMap(t *testing.T) { 1765 v := New() 1766 v.SetConfigType("yml") 1767 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1768 t.Fatal(err) 1769 } 1770 1771 assert := func(i int) { 1772 large := v.GetInt64("hello.largenum") 1773 pop := v.GetInt("hello.pop") 1774 if large != 765432101234567 { 1775 t.Fatal("Got large num:", large) 1776 } 1777 1778 if pop != i { 1779 t.Fatal("Got pop:", pop) 1780 } 1781 } 1782 1783 assert(37890) 1784 1785 update := map[string]interface{}{ 1786 "Hello": map[string]interface{}{ 1787 "Pop": 1234, 1788 }, 1789 "World": map[interface{}]interface{}{ 1790 "Rock": 345, 1791 }, 1792 } 1793 1794 if err := v.MergeConfigMap(update); err != nil { 1795 t.Fatal(err) 1796 } 1797 1798 if rock := v.GetInt("world.rock"); rock != 345 { 1799 t.Fatal("Got rock:", rock) 1800 } 1801 1802 assert(1234) 1803 } 1804 1805 func TestUnmarshalingWithAliases(t *testing.T) { 1806 v := New() 1807 v.SetDefault("ID", 1) 1808 v.Set("name", "Steve") 1809 v.Set("lastname", "Owen") 1810 1811 v.RegisterAlias("UserID", "ID") 1812 v.RegisterAlias("Firstname", "name") 1813 v.RegisterAlias("Surname", "lastname") 1814 1815 type config struct { 1816 ID int 1817 FirstName string 1818 Surname string 1819 } 1820 1821 var C config 1822 err := v.Unmarshal(&C) 1823 if err != nil { 1824 t.Fatalf("unable to decode into struct, %v", err) 1825 } 1826 1827 assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C) 1828 } 1829 1830 func TestSetConfigNameClearsFileCache(t *testing.T) { 1831 SetConfigFile("/tmp/config.yaml") 1832 SetConfigName("default") 1833 f, err := v.getConfigFile() 1834 if err == nil { 1835 t.Fatalf("config file cache should have been cleared") 1836 } 1837 assert.Empty(t, f) 1838 } 1839 1840 func TestShadowedNestedValue(t *testing.T) { 1841 config := `name: steve 1842 clothing: 1843 jacket: leather 1844 trousers: denim 1845 pants: 1846 size: large 1847 ` 1848 initConfig("yaml", config) 1849 1850 assert.Equal(t, "steve", GetString("name")) 1851 1852 polyester := "polyester" 1853 SetDefault("clothing.shirt", polyester) 1854 SetDefault("clothing.jacket.price", 100) 1855 1856 assert.Equal(t, "leather", GetString("clothing.jacket")) 1857 assert.Nil(t, Get("clothing.jacket.price")) 1858 assert.Equal(t, polyester, GetString("clothing.shirt")) 1859 1860 clothingSettings := AllSettings()["clothing"].(map[string]interface{}) 1861 assert.Equal(t, "leather", clothingSettings["jacket"]) 1862 assert.Equal(t, polyester, clothingSettings["shirt"]) 1863 } 1864 1865 func TestDotParameter(t *testing.T) { 1866 initJSON() 1867 // shoud take precedence over batters defined in jsonExample 1868 r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`)) 1869 unmarshalReader(r, v.config) 1870 1871 actual := Get("batters.batter") 1872 expected := []interface{}{map[string]interface{}{"type": "Small"}} 1873 assert.Equal(t, expected, actual) 1874 } 1875 1876 func TestCaseInsensitive(t *testing.T) { 1877 for _, config := range []struct { 1878 typ string 1879 content string 1880 }{ 1881 {"yaml", ` 1882 aBcD: 1 1883 eF: 1884 gH: 2 1885 iJk: 3 1886 Lm: 1887 nO: 4 1888 P: 1889 Q: 5 1890 R: 6 1891 `}, 1892 {"json", `{ 1893 "aBcD": 1, 1894 "eF": { 1895 "iJk": 3, 1896 "Lm": { 1897 "P": { 1898 "Q": 5, 1899 "R": 6 1900 }, 1901 "nO": 4 1902 }, 1903 "gH": 2 1904 } 1905 }`}, 1906 {"toml", `aBcD = 1 1907 [eF] 1908 gH = 2 1909 iJk = 3 1910 [eF.Lm] 1911 nO = 4 1912 [eF.Lm.P] 1913 Q = 5 1914 R = 6 1915 `}, 1916 } { 1917 doTestCaseInsensitive(t, config.typ, config.content) 1918 } 1919 } 1920 1921 func TestCaseInsensitiveSet(t *testing.T) { 1922 Reset() 1923 m1 := map[string]interface{}{ 1924 "Foo": 32, 1925 "Bar": map[interface{}]interface { 1926 }{ 1927 "ABc": "A", 1928 "cDE": "B"}, 1929 } 1930 1931 m2 := map[string]interface{}{ 1932 "Foo": 52, 1933 "Bar": map[interface{}]interface { 1934 }{ 1935 "bCd": "A", 1936 "eFG": "B"}, 1937 } 1938 1939 Set("Given1", m1) 1940 Set("Number1", 42) 1941 1942 SetDefault("Given2", m2) 1943 SetDefault("Number2", 52) 1944 1945 // Verify SetDefault 1946 if v := Get("number2"); v != 52 { 1947 t.Fatalf("Expected 52 got %q", v) 1948 } 1949 1950 if v := Get("given2.foo"); v != 52 { 1951 t.Fatalf("Expected 52 got %q", v) 1952 } 1953 1954 if v := Get("given2.bar.bcd"); v != "A" { 1955 t.Fatalf("Expected A got %q", v) 1956 } 1957 1958 if _, ok := m2["Foo"]; !ok { 1959 t.Fatal("Input map changed") 1960 } 1961 1962 // Verify Set 1963 if v := Get("number1"); v != 42 { 1964 t.Fatalf("Expected 42 got %q", v) 1965 } 1966 1967 if v := Get("given1.foo"); v != 32 { 1968 t.Fatalf("Expected 32 got %q", v) 1969 } 1970 1971 if v := Get("given1.bar.abc"); v != "A" { 1972 t.Fatalf("Expected A got %q", v) 1973 } 1974 1975 if _, ok := m1["Foo"]; !ok { 1976 t.Fatal("Input map changed") 1977 } 1978 } 1979 1980 func TestParseNested(t *testing.T) { 1981 type duration struct { 1982 Delay time.Duration 1983 } 1984 1985 type item struct { 1986 Name string 1987 Delay time.Duration 1988 Nested duration 1989 } 1990 1991 config := `[[parent]] 1992 delay="100ms" 1993 [parent.nested] 1994 delay="200ms" 1995 ` 1996 initConfig("toml", config) 1997 1998 var items []item 1999 err := v.UnmarshalKey("parent", &items) 2000 if err != nil { 2001 t.Fatalf("unable to decode into struct, %v", err) 2002 } 2003 2004 assert.Equal(t, 1, len(items)) 2005 assert.Equal(t, 100*time.Millisecond, items[0].Delay) 2006 assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay) 2007 } 2008 2009 func doTestCaseInsensitive(t *testing.T, typ, config string) { 2010 initConfig(typ, config) 2011 Set("RfD", true) 2012 assert.Equal(t, true, Get("rfd")) 2013 assert.Equal(t, true, Get("rFD")) 2014 assert.Equal(t, 1, cast.ToInt(Get("abcd"))) 2015 assert.Equal(t, 1, cast.ToInt(Get("Abcd"))) 2016 assert.Equal(t, 2, cast.ToInt(Get("ef.gh"))) 2017 assert.Equal(t, 3, cast.ToInt(Get("ef.ijk"))) 2018 assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no"))) 2019 assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q"))) 2020 } 2021 2022 func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) { 2023 watchDir, err := ioutil.TempDir("", "") 2024 require.Nil(t, err) 2025 configFile := path.Join(watchDir, "config.yaml") 2026 err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640) 2027 require.Nil(t, err) 2028 cleanup := func() { 2029 os.RemoveAll(watchDir) 2030 } 2031 v := New() 2032 v.SetConfigFile(configFile) 2033 err = v.ReadInConfig() 2034 require.Nil(t, err) 2035 require.Equal(t, "bar", v.Get("foo")) 2036 return v, configFile, cleanup 2037 } 2038 2039 func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) { 2040 watchDir, err := ioutil.TempDir("", "") 2041 require.Nil(t, err) 2042 dataDir1 := path.Join(watchDir, "data1") 2043 err = os.Mkdir(dataDir1, 0777) 2044 require.Nil(t, err) 2045 realConfigFile := path.Join(dataDir1, "config.yaml") 2046 t.Logf("Real config file location: %s\n", realConfigFile) 2047 err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640) 2048 require.Nil(t, err) 2049 cleanup := func() { 2050 os.RemoveAll(watchDir) 2051 } 2052 // now, symlink the tm `data1` dir to `data` in the baseDir 2053 os.Symlink(dataDir1, path.Join(watchDir, "data")) 2054 // and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml` 2055 configFile := path.Join(watchDir, "config.yaml") 2056 os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile) 2057 t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml")) 2058 // init Viper 2059 v := New() 2060 v.SetConfigFile(configFile) 2061 err = v.ReadInConfig() 2062 require.Nil(t, err) 2063 require.Equal(t, "bar", v.Get("foo")) 2064 return v, watchDir, configFile, cleanup 2065 } 2066 2067 func TestWatchFile(t *testing.T) { 2068 if runtime.GOOS == "linux" { 2069 // TODO(bep) FIX ME 2070 t.Skip("Skip test on Linux ...") 2071 } 2072 2073 t.Run("file content changed", func(t *testing.T) { 2074 // given a `config.yaml` file being watched 2075 v, configFile, cleanup := newViperWithConfigFile(t) 2076 defer cleanup() 2077 _, err := os.Stat(configFile) 2078 require.NoError(t, err) 2079 t.Logf("test config file: %s\n", configFile) 2080 wg := sync.WaitGroup{} 2081 wg.Add(1) 2082 v.OnConfigChange(func(in fsnotify.Event) { 2083 t.Logf("config file changed") 2084 wg.Done() 2085 }) 2086 v.WatchConfig() 2087 // when overwriting the file and waiting for the custom change notification handler to be triggered 2088 err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640) 2089 wg.Wait() 2090 // then the config value should have changed 2091 require.Nil(t, err) 2092 assert.Equal(t, "baz", v.Get("foo")) 2093 }) 2094 2095 t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) { 2096 // skip if not executed on Linux 2097 if runtime.GOOS != "linux" { 2098 t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...") 2099 } 2100 v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t) 2101 // defer cleanup() 2102 wg := sync.WaitGroup{} 2103 v.WatchConfig() 2104 v.OnConfigChange(func(in fsnotify.Event) { 2105 t.Logf("config file changed") 2106 wg.Done() 2107 }) 2108 wg.Add(1) 2109 // when link to another `config.yaml` file 2110 dataDir2 := path.Join(watchDir, "data2") 2111 err := os.Mkdir(dataDir2, 0777) 2112 require.Nil(t, err) 2113 configFile2 := path.Join(dataDir2, "config.yaml") 2114 err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640) 2115 require.Nil(t, err) 2116 // change the symlink using the `ln -sfn` command 2117 err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run() 2118 require.Nil(t, err) 2119 wg.Wait() 2120 // then 2121 require.Nil(t, err) 2122 assert.Equal(t, "baz", v.Get("foo")) 2123 }) 2124 } 2125 2126 func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) { 2127 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 2128 flags.String("foo.bar", "cobra_flag", "") 2129 2130 v := New() 2131 assert.NoError(t, v.BindPFlags(flags)) 2132 2133 config := &struct { 2134 Foo struct { 2135 Bar string 2136 } 2137 }{} 2138 2139 assert.NoError(t, v.Unmarshal(config)) 2140 assert.Equal(t, "cobra_flag", config.Foo.Bar) 2141 } 2142 2143 var yamlExampleWithDot = []byte(`Hacker: true 2144 name: steve 2145 hobbies: 2146 - skateboarding 2147 - snowboarding 2148 - go 2149 clothing: 2150 jacket: leather 2151 trousers: denim 2152 pants: 2153 size: large 2154 age: 35 2155 eyes : brown 2156 beard: true 2157 emails: 2158 steve@hacker.com: 2159 created: 01/02/03 2160 active: true 2161 `) 2162 2163 func TestKeyDelimiter(t *testing.T) { 2164 v := NewWithOptions(KeyDelimiter("::")) 2165 v.SetConfigType("yaml") 2166 r := strings.NewReader(string(yamlExampleWithDot)) 2167 2168 err := v.unmarshalReader(r, v.config) 2169 require.NoError(t, err) 2170 2171 values := map[string]interface{}{ 2172 "image": map[string]interface{}{ 2173 "repository": "someImage", 2174 "tag": "1.0.0", 2175 }, 2176 "ingress": map[string]interface{}{ 2177 "annotations": map[string]interface{}{ 2178 "traefik.frontend.rule.type": "PathPrefix", 2179 "traefik.ingress.kubernetes.io/ssl-redirect": "true", 2180 }, 2181 }, 2182 } 2183 2184 v.SetDefault("charts::values", values) 2185 2186 assert.Equal(t, "leather", v.GetString("clothing::jacket")) 2187 assert.Equal(t, "01/02/03", v.GetString("emails::steve@hacker.com::created")) 2188 2189 type config struct { 2190 Charts struct { 2191 Values map[string]interface{} 2192 } 2193 } 2194 2195 expected := config{ 2196 Charts: struct { 2197 Values map[string]interface{} 2198 }{ 2199 Values: values, 2200 }, 2201 } 2202 2203 var actual config 2204 2205 assert.NoError(t, v.Unmarshal(&actual)) 2206 2207 assert.Equal(t, expected, actual) 2208 } 2209 2210 func BenchmarkGetBool(b *testing.B) { 2211 key := "BenchmarkGetBool" 2212 v = New() 2213 v.Set(key, true) 2214 2215 for i := 0; i < b.N; i++ { 2216 if !v.GetBool(key) { 2217 b.Fatal("GetBool returned false") 2218 } 2219 } 2220 } 2221 2222 func BenchmarkGet(b *testing.B) { 2223 key := "BenchmarkGet" 2224 v = New() 2225 v.Set(key, true) 2226 2227 for i := 0; i < b.N; i++ { 2228 if !v.Get(key).(bool) { 2229 b.Fatal("Get returned false") 2230 } 2231 } 2232 } 2233 2234 // BenchmarkGetBoolFromMap is the "perfect result" for the above. 2235 func BenchmarkGetBoolFromMap(b *testing.B) { 2236 m := make(map[string]bool) 2237 key := "BenchmarkGetBool" 2238 m[key] = true 2239 2240 for i := 0; i < b.N; i++ { 2241 if !m[key] { 2242 b.Fatal("Map value was false") 2243 } 2244 } 2245 }