github.com/pupizoid/viper@v1.2.4/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.EqualValues(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.True(t, reflect.DeepEqual(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 {[]string{}, ""}, 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.EqualValues(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.EqualValues(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.EqualValues(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 world: 1121 - us 1122 - uk 1123 - fr 1124 - de 1125 `) 1126 1127 var yamlMergeExampleSrc = []byte(` 1128 hello: 1129 pop: 45000 1130 lagrenum: 7654321001234567 1131 universe: 1132 - mw 1133 - ad 1134 fu: bar 1135 `) 1136 1137 func TestMergeConfig(t *testing.T) { 1138 v := New() 1139 v.SetConfigType("yml") 1140 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1141 t.Fatal(err) 1142 } 1143 1144 if pop := v.GetInt("hello.pop"); pop != 37890 { 1145 t.Fatalf("pop != 37890, = %d", pop) 1146 } 1147 1148 if pop := v.GetInt32("hello.pop"); pop != int32(37890) { 1149 t.Fatalf("pop != 37890, = %d", pop) 1150 } 1151 1152 if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) { 1153 t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop) 1154 } 1155 1156 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1157 t.Fatalf("len(world) != 4, = %d", len(world)) 1158 } 1159 1160 if fu := v.GetString("fu"); fu != "" { 1161 t.Fatalf("fu != \"\", = %s", fu) 1162 } 1163 1164 if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 1165 t.Fatal(err) 1166 } 1167 1168 if pop := v.GetInt("hello.pop"); pop != 45000 { 1169 t.Fatalf("pop != 45000, = %d", pop) 1170 } 1171 1172 if pop := v.GetInt32("hello.pop"); pop != int32(45000) { 1173 t.Fatalf("pop != 45000, = %d", pop) 1174 } 1175 1176 if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) { 1177 t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop) 1178 } 1179 1180 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1181 t.Fatalf("len(world) != 4, = %d", len(world)) 1182 } 1183 1184 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 1185 t.Fatalf("len(universe) != 2, = %d", len(universe)) 1186 } 1187 1188 if fu := v.GetString("fu"); fu != "bar" { 1189 t.Fatalf("fu != \"bar\", = %s", fu) 1190 } 1191 } 1192 1193 func TestMergeConfigNoMerge(t *testing.T) { 1194 v := New() 1195 v.SetConfigType("yml") 1196 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1197 t.Fatal(err) 1198 } 1199 1200 if pop := v.GetInt("hello.pop"); pop != 37890 { 1201 t.Fatalf("pop != 37890, = %d", pop) 1202 } 1203 1204 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1205 t.Fatalf("len(world) != 4, = %d", len(world)) 1206 } 1207 1208 if fu := v.GetString("fu"); fu != "" { 1209 t.Fatalf("fu != \"\", = %s", fu) 1210 } 1211 1212 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 1213 t.Fatal(err) 1214 } 1215 1216 if pop := v.GetInt("hello.pop"); pop != 45000 { 1217 t.Fatalf("pop != 45000, = %d", pop) 1218 } 1219 1220 if world := v.GetStringSlice("hello.world"); len(world) != 0 { 1221 t.Fatalf("len(world) != 0, = %d", len(world)) 1222 } 1223 1224 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 1225 t.Fatalf("len(universe) != 2, = %d", len(universe)) 1226 } 1227 1228 if fu := v.GetString("fu"); fu != "bar" { 1229 t.Fatalf("fu != \"bar\", = %s", fu) 1230 } 1231 } 1232 1233 func TestUnmarshalingWithAliases(t *testing.T) { 1234 v := New() 1235 v.SetDefault("ID", 1) 1236 v.Set("name", "Steve") 1237 v.Set("lastname", "Owen") 1238 1239 v.RegisterAlias("UserID", "ID") 1240 v.RegisterAlias("Firstname", "name") 1241 v.RegisterAlias("Surname", "lastname") 1242 1243 type config struct { 1244 ID int 1245 FirstName string 1246 Surname string 1247 } 1248 1249 var C config 1250 err := v.Unmarshal(&C) 1251 if err != nil { 1252 t.Fatalf("unable to decode into struct, %v", err) 1253 } 1254 1255 assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C) 1256 } 1257 1258 func TestSetConfigNameClearsFileCache(t *testing.T) { 1259 SetConfigFile("/tmp/config.yaml") 1260 SetConfigName("default") 1261 f, err := v.getConfigFile() 1262 if err == nil { 1263 t.Fatalf("config file cache should have been cleared") 1264 } 1265 assert.Empty(t, f) 1266 } 1267 1268 func TestShadowedNestedValue(t *testing.T) { 1269 1270 config := `name: steve 1271 clothing: 1272 jacket: leather 1273 trousers: denim 1274 pants: 1275 size: large 1276 ` 1277 initConfig("yaml", config) 1278 1279 assert.Equal(t, "steve", GetString("name")) 1280 1281 polyester := "polyester" 1282 SetDefault("clothing.shirt", polyester) 1283 SetDefault("clothing.jacket.price", 100) 1284 1285 assert.Equal(t, "leather", GetString("clothing.jacket")) 1286 assert.Nil(t, Get("clothing.jacket.price")) 1287 assert.Equal(t, polyester, GetString("clothing.shirt")) 1288 1289 clothingSettings := AllSettings()["clothing"].(map[string]interface{}) 1290 assert.Equal(t, "leather", clothingSettings["jacket"]) 1291 assert.Equal(t, polyester, clothingSettings["shirt"]) 1292 } 1293 1294 func TestDotParameter(t *testing.T) { 1295 initJSON() 1296 // shoud take precedence over batters defined in jsonExample 1297 r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`)) 1298 unmarshalReader(r, v.config) 1299 1300 actual := Get("batters.batter") 1301 expected := []interface{}{map[string]interface{}{"type": "Small"}} 1302 assert.Equal(t, expected, actual) 1303 } 1304 1305 func TestCaseInsensitive(t *testing.T) { 1306 for _, config := range []struct { 1307 typ string 1308 content string 1309 }{ 1310 {"yaml", ` 1311 aBcD: 1 1312 eF: 1313 gH: 2 1314 iJk: 3 1315 Lm: 1316 nO: 4 1317 P: 1318 Q: 5 1319 R: 6 1320 `}, 1321 {"json", `{ 1322 "aBcD": 1, 1323 "eF": { 1324 "iJk": 3, 1325 "Lm": { 1326 "P": { 1327 "Q": 5, 1328 "R": 6 1329 }, 1330 "nO": 4 1331 }, 1332 "gH": 2 1333 } 1334 }`}, 1335 {"toml", `aBcD = 1 1336 [eF] 1337 gH = 2 1338 iJk = 3 1339 [eF.Lm] 1340 nO = 4 1341 [eF.Lm.P] 1342 Q = 5 1343 R = 6 1344 `}, 1345 } { 1346 doTestCaseInsensitive(t, config.typ, config.content) 1347 } 1348 } 1349 1350 func TestCaseInsensitiveSet(t *testing.T) { 1351 Reset() 1352 m1 := map[string]interface{}{ 1353 "Foo": 32, 1354 "Bar": map[interface{}]interface { 1355 }{ 1356 "ABc": "A", 1357 "cDE": "B"}, 1358 } 1359 1360 m2 := map[string]interface{}{ 1361 "Foo": 52, 1362 "Bar": map[interface{}]interface { 1363 }{ 1364 "bCd": "A", 1365 "eFG": "B"}, 1366 } 1367 1368 Set("Given1", m1) 1369 Set("Number1", 42) 1370 1371 SetDefault("Given2", m2) 1372 SetDefault("Number2", 52) 1373 1374 // Verify SetDefault 1375 if v := Get("number2"); v != 52 { 1376 t.Fatalf("Expected 52 got %q", v) 1377 } 1378 1379 if v := Get("given2.foo"); v != 52 { 1380 t.Fatalf("Expected 52 got %q", v) 1381 } 1382 1383 if v := Get("given2.bar.bcd"); v != "A" { 1384 t.Fatalf("Expected A got %q", v) 1385 } 1386 1387 if _, ok := m2["Foo"]; !ok { 1388 t.Fatal("Input map changed") 1389 } 1390 1391 // Verify Set 1392 if v := Get("number1"); v != 42 { 1393 t.Fatalf("Expected 42 got %q", v) 1394 } 1395 1396 if v := Get("given1.foo"); v != 32 { 1397 t.Fatalf("Expected 32 got %q", v) 1398 } 1399 1400 if v := Get("given1.bar.abc"); v != "A" { 1401 t.Fatalf("Expected A got %q", v) 1402 } 1403 1404 if _, ok := m1["Foo"]; !ok { 1405 t.Fatal("Input map changed") 1406 } 1407 } 1408 1409 func TestParseNested(t *testing.T) { 1410 type duration struct { 1411 Delay time.Duration 1412 } 1413 1414 type item struct { 1415 Name string 1416 Delay time.Duration 1417 Nested duration 1418 } 1419 1420 config := `[[parent]] 1421 delay="100ms" 1422 [parent.nested] 1423 delay="200ms" 1424 ` 1425 initConfig("toml", config) 1426 1427 var items []item 1428 err := v.UnmarshalKey("parent", &items) 1429 if err != nil { 1430 t.Fatalf("unable to decode into struct, %v", err) 1431 } 1432 1433 assert.Equal(t, 1, len(items)) 1434 assert.Equal(t, 100*time.Millisecond, items[0].Delay) 1435 assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay) 1436 } 1437 1438 func doTestCaseInsensitive(t *testing.T, typ, config string) { 1439 initConfig(typ, config) 1440 Set("RfD", true) 1441 assert.Equal(t, true, Get("rfd")) 1442 assert.Equal(t, true, Get("rFD")) 1443 assert.Equal(t, 1, cast.ToInt(Get("abcd"))) 1444 assert.Equal(t, 1, cast.ToInt(Get("Abcd"))) 1445 assert.Equal(t, 2, cast.ToInt(Get("ef.gh"))) 1446 assert.Equal(t, 3, cast.ToInt(Get("ef.ijk"))) 1447 assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no"))) 1448 assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q"))) 1449 1450 } 1451 1452 func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) { 1453 watchDir, err := ioutil.TempDir("", "") 1454 require.Nil(t, err) 1455 configFile := path.Join(watchDir, "config.yaml") 1456 err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640) 1457 require.Nil(t, err) 1458 cleanup := func() { 1459 os.RemoveAll(watchDir) 1460 } 1461 v := New() 1462 v.SetConfigFile(configFile) 1463 err = v.ReadInConfig() 1464 require.Nil(t, err) 1465 require.Equal(t, "bar", v.Get("foo")) 1466 return v, configFile, cleanup 1467 } 1468 1469 func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) { 1470 watchDir, err := ioutil.TempDir("", "") 1471 require.Nil(t, err) 1472 dataDir1 := path.Join(watchDir, "data1") 1473 err = os.Mkdir(dataDir1, 0777) 1474 require.Nil(t, err) 1475 realConfigFile := path.Join(dataDir1, "config.yaml") 1476 t.Logf("Real config file location: %s\n", realConfigFile) 1477 err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640) 1478 require.Nil(t, err) 1479 cleanup := func() { 1480 os.RemoveAll(watchDir) 1481 } 1482 // now, symlink the tm `data1` dir to `data` in the baseDir 1483 os.Symlink(dataDir1, path.Join(watchDir, "data")) 1484 // and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml` 1485 configFile := path.Join(watchDir, "config.yaml") 1486 os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile) 1487 t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml")) 1488 // init Viper 1489 v := New() 1490 v.SetConfigFile(configFile) 1491 err = v.ReadInConfig() 1492 require.Nil(t, err) 1493 require.Equal(t, "bar", v.Get("foo")) 1494 return v, watchDir, configFile, cleanup 1495 } 1496 1497 func TestWatchFile(t *testing.T) { 1498 1499 t.Run("file content changed", func(t *testing.T) { 1500 // given a `config.yaml` file being watched 1501 v, configFile, cleanup := newViperWithConfigFile(t) 1502 defer cleanup() 1503 _, err := os.Stat(configFile) 1504 require.NoError(t, err) 1505 t.Logf("test config file: %s\n", configFile) 1506 wg := sync.WaitGroup{} 1507 wg.Add(1) 1508 v.OnConfigChange(func(in fsnotify.Event) { 1509 t.Logf("config file changed") 1510 wg.Done() 1511 }) 1512 v.WatchConfig() 1513 // when overwriting the file and waiting for the custom change notification handler to be triggered 1514 err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640) 1515 wg.Wait() 1516 // then the config value should have changed 1517 require.Nil(t, err) 1518 assert.Equal(t, "baz", v.Get("foo")) 1519 }) 1520 1521 t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) { 1522 // skip if not executed on Linux 1523 if runtime.GOOS != "linux" { 1524 t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...") 1525 } 1526 v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t) 1527 // defer cleanup() 1528 wg := sync.WaitGroup{} 1529 v.WatchConfig() 1530 v.OnConfigChange(func(in fsnotify.Event) { 1531 t.Logf("config file changed") 1532 wg.Done() 1533 }) 1534 wg.Add(1) 1535 // when link to another `config.yaml` file 1536 dataDir2 := path.Join(watchDir, "data2") 1537 err := os.Mkdir(dataDir2, 0777) 1538 require.Nil(t, err) 1539 configFile2 := path.Join(dataDir2, "config.yaml") 1540 err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640) 1541 require.Nil(t, err) 1542 // change the symlink using the `ln -sfn` command 1543 err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run() 1544 require.Nil(t, err) 1545 wg.Wait() 1546 // then 1547 require.Nil(t, err) 1548 assert.Equal(t, "baz", v.Get("foo")) 1549 }) 1550 1551 } 1552 1553 func BenchmarkGetBool(b *testing.B) { 1554 key := "BenchmarkGetBool" 1555 v = New() 1556 v.Set(key, true) 1557 1558 for i := 0; i < b.N; i++ { 1559 if !v.GetBool(key) { 1560 b.Fatal("GetBool returned false") 1561 } 1562 } 1563 } 1564 1565 func BenchmarkGet(b *testing.B) { 1566 key := "BenchmarkGet" 1567 v = New() 1568 v.Set(key, true) 1569 1570 for i := 0; i < b.N; i++ { 1571 if !v.Get(key).(bool) { 1572 b.Fatal("Get returned false") 1573 } 1574 } 1575 } 1576 1577 // This is the "perfect result" for the above. 1578 func BenchmarkGetBoolFromMap(b *testing.B) { 1579 m := make(map[string]bool) 1580 key := "BenchmarkGetBool" 1581 m[key] = true 1582 1583 for i := 0; i < b.N; i++ { 1584 if !m[key] { 1585 b.Fatal("Map value was false") 1586 } 1587 } 1588 } 1589 1590 func TestRemoveKey(t *testing.T) { 1591 v := New() 1592 1593 v.Set("flat", 123) 1594 v.Set("test.inner", 321) 1595 v.Set("test.inner2", 456) 1596 1597 assert.EqualValues(t, 123, v.override["flat"]) 1598 assert.EqualValues(t, 321, v.override["test"].(map[string]interface{})["inner"]) 1599 assert.EqualValues(t, 456, v.override["test"].(map[string]interface{})["inner2"]) 1600 1601 v.RemoveKey("flat") 1602 v.RemoveKey("test.inner") 1603 1604 assert.EqualValues(t, nil, v.Get("flat")) 1605 assert.EqualValues(t, nil, v.Get("test.inner")) 1606 assert.EqualValues(t, 456, v.Get("test.inner2")) 1607 1608 v.RemoveKey("test") 1609 1610 assert.EqualValues(t, nil, v.Get("test")) 1611 }