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