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