github.com/kannman/viper@v1.0.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 "fmt" 11 "io" 12 "io/ioutil" 13 "os" 14 "path" 15 "reflect" 16 "sort" 17 "strings" 18 "testing" 19 "time" 20 21 "github.com/spf13/afero" 22 "github.com/spf13/cast" 23 24 "github.com/spf13/pflag" 25 "github.com/stretchr/testify/assert" 26 ) 27 28 var yamlExample = []byte(`Hacker: true 29 name: steve 30 hobbies: 31 - skateboarding 32 - snowboarding 33 - go 34 clothing: 35 jacket: leather 36 trousers: denim 37 pants: 38 size: large 39 age: 35 40 eyes : brown 41 beard: true 42 `) 43 44 var yamlExampleWithExtras = []byte(`Existing: true 45 Bogus: true 46 `) 47 48 type testUnmarshalExtra struct { 49 Existing bool 50 } 51 52 var tomlExample = []byte(` 53 title = "TOML Example" 54 55 [owner] 56 organization = "MongoDB" 57 Bio = "MongoDB Chief Developer Advocate & Hacker at Large" 58 dob = 1979-05-27T07:32:00Z # First class dates? Why not?`) 59 60 var jsonExample = []byte(`{ 61 "id": "0001", 62 "type": "donut", 63 "name": "Cake", 64 "ppu": 0.55, 65 "batters": { 66 "batter": [ 67 { "type": "Regular" }, 68 { "type": "Chocolate" }, 69 { "type": "Blueberry" }, 70 { "type": "Devil's Food" } 71 ] 72 } 73 }`) 74 75 var propertiesExample = []byte(` 76 p_id: 0001 77 p_type: donut 78 p_name: Cake 79 p_ppu: 0.55 80 p_batters.batter.type: Regular 81 `) 82 83 var remoteExample = []byte(`{ 84 "id":"0002", 85 "type":"cronut", 86 "newkey":"remote" 87 }`) 88 89 func initConfigs() { 90 Reset() 91 var r io.Reader 92 SetConfigType("yaml") 93 r = bytes.NewReader(yamlExample) 94 unmarshalReader(r, v.config) 95 96 SetConfigType("json") 97 r = bytes.NewReader(jsonExample) 98 unmarshalReader(r, v.config) 99 100 SetConfigType("properties") 101 r = bytes.NewReader(propertiesExample) 102 unmarshalReader(r, v.config) 103 104 SetConfigType("toml") 105 r = bytes.NewReader(tomlExample) 106 unmarshalReader(r, v.config) 107 108 SetConfigType("json") 109 remote := bytes.NewReader(remoteExample) 110 unmarshalReader(remote, v.kvstore) 111 } 112 113 func initConfig(typ, config string) { 114 Reset() 115 SetConfigType(typ) 116 r := strings.NewReader(config) 117 118 if err := unmarshalReader(r, v.config); err != nil { 119 panic(err) 120 } 121 } 122 123 func initYAML() { 124 initConfig("yaml", string(yamlExample)) 125 } 126 127 func initJSON() { 128 Reset() 129 SetConfigType("json") 130 r := bytes.NewReader(jsonExample) 131 132 unmarshalReader(r, v.config) 133 } 134 135 func initProperties() { 136 Reset() 137 SetConfigType("properties") 138 r := bytes.NewReader(propertiesExample) 139 140 unmarshalReader(r, v.config) 141 } 142 143 func initTOML() { 144 Reset() 145 SetConfigType("toml") 146 r := bytes.NewReader(tomlExample) 147 148 unmarshalReader(r, v.config) 149 } 150 151 // make directories for testing 152 func initDirs(t *testing.T) (string, string, func()) { 153 154 var ( 155 testDirs = []string{`a a`, `b`, `c\c`, `D_`} 156 config = `improbable` 157 ) 158 159 root, err := ioutil.TempDir("", "") 160 161 cleanup := true 162 defer func() { 163 if cleanup { 164 os.Chdir("..") 165 os.RemoveAll(root) 166 } 167 }() 168 169 assert.Nil(t, err) 170 171 err = os.Chdir(root) 172 assert.Nil(t, err) 173 174 for _, dir := range testDirs { 175 err = os.Mkdir(dir, 0750) 176 assert.Nil(t, err) 177 178 err = ioutil.WriteFile( 179 path.Join(dir, config+".toml"), 180 []byte("key = \"value is "+dir+"\"\n"), 181 0640) 182 assert.Nil(t, err) 183 } 184 185 cleanup = false 186 return root, config, func() { 187 os.Chdir("..") 188 os.RemoveAll(root) 189 } 190 } 191 192 //stubs for PFlag Values 193 type stringValue string 194 195 func newStringValue(val string, p *string) *stringValue { 196 *p = val 197 return (*stringValue)(p) 198 } 199 200 func (s *stringValue) Set(val string) error { 201 *s = stringValue(val) 202 return nil 203 } 204 205 func (s *stringValue) Type() string { 206 return "string" 207 } 208 209 func (s *stringValue) String() string { 210 return fmt.Sprintf("%s", *s) 211 } 212 213 func TestBasics(t *testing.T) { 214 SetConfigFile("/tmp/config.yaml") 215 filename, err := v.getConfigFile() 216 assert.Equal(t, "/tmp/config.yaml", filename) 217 assert.NoError(t, err) 218 } 219 220 func TestDefault(t *testing.T) { 221 SetDefault("age", 45) 222 assert.Equal(t, 45, Get("age")) 223 224 SetDefault("clothing.jacket", "slacks") 225 assert.Equal(t, "slacks", Get("clothing.jacket")) 226 227 SetConfigType("yaml") 228 err := ReadConfig(bytes.NewBuffer(yamlExample)) 229 230 assert.NoError(t, err) 231 assert.Equal(t, "leather", Get("clothing.jacket")) 232 } 233 234 func TestUnmarshaling(t *testing.T) { 235 SetConfigType("yaml") 236 r := bytes.NewReader(yamlExample) 237 238 unmarshalReader(r, v.config) 239 assert.True(t, InConfig("name")) 240 assert.False(t, InConfig("state")) 241 assert.Equal(t, "steve", Get("name")) 242 assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies")) 243 assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing")) 244 assert.Equal(t, 35, Get("age")) 245 } 246 247 func TestUnmarshalExact(t *testing.T) { 248 vip := New() 249 target := &testUnmarshalExtra{} 250 vip.SetConfigType("yaml") 251 r := bytes.NewReader(yamlExampleWithExtras) 252 vip.ReadConfig(r) 253 err := vip.UnmarshalExact(target) 254 if err == nil { 255 t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields") 256 } 257 } 258 259 func TestOverrides(t *testing.T) { 260 Set("age", 40) 261 assert.Equal(t, 40, Get("age")) 262 } 263 264 func TestDefaultPost(t *testing.T) { 265 assert.NotEqual(t, "NYC", Get("state")) 266 SetDefault("state", "NYC") 267 assert.Equal(t, "NYC", Get("state")) 268 } 269 270 func TestAliases(t *testing.T) { 271 RegisterAlias("years", "age") 272 assert.Equal(t, 40, Get("years")) 273 Set("years", 45) 274 assert.Equal(t, 45, Get("age")) 275 } 276 277 func TestAliasInConfigFile(t *testing.T) { 278 // the config file specifies "beard". If we make this an alias for 279 // "hasbeard", we still want the old config file to work with beard. 280 RegisterAlias("beard", "hasbeard") 281 assert.Equal(t, true, Get("hasbeard")) 282 Set("hasbeard", false) 283 assert.Equal(t, false, Get("beard")) 284 } 285 286 func TestYML(t *testing.T) { 287 initYAML() 288 assert.Equal(t, "steve", Get("name")) 289 } 290 291 func TestJSON(t *testing.T) { 292 initJSON() 293 assert.Equal(t, "0001", Get("id")) 294 } 295 296 func TestProperties(t *testing.T) { 297 initProperties() 298 assert.Equal(t, "0001", Get("p_id")) 299 } 300 301 func TestTOML(t *testing.T) { 302 initTOML() 303 assert.Equal(t, "TOML Example", Get("title")) 304 } 305 306 func TestEnv(t *testing.T) { 307 initJSON() 308 309 BindEnv("id") 310 BindEnv("f", "FOOD") 311 312 os.Setenv("ID", "13") 313 os.Setenv("FOOD", "apple") 314 os.Setenv("NAME", "crunk") 315 316 assert.Equal(t, "13", Get("id")) 317 assert.Equal(t, "apple", Get("f")) 318 assert.Equal(t, "Cake", Get("name")) 319 320 AutomaticEnv() 321 322 assert.Equal(t, "crunk", Get("name")) 323 324 } 325 326 func TestEnvPrefix(t *testing.T) { 327 initJSON() 328 329 SetEnvPrefix("foo") // will be uppercased automatically 330 BindEnv("id") 331 BindEnv("f", "FOOD") // not using prefix 332 333 os.Setenv("FOO_ID", "13") 334 os.Setenv("FOOD", "apple") 335 os.Setenv("FOO_NAME", "crunk") 336 337 assert.Equal(t, "13", Get("id")) 338 assert.Equal(t, "apple", Get("f")) 339 assert.Equal(t, "Cake", Get("name")) 340 341 AutomaticEnv() 342 343 assert.Equal(t, "crunk", Get("name")) 344 } 345 346 func TestAutoEnv(t *testing.T) { 347 Reset() 348 349 AutomaticEnv() 350 os.Setenv("FOO_BAR", "13") 351 assert.Equal(t, "13", Get("foo_bar")) 352 } 353 354 func TestAutoEnvWithPrefix(t *testing.T) { 355 Reset() 356 357 AutomaticEnv() 358 SetEnvPrefix("Baz") 359 os.Setenv("BAZ_BAR", "13") 360 assert.Equal(t, "13", Get("bar")) 361 } 362 363 func TestSetEnvKeyReplacer(t *testing.T) { 364 Reset() 365 366 AutomaticEnv() 367 os.Setenv("REFRESH_INTERVAL", "30s") 368 369 replacer := strings.NewReplacer("-", "_") 370 SetEnvKeyReplacer(replacer) 371 372 assert.Equal(t, "30s", Get("refresh-interval")) 373 } 374 375 func TestAllKeys(t *testing.T) { 376 initConfigs() 377 378 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"} 379 dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 380 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"} 381 382 var allkeys sort.StringSlice 383 allkeys = AllKeys() 384 allkeys.Sort() 385 ks.Sort() 386 387 assert.Equal(t, ks, allkeys) 388 assert.Equal(t, all, AllSettings()) 389 } 390 391 func TestAllKeysWithEnv(t *testing.T) { 392 v := New() 393 394 // bind and define environment variables (including a nested one) 395 v.BindEnv("id") 396 v.BindEnv("foo.bar") 397 v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) 398 os.Setenv("ID", "13") 399 os.Setenv("FOO_BAR", "baz") 400 401 expectedKeys := sort.StringSlice{"id", "foo.bar"} 402 expectedKeys.Sort() 403 keys := sort.StringSlice(v.AllKeys()) 404 keys.Sort() 405 assert.Equal(t, expectedKeys, keys) 406 } 407 408 func TestAliasesOfAliases(t *testing.T) { 409 Set("Title", "Checking Case") 410 RegisterAlias("Foo", "Bar") 411 RegisterAlias("Bar", "Title") 412 assert.Equal(t, "Checking Case", Get("FOO")) 413 } 414 415 func TestRecursiveAliases(t *testing.T) { 416 RegisterAlias("Baz", "Roo") 417 RegisterAlias("Roo", "baz") 418 } 419 420 func TestUnmarshal(t *testing.T) { 421 SetDefault("port", 1313) 422 Set("name", "Steve") 423 Set("duration", "1s1ms") 424 425 type config struct { 426 Port int 427 Name string 428 Duration time.Duration 429 } 430 431 var C config 432 433 err := Unmarshal(&C) 434 if err != nil { 435 t.Fatalf("unable to decode into struct, %v", err) 436 } 437 438 assert.Equal(t, &config{Name: "Steve", Port: 1313, Duration: time.Second + time.Millisecond}, &C) 439 440 Set("port", 1234) 441 err = Unmarshal(&C) 442 if err != nil { 443 t.Fatalf("unable to decode into struct, %v", err) 444 } 445 assert.Equal(t, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond}, &C) 446 } 447 448 func TestBindPFlags(t *testing.T) { 449 v := New() // create independent Viper object 450 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) 451 452 var testValues = map[string]*string{ 453 "host": nil, 454 "port": nil, 455 "endpoint": nil, 456 } 457 458 var mutatedTestValues = map[string]string{ 459 "host": "localhost", 460 "port": "6060", 461 "endpoint": "/public", 462 } 463 464 for name := range testValues { 465 testValues[name] = flagSet.String(name, "", "test") 466 } 467 468 err := v.BindPFlags(flagSet) 469 if err != nil { 470 t.Fatalf("error binding flag set, %v", err) 471 } 472 473 flagSet.VisitAll(func(flag *pflag.Flag) { 474 flag.Value.Set(mutatedTestValues[flag.Name]) 475 flag.Changed = true 476 }) 477 478 for name, expected := range mutatedTestValues { 479 assert.Equal(t, expected, v.Get(name)) 480 } 481 482 } 483 484 func TestBindPFlagsStringSlice(t *testing.T) { 485 for _, testValue := range []struct { 486 Expected []string 487 Value string 488 }{ 489 {[]string{}, ""}, 490 {[]string{"jeden"}, "jeden"}, 491 {[]string{"dwa", "trzy"}, "dwa,trzy"}, 492 {[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""}} { 493 494 for _, changed := range []bool{true, false} { 495 v := New() // create independent Viper object 496 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) 497 flagSet.StringSlice("stringslice", testValue.Expected, "test") 498 flagSet.Visit(func(f *pflag.Flag) { 499 if len(testValue.Value) > 0 { 500 f.Value.Set(testValue.Value) 501 f.Changed = changed 502 } 503 }) 504 505 err := v.BindPFlags(flagSet) 506 if err != nil { 507 t.Fatalf("error binding flag set, %v", err) 508 } 509 510 type TestStr struct { 511 StringSlice []string 512 } 513 val := &TestStr{} 514 if err := v.Unmarshal(val); err != nil { 515 t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err) 516 } 517 assert.Equal(t, testValue.Expected, val.StringSlice) 518 } 519 } 520 } 521 522 func TestBindPFlag(t *testing.T) { 523 var testString = "testing" 524 var testValue = newStringValue(testString, &testString) 525 526 flag := &pflag.Flag{ 527 Name: "testflag", 528 Value: testValue, 529 Changed: false, 530 } 531 532 BindPFlag("testvalue", flag) 533 534 assert.Equal(t, testString, Get("testvalue")) 535 536 flag.Value.Set("testing_mutate") 537 flag.Changed = true //hack for pflag usage 538 539 assert.Equal(t, "testing_mutate", Get("testvalue")) 540 541 } 542 543 func TestBoundCaseSensitivity(t *testing.T) { 544 assert.Equal(t, "brown", Get("eyes")) 545 546 BindEnv("eYEs", "TURTLE_EYES") 547 os.Setenv("TURTLE_EYES", "blue") 548 549 assert.Equal(t, "blue", Get("eyes")) 550 551 var testString = "green" 552 var testValue = newStringValue(testString, &testString) 553 554 flag := &pflag.Flag{ 555 Name: "eyeballs", 556 Value: testValue, 557 Changed: true, 558 } 559 560 BindPFlag("eYEs", flag) 561 assert.Equal(t, "green", Get("eyes")) 562 563 } 564 565 func TestSizeInBytes(t *testing.T) { 566 input := map[string]uint{ 567 "": 0, 568 "b": 0, 569 "12 bytes": 0, 570 "200000000000gb": 0, 571 "12 b": 12, 572 "43 MB": 43 * (1 << 20), 573 "10mb": 10 * (1 << 20), 574 "1gb": 1 << 30, 575 } 576 577 for str, expected := range input { 578 assert.Equal(t, expected, parseSizeInBytes(str), str) 579 } 580 } 581 582 func TestFindsNestedKeys(t *testing.T) { 583 initConfigs() 584 dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 585 586 Set("super", map[string]interface{}{ 587 "deep": map[string]interface{}{ 588 "nested": "value", 589 }, 590 }) 591 592 expected := map[string]interface{}{ 593 "super": map[string]interface{}{ 594 "deep": map[string]interface{}{ 595 "nested": "value", 596 }, 597 }, 598 "super.deep": map[string]interface{}{ 599 "nested": "value", 600 }, 601 "super.deep.nested": "value", 602 "owner.organization": "MongoDB", 603 "batters.batter": []interface{}{ 604 map[string]interface{}{ 605 "type": "Regular", 606 }, 607 map[string]interface{}{ 608 "type": "Chocolate", 609 }, 610 map[string]interface{}{ 611 "type": "Blueberry", 612 }, 613 map[string]interface{}{ 614 "type": "Devil's Food", 615 }, 616 }, 617 "hobbies": []interface{}{ 618 "skateboarding", "snowboarding", "go", 619 }, 620 "title": "TOML Example", 621 "newkey": "remote", 622 "batters": map[string]interface{}{ 623 "batter": []interface{}{ 624 map[string]interface{}{ 625 "type": "Regular", 626 }, 627 map[string]interface{}{ 628 "type": "Chocolate", 629 }, map[string]interface{}{ 630 "type": "Blueberry", 631 }, map[string]interface{}{ 632 "type": "Devil's Food", 633 }, 634 }, 635 }, 636 "eyes": "brown", 637 "age": 35, 638 "owner": map[string]interface{}{ 639 "organization": "MongoDB", 640 "bio": "MongoDB Chief Developer Advocate & Hacker at Large", 641 "dob": dob, 642 }, 643 "owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large", 644 "type": "donut", 645 "id": "0001", 646 "name": "Cake", 647 "hacker": true, 648 "ppu": 0.55, 649 "clothing": map[string]interface{}{ 650 "jacket": "leather", 651 "trousers": "denim", 652 "pants": map[string]interface{}{ 653 "size": "large", 654 }, 655 }, 656 "clothing.jacket": "leather", 657 "clothing.pants.size": "large", 658 "clothing.trousers": "denim", 659 "owner.dob": dob, 660 "beard": true, 661 } 662 663 for key, expectedValue := range expected { 664 665 assert.Equal(t, expectedValue, v.Get(key)) 666 } 667 668 } 669 670 func TestReadBufConfig(t *testing.T) { 671 v := New() 672 v.SetConfigType("yaml") 673 v.ReadConfig(bytes.NewBuffer(yamlExample)) 674 t.Log(v.AllKeys()) 675 676 assert.True(t, v.InConfig("name")) 677 assert.False(t, v.InConfig("state")) 678 assert.Equal(t, "steve", v.Get("name")) 679 assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies")) 680 assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing")) 681 assert.Equal(t, 35, v.Get("age")) 682 } 683 684 func TestIsSet(t *testing.T) { 685 v := New() 686 v.SetConfigType("yaml") 687 v.ReadConfig(bytes.NewBuffer(yamlExample)) 688 assert.True(t, v.IsSet("clothing.jacket")) 689 assert.False(t, v.IsSet("clothing.jackets")) 690 assert.False(t, v.IsSet("helloworld")) 691 v.Set("helloworld", "fubar") 692 assert.True(t, v.IsSet("helloworld")) 693 } 694 695 func TestDirsSearch(t *testing.T) { 696 697 root, config, cleanup := initDirs(t) 698 defer cleanup() 699 700 v := New() 701 v.SetConfigName(config) 702 v.SetDefault(`key`, `default`) 703 704 entries, err := ioutil.ReadDir(root) 705 for _, e := range entries { 706 if e.IsDir() { 707 v.AddConfigPath(e.Name()) 708 } 709 } 710 711 err = v.ReadInConfig() 712 assert.Nil(t, err) 713 714 assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`)) 715 } 716 717 func TestWrongDirsSearchNotFound(t *testing.T) { 718 719 _, config, cleanup := initDirs(t) 720 defer cleanup() 721 722 v := New() 723 v.SetConfigName(config) 724 v.SetDefault(`key`, `default`) 725 726 v.AddConfigPath(`whattayoutalkingbout`) 727 v.AddConfigPath(`thispathaintthere`) 728 729 err := v.ReadInConfig() 730 assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) 731 732 // Even though config did not load and the error might have 733 // been ignored by the client, the default still loads 734 assert.Equal(t, `default`, v.GetString(`key`)) 735 } 736 737 func TestWrongDirsSearchNotFoundForMerge(t *testing.T) { 738 739 _, config, cleanup := initDirs(t) 740 defer cleanup() 741 742 v := New() 743 v.SetConfigName(config) 744 v.SetDefault(`key`, `default`) 745 746 v.AddConfigPath(`whattayoutalkingbout`) 747 v.AddConfigPath(`thispathaintthere`) 748 749 err := v.MergeInConfig() 750 assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) 751 752 // Even though config did not load and the error might have 753 // been ignored by the client, the default still loads 754 assert.Equal(t, `default`, v.GetString(`key`)) 755 } 756 757 func TestSub(t *testing.T) { 758 v := New() 759 v.SetConfigType("yaml") 760 v.ReadConfig(bytes.NewBuffer(yamlExample)) 761 762 subv := v.Sub("clothing") 763 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size")) 764 765 subv = v.Sub("clothing.pants") 766 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size")) 767 768 subv = v.Sub("clothing.pants.size") 769 assert.Equal(t, (*Viper)(nil), subv) 770 771 subv = v.Sub("missing.key") 772 assert.Equal(t, (*Viper)(nil), subv) 773 } 774 775 var jsonWriteExpected = []byte(`{ 776 "batters": { 777 "batter": [ 778 { 779 "type": "Regular" 780 }, 781 { 782 "type": "Chocolate" 783 }, 784 { 785 "type": "Blueberry" 786 }, 787 { 788 "type": "Devil's Food" 789 } 790 ] 791 }, 792 "id": "0001", 793 "name": "Cake", 794 "ppu": 0.55, 795 "type": "donut" 796 }`) 797 798 func TestWriteConfigJson(t *testing.T) { 799 v := New() 800 fs := afero.NewMemMapFs() 801 v.SetFs(fs) 802 v.SetConfigName("c") 803 v.SetConfigType("json") 804 err := v.ReadConfig(bytes.NewBuffer(jsonExample)) 805 if err != nil { 806 t.Fatal(err) 807 } 808 if err := v.WriteConfigAs("c.json"); err != nil { 809 t.Fatal(err) 810 } 811 read, err := afero.ReadFile(fs, "c.json") 812 if err != nil { 813 t.Fatal(err) 814 } 815 assert.Equal(t, jsonWriteExpected, read) 816 } 817 818 var propertiesWriteExpected = []byte(`p_id = 0001 819 p_type = donut 820 p_name = Cake 821 p_ppu = 0.55 822 p_batters.batter.type = Regular 823 `) 824 825 func TestWriteConfigProperties(t *testing.T) { 826 v := New() 827 fs := afero.NewMemMapFs() 828 v.SetFs(fs) 829 v.SetConfigName("c") 830 v.SetConfigType("properties") 831 err := v.ReadConfig(bytes.NewBuffer(propertiesExample)) 832 if err != nil { 833 t.Fatal(err) 834 } 835 if err := v.WriteConfigAs("c.properties"); err != nil { 836 t.Fatal(err) 837 } 838 read, err := afero.ReadFile(fs, "c.properties") 839 if err != nil { 840 t.Fatal(err) 841 } 842 assert.Equal(t, propertiesWriteExpected, read) 843 } 844 845 func TestWriteConfigTOML(t *testing.T) { 846 fs := afero.NewMemMapFs() 847 v := New() 848 v.SetFs(fs) 849 v.SetConfigName("c") 850 v.SetConfigType("toml") 851 err := v.ReadConfig(bytes.NewBuffer(tomlExample)) 852 if err != nil { 853 t.Fatal(err) 854 } 855 if err := v.WriteConfigAs("c.toml"); err != nil { 856 t.Fatal(err) 857 } 858 859 // The TOML String method does not order the contents. 860 // Therefore, we must read the generated file and compare the data. 861 v2 := New() 862 v2.SetFs(fs) 863 v2.SetConfigName("c") 864 v2.SetConfigType("toml") 865 v2.SetConfigFile("c.toml") 866 err = v2.ReadInConfig() 867 if err != nil { 868 t.Fatal(err) 869 } 870 871 assert.Equal(t, v.GetString("title"), v2.GetString("title")) 872 assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio")) 873 assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob")) 874 assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization")) 875 } 876 877 var yamlWriteExpected = []byte(`age: 35 878 beard: true 879 clothing: 880 jacket: leather 881 pants: 882 size: large 883 trousers: denim 884 eyes: brown 885 hacker: true 886 hobbies: 887 - skateboarding 888 - snowboarding 889 - go 890 name: steve 891 `) 892 893 func TestWriteConfigYAML(t *testing.T) { 894 v := New() 895 fs := afero.NewMemMapFs() 896 v.SetFs(fs) 897 v.SetConfigName("c") 898 v.SetConfigType("yaml") 899 err := v.ReadConfig(bytes.NewBuffer(yamlExample)) 900 if err != nil { 901 t.Fatal(err) 902 } 903 if err := v.WriteConfigAs("c.yaml"); err != nil { 904 t.Fatal(err) 905 } 906 read, err := afero.ReadFile(fs, "c.yaml") 907 if err != nil { 908 t.Fatal(err) 909 } 910 assert.Equal(t, yamlWriteExpected, read) 911 } 912 913 var yamlMergeExampleTgt = []byte(` 914 hello: 915 pop: 37890 916 lagrenum: 765432101234567 917 world: 918 - us 919 - uk 920 - fr 921 - de 922 `) 923 924 var yamlMergeExampleSrc = []byte(` 925 hello: 926 pop: 45000 927 lagrenum: 7654321001234567 928 universe: 929 - mw 930 - ad 931 fu: bar 932 `) 933 934 func TestMergeConfig(t *testing.T) { 935 v := New() 936 v.SetConfigType("yml") 937 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 938 t.Fatal(err) 939 } 940 941 if pop := v.GetInt("hello.pop"); pop != 37890 { 942 t.Fatalf("pop != 37890, = %d", pop) 943 } 944 945 if pop := v.GetInt("hello.lagrenum"); pop != 765432101234567 { 946 t.Fatalf("lagrenum != 765432101234567, = %d", pop) 947 } 948 949 if pop := v.GetInt32("hello.pop"); pop != int32(37890) { 950 t.Fatalf("pop != 37890, = %d", pop) 951 } 952 953 if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) { 954 t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop) 955 } 956 957 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 958 t.Fatalf("len(world) != 4, = %d", len(world)) 959 } 960 961 if fu := v.GetString("fu"); fu != "" { 962 t.Fatalf("fu != \"\", = %s", fu) 963 } 964 965 if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 966 t.Fatal(err) 967 } 968 969 if pop := v.GetInt("hello.pop"); pop != 45000 { 970 t.Fatalf("pop != 45000, = %d", pop) 971 } 972 973 if pop := v.GetInt("hello.lagrenum"); pop != 7654321001234567 { 974 t.Fatalf("lagrenum != 7654321001234567, = %d", pop) 975 } 976 977 if pop := v.GetInt32("hello.pop"); pop != int32(45000) { 978 t.Fatalf("pop != 45000, = %d", pop) 979 } 980 981 if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) { 982 t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop) 983 } 984 985 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 986 t.Fatalf("len(world) != 4, = %d", len(world)) 987 } 988 989 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 990 t.Fatalf("len(universe) != 2, = %d", len(universe)) 991 } 992 993 if fu := v.GetString("fu"); fu != "bar" { 994 t.Fatalf("fu != \"bar\", = %s", fu) 995 } 996 } 997 998 func TestMergeConfigNoMerge(t *testing.T) { 999 v := New() 1000 v.SetConfigType("yml") 1001 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1002 t.Fatal(err) 1003 } 1004 1005 if pop := v.GetInt("hello.pop"); pop != 37890 { 1006 t.Fatalf("pop != 37890, = %d", pop) 1007 } 1008 1009 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1010 t.Fatalf("len(world) != 4, = %d", len(world)) 1011 } 1012 1013 if fu := v.GetString("fu"); fu != "" { 1014 t.Fatalf("fu != \"\", = %s", fu) 1015 } 1016 1017 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 1018 t.Fatal(err) 1019 } 1020 1021 if pop := v.GetInt("hello.pop"); pop != 45000 { 1022 t.Fatalf("pop != 45000, = %d", pop) 1023 } 1024 1025 if world := v.GetStringSlice("hello.world"); len(world) != 0 { 1026 t.Fatalf("len(world) != 0, = %d", len(world)) 1027 } 1028 1029 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 1030 t.Fatalf("len(universe) != 2, = %d", len(universe)) 1031 } 1032 1033 if fu := v.GetString("fu"); fu != "bar" { 1034 t.Fatalf("fu != \"bar\", = %s", fu) 1035 } 1036 } 1037 1038 func TestUnmarshalingWithAliases(t *testing.T) { 1039 v := New() 1040 v.SetDefault("ID", 1) 1041 v.Set("name", "Steve") 1042 v.Set("lastname", "Owen") 1043 1044 v.RegisterAlias("UserID", "ID") 1045 v.RegisterAlias("Firstname", "name") 1046 v.RegisterAlias("Surname", "lastname") 1047 1048 type config struct { 1049 ID int 1050 FirstName string 1051 Surname string 1052 } 1053 1054 var C config 1055 err := v.Unmarshal(&C) 1056 if err != nil { 1057 t.Fatalf("unable to decode into struct, %v", err) 1058 } 1059 1060 assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C) 1061 } 1062 1063 func TestSetConfigNameClearsFileCache(t *testing.T) { 1064 SetConfigFile("/tmp/config.yaml") 1065 SetConfigName("default") 1066 f, err := v.getConfigFile() 1067 if err == nil { 1068 t.Fatalf("config file cache should have been cleared") 1069 } 1070 assert.Empty(t, f) 1071 } 1072 1073 func TestShadowedNestedValue(t *testing.T) { 1074 1075 config := `name: steve 1076 clothing: 1077 jacket: leather 1078 trousers: denim 1079 pants: 1080 size: large 1081 ` 1082 initConfig("yaml", config) 1083 1084 assert.Equal(t, "steve", GetString("name")) 1085 1086 polyester := "polyester" 1087 SetDefault("clothing.shirt", polyester) 1088 SetDefault("clothing.jacket.price", 100) 1089 1090 assert.Equal(t, "leather", GetString("clothing.jacket")) 1091 assert.Nil(t, Get("clothing.jacket.price")) 1092 assert.Equal(t, polyester, GetString("clothing.shirt")) 1093 1094 clothingSettings := AllSettings()["clothing"].(map[string]interface{}) 1095 assert.Equal(t, "leather", clothingSettings["jacket"]) 1096 assert.Equal(t, polyester, clothingSettings["shirt"]) 1097 } 1098 1099 func TestDotParameter(t *testing.T) { 1100 initJSON() 1101 // shoud take precedence over batters defined in jsonExample 1102 r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`)) 1103 unmarshalReader(r, v.config) 1104 1105 actual := Get("batters.batter") 1106 expected := []interface{}{map[string]interface{}{"type": "Small"}} 1107 assert.Equal(t, expected, actual) 1108 } 1109 1110 func TestCaseInsensitive(t *testing.T) { 1111 for _, config := range []struct { 1112 typ string 1113 content string 1114 }{ 1115 {"yaml", ` 1116 aBcD: 1 1117 eF: 1118 gH: 2 1119 iJk: 3 1120 Lm: 1121 nO: 4 1122 P: 1123 Q: 5 1124 R: 6 1125 `}, 1126 {"json", `{ 1127 "aBcD": 1, 1128 "eF": { 1129 "iJk": 3, 1130 "Lm": { 1131 "P": { 1132 "Q": 5, 1133 "R": 6 1134 }, 1135 "nO": 4 1136 }, 1137 "gH": 2 1138 } 1139 }`}, 1140 {"toml", `aBcD = 1 1141 [eF] 1142 gH = 2 1143 iJk = 3 1144 [eF.Lm] 1145 nO = 4 1146 [eF.Lm.P] 1147 Q = 5 1148 R = 6 1149 `}, 1150 } { 1151 doTestCaseInsensitive(t, config.typ, config.content) 1152 } 1153 } 1154 1155 func TestCaseInsensitiveSet(t *testing.T) { 1156 Reset() 1157 m1 := map[string]interface{}{ 1158 "Foo": 32, 1159 "Bar": map[interface{}]interface { 1160 }{ 1161 "ABc": "A", 1162 "cDE": "B"}, 1163 } 1164 1165 m2 := map[string]interface{}{ 1166 "Foo": 52, 1167 "Bar": map[interface{}]interface { 1168 }{ 1169 "bCd": "A", 1170 "eFG": "B"}, 1171 } 1172 1173 Set("Given1", m1) 1174 Set("Number1", 42) 1175 1176 SetDefault("Given2", m2) 1177 SetDefault("Number2", 52) 1178 1179 // Verify SetDefault 1180 if v := Get("number2"); v != 52 { 1181 t.Fatalf("Expected 52 got %q", v) 1182 } 1183 1184 if v := Get("given2.foo"); v != 52 { 1185 t.Fatalf("Expected 52 got %q", v) 1186 } 1187 1188 if v := Get("given2.bar.bcd"); v != "A" { 1189 t.Fatalf("Expected A got %q", v) 1190 } 1191 1192 if _, ok := m2["Foo"]; !ok { 1193 t.Fatal("Input map changed") 1194 } 1195 1196 // Verify Set 1197 if v := Get("number1"); v != 42 { 1198 t.Fatalf("Expected 42 got %q", v) 1199 } 1200 1201 if v := Get("given1.foo"); v != 32 { 1202 t.Fatalf("Expected 32 got %q", v) 1203 } 1204 1205 if v := Get("given1.bar.abc"); v != "A" { 1206 t.Fatalf("Expected A got %q", v) 1207 } 1208 1209 if _, ok := m1["Foo"]; !ok { 1210 t.Fatal("Input map changed") 1211 } 1212 } 1213 1214 func TestParseNested(t *testing.T) { 1215 type duration struct { 1216 Delay time.Duration 1217 } 1218 1219 type item struct { 1220 Name string 1221 Delay time.Duration 1222 Nested duration 1223 } 1224 1225 config := `[[parent]] 1226 delay="100ms" 1227 [parent.nested] 1228 delay="200ms" 1229 ` 1230 initConfig("toml", config) 1231 1232 var items []item 1233 err := v.UnmarshalKey("parent", &items) 1234 if err != nil { 1235 t.Fatalf("unable to decode into struct, %v", err) 1236 } 1237 1238 assert.Equal(t, 1, len(items)) 1239 assert.Equal(t, 100*time.Millisecond, items[0].Delay) 1240 assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay) 1241 } 1242 1243 func doTestCaseInsensitive(t *testing.T, typ, config string) { 1244 initConfig(typ, config) 1245 Set("RfD", true) 1246 assert.Equal(t, true, Get("rfd")) 1247 assert.Equal(t, true, Get("rFD")) 1248 assert.Equal(t, 1, cast.ToInt(Get("abcd"))) 1249 assert.Equal(t, 1, cast.ToInt(Get("Abcd"))) 1250 assert.Equal(t, 2, cast.ToInt(Get("ef.gh"))) 1251 assert.Equal(t, 3, cast.ToInt(Get("ef.ijk"))) 1252 assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no"))) 1253 assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q"))) 1254 1255 } 1256 1257 func BenchmarkGetBool(b *testing.B) { 1258 key := "BenchmarkGetBool" 1259 v = New() 1260 v.Set(key, true) 1261 1262 for i := 0; i < b.N; i++ { 1263 if !v.GetBool(key) { 1264 b.Fatal("GetBool returned false") 1265 } 1266 } 1267 } 1268 1269 func BenchmarkGet(b *testing.B) { 1270 key := "BenchmarkGet" 1271 v = New() 1272 v.Set(key, true) 1273 1274 for i := 0; i < b.N; i++ { 1275 if !v.Get(key).(bool) { 1276 b.Fatal("Get returned false") 1277 } 1278 } 1279 } 1280 1281 // This is the "perfect result" for the above. 1282 func BenchmarkGetBoolFromMap(b *testing.B) { 1283 m := make(map[string]bool) 1284 key := "BenchmarkGetBool" 1285 m[key] = true 1286 1287 for i := 0; i < b.N; i++ { 1288 if !m[key] { 1289 b.Fatal("Map value was false") 1290 } 1291 } 1292 }