github.com/dvln/viper@v0.0.0-20161024040611-d5f329914da8/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/dvln/cast" 22 "github.com/dvln/out" 23 "github.com/dvln/pflag" 24 "github.com/dvln/pretty" 25 "github.com/dvln/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 hclExample = []byte(` 76 id = "0001" 77 type = "donut" 78 name = "Cake" 79 ppu = 0.55 80 foos { 81 foo { 82 key = 1 83 } 84 foo { 85 key = 2 86 } 87 foo { 88 key = 3 89 } 90 foo { 91 key = 4 92 } 93 }`) 94 95 var propertiesExample = []byte(` 96 p_id: 0001 97 p_type: donut 98 p_name: Cake 99 p_ppu: 0.55 100 p_batters.batter.type: Regular 101 `) 102 103 var remoteExample = []byte(`{ 104 "id":"0002", 105 "type":"cronut", 106 "newkey":"remote" 107 }`) 108 109 func initConfigs() { 110 Reset() 111 var r io.Reader 112 SetConfigType("yaml") 113 r = bytes.NewReader(yamlExample) 114 unmarshalReader(r, v.config) 115 116 SetConfigType("json") 117 r = bytes.NewReader(jsonExample) 118 unmarshalReader(r, v.config) 119 120 SetConfigType("hcl") 121 r = bytes.NewReader(hclExample) 122 unmarshalReader(r, v.config) 123 124 SetConfigType("properties") 125 r = bytes.NewReader(propertiesExample) 126 unmarshalReader(r, v.config) 127 128 SetConfigType("toml") 129 r = bytes.NewReader(tomlExample) 130 unmarshalReader(r, v.config) 131 132 SetConfigType("json") 133 remote := bytes.NewReader(remoteExample) 134 unmarshalReader(remote, v.kvstore) 135 } 136 137 func initConfig(typ, config string) { 138 Reset() 139 SetConfigType(typ) 140 r := strings.NewReader(config) 141 142 if err := unmarshalReader(r, v.config); err != nil { 143 panic(err) 144 } 145 } 146 147 func initYAML() { 148 initConfig("yaml", string(yamlExample)) 149 } 150 151 func initJSON() { 152 Reset() 153 SetConfigType("json") 154 r := bytes.NewReader(jsonExample) 155 156 unmarshalReader(r, v.config) 157 } 158 159 func initProperties() { 160 Reset() 161 SetConfigType("properties") 162 r := bytes.NewReader(propertiesExample) 163 164 unmarshalReader(r, v.config) 165 } 166 167 func initTOML() { 168 Reset() 169 SetConfigType("toml") 170 r := bytes.NewReader(tomlExample) 171 172 unmarshalReader(r, v.config) 173 } 174 175 func initHcl() { 176 Reset() 177 SetConfigType("hcl") 178 r := bytes.NewReader(hclExample) 179 180 unmarshalReader(r, v.config) 181 } 182 183 // make directories for testing 184 func initDirs(t *testing.T) (string, string, func()) { 185 186 var ( 187 testDirs = []string{`a a`, `b`, `c\c`, `D_`} 188 config = `improbable` 189 ) 190 191 root, err := ioutil.TempDir("", "") 192 193 cleanup := true 194 defer func() { 195 if cleanup { 196 os.Chdir("..") 197 os.RemoveAll(root) 198 } 199 }() 200 201 assert.Nil(t, err) 202 203 err = os.Chdir(root) 204 assert.Nil(t, err) 205 206 for _, dir := range testDirs { 207 err = os.Mkdir(dir, 0750) 208 assert.Nil(t, err) 209 210 err = ioutil.WriteFile( 211 path.Join(dir, config+".toml"), 212 []byte("key = \"value is "+dir+"\"\n"), 213 0640) 214 assert.Nil(t, err) 215 } 216 217 cleanup = false 218 return root, config, func() { 219 os.Chdir("..") 220 os.RemoveAll(root) 221 } 222 } 223 224 //stubs for PFlag Values 225 type stringValue string 226 227 func newStringValue(val string, p *string) *stringValue { 228 *p = val 229 return (*stringValue)(p) 230 } 231 232 func (s *stringValue) Set(val string) error { 233 *s = stringValue(val) 234 return nil 235 } 236 237 func (s *stringValue) Type() string { 238 return "string" 239 } 240 241 func (s *stringValue) String() string { 242 return fmt.Sprintf("%s", *s) 243 } 244 245 func TestBasics(t *testing.T) { 246 SetConfigFile("/tmp/config.yaml") 247 filename, err := v.getConfigFile() 248 assert.Equal(t, "/tmp/config.yaml", filename) 249 assert.NoError(t, err) 250 } 251 252 func TestDefault(t *testing.T) { 253 SetDefault("age", 45) 254 assert.Equal(t, 45, Get("age")) 255 256 SetDefault("clothing.jacket", "slacks") 257 assert.Equal(t, "slacks", Get("clothing.jacket")) 258 259 SetConfigType("yaml") 260 err := ReadConfig(bytes.NewBuffer(yamlExample)) 261 262 assert.NoError(t, err) 263 assert.Equal(t, "leather", Get("clothing.jacket")) 264 } 265 266 func TestUnmarshalling(t *testing.T) { 267 SetConfigType("yaml") 268 r := bytes.NewReader(yamlExample) 269 270 unmarshalReader(r, v.config) 271 assert.True(t, InConfig("name")) 272 assert.False(t, InConfig("state")) 273 assert.Equal(t, "steve", Get("name")) 274 assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies")) 275 assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing")) 276 assert.Equal(t, 35, Get("age")) 277 } 278 279 func TestUnmarshalExact(t *testing.T) { 280 vip := New() 281 target := &testUnmarshalExtra{} 282 vip.SetConfigType("yaml") 283 r := bytes.NewReader(yamlExampleWithExtras) 284 vip.ReadConfig(r) 285 err := vip.UnmarshalExact(target) 286 if err == nil { 287 t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields") 288 } 289 } 290 291 func TestOverrides(t *testing.T) { 292 Set("age", 40) 293 assert.Equal(t, 40, Get("age")) 294 } 295 296 func TestDefaultPost(t *testing.T) { 297 assert.NotEqual(t, "NYC", Get("state")) 298 SetDefault("state", "NYC") 299 assert.Equal(t, "NYC", Get("state")) 300 } 301 302 func TestAliases(t *testing.T) { 303 RegisterAlias("years", "age") 304 assert.Equal(t, 40, Get("years")) 305 Set("years", 45) 306 assert.Equal(t, 45, Get("age")) 307 } 308 309 func TestAliasInConfigFile(t *testing.T) { 310 // the config file specifies "beard". If we make this an alias for 311 // "hasbeard", we still want the old config file to work with beard. 312 RegisterAlias("beard", "hasbeard") 313 assert.Equal(t, true, Get("hasbeard")) 314 Set("hasbeard", false) 315 assert.Equal(t, false, Get("beard")) 316 } 317 318 func TestYML(t *testing.T) { 319 initYAML() 320 assert.Equal(t, "steve", Get("name")) 321 } 322 323 func TestJSON(t *testing.T) { 324 initJSON() 325 assert.Equal(t, "0001", Get("id")) 326 } 327 328 func TestProperties(t *testing.T) { 329 initProperties() 330 assert.Equal(t, "0001", Get("p_id")) 331 } 332 333 func TestTOML(t *testing.T) { 334 initTOML() 335 assert.Equal(t, "TOML Example", Get("title")) 336 } 337 338 func TestHCL(t *testing.T) { 339 initHcl() 340 assert.Equal(t, "0001", Get("id")) 341 assert.Equal(t, 0.55, Get("ppu")) 342 assert.Equal(t, "donut", Get("type")) 343 assert.Equal(t, "Cake", Get("name")) 344 Set("id", "0002") 345 assert.Equal(t, "0002", Get("id")) 346 assert.NotEqual(t, "cronut", Get("type")) 347 } 348 349 func TestRemotePrecedence(t *testing.T) { 350 initJSON() 351 352 remote := bytes.NewReader(remoteExample) 353 assert.Equal(t, "0001", Get("id")) 354 unmarshalReader(remote, v.kvstore) 355 assert.Equal(t, "0001", Get("id")) 356 assert.NotEqual(t, "cronut", Get("type")) 357 assert.Equal(t, "remote", Get("newkey")) 358 Set("newkey", "newvalue") 359 assert.NotEqual(t, "remote", Get("newkey")) 360 assert.Equal(t, "newvalue", Get("newkey")) 361 Set("newkey", "remote") 362 } 363 364 func TestEnv(t *testing.T) { 365 initJSON() 366 367 BindEnv("id") 368 BindEnv("f", "FOOD") 369 370 os.Setenv("ID", "13") 371 os.Setenv("FOOD", "apple") 372 os.Setenv("NAME", "crunk") 373 374 assert.Equal(t, "13", Get("id")) 375 assert.Equal(t, "apple", Get("f")) 376 assert.Equal(t, "Cake", Get("name")) 377 378 AutomaticEnv() 379 380 assert.Equal(t, "crunk", Get("name")) 381 382 } 383 384 func TestEnvPrefix(t *testing.T) { 385 initJSON() 386 387 SetEnvPrefix("foo") // will be uppercased automatically 388 BindEnv("id") 389 BindEnv("f", "FOOD") // not using prefix 390 391 os.Setenv("FOO_ID", "13") 392 os.Setenv("FOOD", "apple") 393 os.Setenv("FOO_NAME", "crunk") 394 395 assert.Equal(t, "13", Get("id")) 396 assert.Equal(t, "apple", Get("f")) 397 assert.Equal(t, "Cake", Get("name")) 398 399 AutomaticEnv() 400 401 assert.Equal(t, "crunk", Get("name")) 402 } 403 404 func TestAutoEnv(t *testing.T) { 405 Reset() 406 407 AutomaticEnv() 408 os.Setenv("FOO_BAR", "13") 409 assert.Equal(t, "13", Get("foo_bar")) 410 } 411 412 func TestAutoEnvWithPrefix(t *testing.T) { 413 Reset() 414 415 AutomaticEnv() 416 SetEnvPrefix("Baz") 417 os.Setenv("BAZ_BAR", "13") 418 assert.Equal(t, "13", Get("bar")) 419 } 420 421 func TestSetEnvReplacer(t *testing.T) { 422 Reset() 423 424 AutomaticEnv() 425 os.Setenv("REFRESH_INTERVAL", "30s") 426 427 replacer := strings.NewReplacer("-", "_") 428 SetEnvKeyReplacer(replacer) 429 430 assert.Equal(t, "30s", Get("refresh-interval")) 431 } 432 433 func TestAllKeys(t *testing.T) { 434 initConfigs() 435 436 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"} 437 dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 438 //FIXME: remove if fixed: 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[interface{}]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[interface{}]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.batter.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}}}}} 439 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}}}}} 440 441 var allkeys sort.StringSlice 442 allkeys = AllKeys() 443 allkeys.Sort() 444 ks.Sort() 445 446 assert.Equal(t, ks, allkeys) 447 assert.Equal(t, all, AllSettings()) 448 } 449 450 func TestAllKeysWithEnv(t *testing.T) { 451 v := New() 452 453 // bind and define environment variables (including a nested one) 454 v.BindEnv("id") 455 v.BindEnv("foo.bar") 456 v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) 457 os.Setenv("ID", "13") 458 os.Setenv("FOO_BAR", "baz") 459 460 expectedKeys := sort.StringSlice{"id", "foo.bar"} 461 expectedKeys.Sort() 462 keys := sort.StringSlice(v.AllKeys()) 463 keys.Sort() 464 assert.Equal(t, expectedKeys, keys) 465 } 466 467 func TestAliasesOfAliases(t *testing.T) { 468 Set("Title", "Checking Case") 469 RegisterAlias("Foo", "Bar") 470 RegisterAlias("Bar", "Title") 471 assert.Equal(t, "Checking Case", Get("FOO")) 472 } 473 474 func TestRecursiveAliases(t *testing.T) { 475 // Stash existing 'out' package screen threshold and screen writer and 476 // adjust it so we're only writing ERROR's and below and that we send 477 // those to a buffer and not the screen: 478 oldScreenThreshold := out.Threshold(out.ForScreen) 479 oldScreenWriter := out.Writer(out.LevelAll, out.ForScreen) 480 screenBuf := new(bytes.Buffer) 481 out.SetThreshold(out.LevelError, out.ForScreen) 482 out.SetWriter(out.LevelAll, screenBuf, out.ForScreen) 483 484 // Now set up a recursive alias, should produce an Error: 485 RegisterAlias("Baz", "Roo") 486 RegisterAlias("Roo", "baz") 487 assert.Contains(t, screenBuf.String(), "Error: Creating circular reference alias:") 488 489 // Reset the screen output threshold and screen writer for remaining 490 // tests in case they are needed 491 out.SetThreshold(oldScreenThreshold, out.ForScreen) 492 out.SetWriter(out.LevelAll, oldScreenWriter, out.ForScreen) 493 } 494 495 func TestUnmarshal(t *testing.T) { 496 SetDefault("port", 1313) 497 Set("name", "Steve") 498 Set("duration", "1s1ms") 499 500 type config struct { 501 Port int 502 Name string 503 Duration time.Duration 504 } 505 506 var C config 507 508 err := Unmarshal(&C) 509 if err != nil { 510 t.Fatalf("unable to decode into struct, %v", err) 511 } 512 513 assert.Equal(t, &config{Name: "Steve", Port: 1313, Duration: time.Second + time.Millisecond}, &C) 514 515 Set("port", 1234) 516 err = Unmarshal(&C) 517 if err != nil { 518 t.Fatalf("unable to decode into struct, %v", err) 519 } 520 assert.Equal(t, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond}, &C) 521 } 522 523 func TestBindPFlags(t *testing.T) { 524 v := New() // create independent Viper object 525 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) 526 527 var testValues = map[string]*string{ 528 "host": nil, 529 "port": nil, 530 "endpoint": nil, 531 } 532 533 var mutatedTestValues = map[string]string{ 534 "host": "localhost", 535 "port": "6060", 536 "endpoint": "/public", 537 } 538 539 for name := range testValues { 540 testValues[name] = flagSet.String(name, "", "test") 541 } 542 543 err := v.BindPFlags(flagSet) 544 if err != nil { 545 t.Fatalf("error binding flag set, %v", err) 546 } 547 548 flagSet.VisitAll(func(flag *pflag.Flag) { 549 flag.Value.Set(mutatedTestValues[flag.Name]) 550 flag.Changed = true 551 }) 552 553 for name, expected := range mutatedTestValues { 554 assert.Equal(t, expected, v.Get(name)) 555 } 556 557 } 558 559 func TestBindPFlag(t *testing.T) { 560 var testString = "testing" 561 var testValue = newStringValue(testString, &testString) 562 563 flag := &pflag.Flag{ 564 Name: "testflag", 565 Value: testValue, 566 Changed: false, 567 } 568 569 BindPFlag("testvalue", flag) 570 571 assert.Equal(t, testString, Get("testvalue")) 572 573 flag.Value.Set("testing_mutate") 574 flag.Changed = true //hack for pflag usage 575 576 assert.Equal(t, "testing_mutate", Get("testvalue")) 577 578 } 579 580 func TestBoundCaseSensitivity(t *testing.T) { 581 assert.Equal(t, "brown", Get("eyes")) 582 583 BindEnv("eYEs", "TURTLE_EYES") 584 os.Setenv("TURTLE_EYES", "blue") 585 586 assert.Equal(t, "blue", Get("eyes")) 587 588 var testString = "green" 589 var testValue = newStringValue(testString, &testString) 590 591 flag := &pflag.Flag{ 592 Name: "eyeballs", 593 Value: testValue, 594 Changed: true, 595 } 596 597 BindPFlag("eYEs", flag) 598 assert.Equal(t, "green", Get("eyes")) 599 600 } 601 602 func TestDescriptionData(t *testing.T) { 603 // On a key we know exists, add description info 604 SetDesc("eyes", "eye color", NoviceUser, BasicGlobal) 605 // Grab the description info for that key 606 str, useLvl, useScope := Desc("eyes") 607 // Verify the results are what we just set 608 assert.Equal(t, "eye color", str) 609 assert.Equal(t, NoviceUser, useLvl) 610 assert.Equal(t, useScope, BasicGlobal) 611 612 // Double check the useLevel String() method and it's reverse 613 useLvlStr := fmt.Sprintf("%s", useLvl) 614 assert.Equal(t, useLvlStr, "NOVICE") 615 newUseLvl := UseLevelString2UseLevel(useLvlStr) 616 assert.Equal(t, useLvl, newUseLvl) 617 618 // Try it an unknown/unset key, should return these values 619 str, useLvl, useScope = Desc("bogusEyes") 620 assert.Equal(t, "", str) 621 assert.Equal(t, useLvl, UnknownUseLevel) 622 assert.Equal(t, useScope, 0) 623 } 624 625 func TestViperTextPrint(t *testing.T) { 626 initConfigs() 627 // Add in some variables needed for pretty print String() method 628 Set("verbose", true) 629 Set("look", "text") 630 Set("globs", "cfg") 631 632 // Tweak the eyes setting a bit, adding full description as well 633 Set("eyes", "blue") 634 SetDesc("eyes", "eye color", NoviceUser, CLIGlobal) 635 636 // Make sure our output uses the pretty format 637 pretty.SetHumanize(true) 638 myV := GetSingleton() 639 output := fmt.Sprintf("%s", myV) 640 pretty.SetHumanize(false) 641 // See if we get what we expect 642 assert.Contains(t, output, "eyes:") 643 assert.Contains(t, output, "Description:") 644 assert.Contains(t, output, "eye color\n") 645 assert.Contains(t, output, "Use Level:") 646 assert.Contains(t, output, "NOVICE\n") 647 assert.Contains(t, output, "Value:") 648 assert.Contains(t, output, "blue\n") 649 // Verify the humanized output is coming out ok 650 assert.NotContains(t, output, "interface") 651 } 652 653 func TestViperJSONPrint(t *testing.T) { 654 initConfigs() 655 // Tweak config with a few expected "print" control variables: 656 Set("verbose", true) 657 Set("look", "json") 658 Set("globs", "env") 659 Set("apiver", "0.1") 660 661 // Lets see eyes to blue for grins and add a description 662 Set("eyes", "blue") 663 // Note that SetDesc does the BindEnv for you if the visibility is to 664 // the environment (ie: CLIGlobal will make it available to the env via 665 // a BindKey call within SetDesc() here for the "eyes" key) 666 SetDesc("eyes", "eye color", NoviceUser, CLIGlobal) 667 myV := GetSingleton() 668 output := fmt.Sprintf("%s", myV) 669 pretty.SetHumanize(false) 670 assert.Contains(t, output, "\"description\": \"eye color\"") 671 assert.Contains(t, output, "\"useLevel\": \"NOVICE\"") 672 assert.Contains(t, output, "\"value\": \"blue\"") 673 assert.Contains(t, output, "\"EYES\":") 674 assert.Contains(t, output, "\"apiVersion\": \"0.1\",") 675 } 676 677 func TestSizeInBytes(t *testing.T) { 678 input := map[string]uint{ 679 "": 0, 680 "b": 0, 681 "12 bytes": 0, 682 "200000000000gb": 0, 683 "12 b": 12, 684 "43 MB": 43 * (1 << 20), 685 "10mb": 10 * (1 << 20), 686 "1gb": 1 << 30, 687 } 688 689 for str, expected := range input { 690 assert.Equal(t, expected, parseSizeInBytes(str), str) 691 } 692 } 693 694 func TestFindsNestedKeys(t *testing.T) { 695 initConfigs() 696 dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 697 698 Set("super", map[string]interface{}{ 699 "deep": map[string]interface{}{ 700 "nested": "value", 701 }, 702 }) 703 704 expected := map[string]interface{}{ 705 "super": map[string]interface{}{ 706 "deep": map[string]interface{}{ 707 "nested": "value", 708 }, 709 }, 710 "super.deep": map[string]interface{}{ 711 "nested": "value", 712 }, 713 "super.deep.nested": "value", 714 "owner.organization": "MongoDB", 715 "batters.batter": []interface{}{ 716 map[string]interface{}{ 717 "type": "Regular", 718 }, 719 map[string]interface{}{ 720 "type": "Chocolate", 721 }, 722 map[string]interface{}{ 723 "type": "Blueberry", 724 }, 725 map[string]interface{}{ 726 "type": "Devil's Food", 727 }, 728 }, 729 "hobbies": []interface{}{ 730 "skateboarding", "snowboarding", "go", 731 }, 732 "title": "TOML Example", 733 "newkey": "remote", 734 "batters": map[string]interface{}{ 735 "batter": []interface{}{ 736 map[string]interface{}{ 737 "type": "Regular", 738 }, 739 map[string]interface{}{ 740 "type": "Chocolate", 741 }, map[string]interface{}{ 742 "type": "Blueberry", 743 }, map[string]interface{}{ 744 "type": "Devil's Food", 745 }, 746 }, 747 }, 748 "eyes": "brown", 749 "age": 35, 750 "owner": map[string]interface{}{ 751 "organization": "MongoDB", 752 "bio": "MongoDB Chief Developer Advocate & Hacker at Large", 753 "dob": dob, 754 }, 755 "owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large", 756 "type": "donut", 757 "id": "0001", 758 "name": "Cake", 759 "hacker": true, 760 "ppu": 0.55, 761 "clothing": map[string]interface{}{ 762 "jacket": "leather", 763 "trousers": "denim", 764 "pants": map[string]interface{}{ 765 "size": "large", 766 }, 767 }, 768 "clothing.jacket": "leather", 769 "clothing.pants.size": "large", 770 "clothing.trousers": "denim", 771 "owner.dob": dob, 772 "beard": true, 773 "foos": []map[string]interface{}{ 774 map[string]interface{}{ 775 "foo": []map[string]interface{}{ 776 map[string]interface{}{ 777 "key": 1, 778 }, 779 map[string]interface{}{ 780 "key": 2, 781 }, 782 map[string]interface{}{ 783 "key": 3, 784 }, 785 map[string]interface{}{ 786 "key": 4, 787 }, 788 }, 789 }, 790 }, 791 } 792 793 for key, expectedValue := range expected { 794 795 assert.Equal(t, expectedValue, v.Get(key)) 796 } 797 798 } 799 800 func TestReadBufConfig(t *testing.T) { 801 v := New() 802 v.SetConfigType("yaml") 803 v.ReadConfig(bytes.NewBuffer(yamlExample)) 804 805 assert.True(t, v.InConfig("name")) 806 assert.False(t, v.InConfig("state")) 807 assert.Equal(t, "steve", v.Get("name")) 808 assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies")) 809 assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing")) 810 assert.Equal(t, 35, v.Get("age")) 811 } 812 813 func TestIsSet(t *testing.T) { 814 v := New() 815 v.SetConfigType("yaml") 816 v.ReadConfig(bytes.NewBuffer(yamlExample)) 817 assert.True(t, v.IsSet("clothing.jacket")) 818 assert.False(t, v.IsSet("clothing.jackets")) 819 assert.False(t, v.IsSet("helloworld")) 820 v.Set("helloworld", "fubar") 821 assert.True(t, v.IsSet("helloworld")) 822 } 823 824 func TestDirsSearch(t *testing.T) { 825 826 root, config, cleanup := initDirs(t) 827 defer cleanup() 828 829 v := New() 830 v.SetConfigName(config) 831 v.SetDefault(`key`, `default`) 832 833 entries, err := ioutil.ReadDir(root) 834 for _, e := range entries { 835 if e.IsDir() { 836 v.AddConfigPath(e.Name()) 837 } 838 } 839 840 err = v.ReadInConfig() 841 assert.Nil(t, err) 842 843 assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`)) 844 } 845 846 func TestWrongDirsSearchNotFound(t *testing.T) { 847 848 _, config, cleanup := initDirs(t) 849 defer cleanup() 850 851 v := New() 852 v.SetConfigName(config) 853 v.SetDefault(`key`, `default`) 854 855 v.AddConfigPath(`whattayoutalkingbout`) 856 v.AddConfigPath(`thispathaintthere`) 857 858 err := v.ReadInConfig() 859 assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) 860 861 // Even though config did not load and the error might have 862 // been ignored by the client, the default still loads 863 assert.Equal(t, `default`, v.GetString(`key`)) 864 } 865 866 func TestSub(t *testing.T) { 867 v := New() 868 v.SetConfigType("yaml") 869 v.ReadConfig(bytes.NewBuffer(yamlExample)) 870 871 subv := v.Sub("clothing") 872 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size")) 873 874 subv = v.Sub("clothing.pants") 875 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size")) 876 877 subv = v.Sub("clothing.pants.size") 878 assert.Equal(t, (*Viper)(nil), subv) 879 880 subv = v.Sub("missing.key") 881 assert.Equal(t, (*Viper)(nil), subv) 882 } 883 884 var yamlMergeExampleTgt = []byte(` 885 hello: 886 pop: 37890 887 lagrenum: 765432101234567 888 world: 889 - us 890 - uk 891 - fr 892 - de 893 `) 894 895 var yamlMergeExampleSrc = []byte(` 896 hello: 897 pop: 45000 898 lagrenum: 7654321001234567 899 universe: 900 - mw 901 - ad 902 fu: bar 903 `) 904 905 func TestMergeConfig(t *testing.T) { 906 v := New() 907 v.SetConfigType("yml") 908 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 909 t.Fatal(err) 910 } 911 912 if pop := v.GetInt("hello.pop"); pop != 37890 { 913 t.Fatalf("pop != 37890, = %d", pop) 914 } 915 916 if pop := v.GetInt("hello.lagrenum"); pop != 765432101234567 { 917 t.Fatalf("lagrenum != 765432101234567, = %d", pop) 918 } 919 920 if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) { 921 t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop) 922 } 923 924 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 925 t.Fatalf("len(world) != 4, = %d", len(world)) 926 } 927 928 if fu := v.GetString("fu"); fu != "" { 929 t.Fatalf("fu != \"\", = %s", fu) 930 } 931 932 if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 933 t.Fatal(err) 934 } 935 936 if pop := v.GetInt("hello.pop"); pop != 45000 { 937 t.Fatalf("pop != 45000, = %d", pop) 938 } 939 940 if pop := v.GetInt("hello.lagrenum"); pop != 7654321001234567 { 941 t.Fatalf("lagrenum != 7654321001234567, = %d", pop) 942 } 943 944 if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) { 945 t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop) 946 } 947 948 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 949 t.Fatalf("len(world) != 4, = %d", len(world)) 950 } 951 952 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 953 t.Fatalf("len(universe) != 2, = %d", len(universe)) 954 } 955 956 if fu := v.GetString("fu"); fu != "bar" { 957 t.Fatalf("fu != \"bar\", = %s", fu) 958 } 959 } 960 961 func TestMergeConfigNoMerge(t *testing.T) { 962 v := New() 963 v.SetConfigType("yml") 964 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 965 t.Fatal(err) 966 } 967 968 if pop := v.GetInt("hello.pop"); pop != 37890 { 969 t.Fatalf("pop != 37890, = %d", pop) 970 } 971 972 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 973 t.Fatalf("len(world) != 4, = %d", len(world)) 974 } 975 976 if fu := v.GetString("fu"); fu != "" { 977 t.Fatalf("fu != \"\", = %s", fu) 978 } 979 980 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 981 t.Fatal(err) 982 } 983 984 if pop := v.GetInt("hello.pop"); pop != 45000 { 985 t.Fatalf("pop != 45000, = %d", pop) 986 } 987 988 if world := v.GetStringSlice("hello.world"); len(world) != 0 { 989 t.Fatalf("len(world) != 0, = %d", len(world)) 990 } 991 992 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 993 t.Fatalf("len(universe) != 2, = %d", len(universe)) 994 } 995 996 if fu := v.GetString("fu"); fu != "bar" { 997 t.Fatalf("fu != \"bar\", = %s", fu) 998 } 999 } 1000 1001 func TestUnmarshalingWithAliases(t *testing.T) { 1002 v := New() 1003 v.SetDefault("ID", 1) 1004 v.Set("name", "Steve") 1005 v.Set("lastname", "Owen") 1006 1007 v.RegisterAlias("UserID", "ID") 1008 v.RegisterAlias("Firstname", "name") 1009 v.RegisterAlias("Surname", "lastname") 1010 1011 type config struct { 1012 ID int 1013 FirstName string 1014 Surname string 1015 } 1016 1017 var C config 1018 err := v.Unmarshal(&C) 1019 if err != nil { 1020 t.Fatalf("unable to decode into struct, %v", err) 1021 } 1022 1023 assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C) 1024 } 1025 1026 func TestSetConfigNameClearsFileCache(t *testing.T) { 1027 SetConfigFile("/tmp/config.yaml") 1028 SetConfigName("default") 1029 f, err := v.getConfigFile() 1030 if err == nil { 1031 t.Fatalf("config file cache should have been cleared") 1032 } 1033 assert.Empty(t, f) 1034 } 1035 1036 func TestShadowedNestedValue(t *testing.T) { 1037 1038 config := `name: steve 1039 clothing: 1040 jacket: leather 1041 trousers: denim 1042 pants: 1043 size: large 1044 ` 1045 initConfig("yaml", config) 1046 1047 assert.Equal(t, "steve", GetString("name")) 1048 1049 polyester := "polyester" 1050 SetDefault("clothing.shirt", polyester) 1051 SetDefault("clothing.jacket.price", 100) 1052 1053 assert.Equal(t, "leather", GetString("clothing.jacket")) 1054 assert.Nil(t, Get("clothing.jacket.price")) 1055 assert.Equal(t, polyester, GetString("clothing.shirt")) 1056 1057 clothingSettings := AllSettings()["clothing"].(map[string]interface{}) 1058 assert.Equal(t, "leather", clothingSettings["jacket"]) 1059 assert.Equal(t, polyester, clothingSettings["shirt"]) 1060 } 1061 1062 func TestDotParameter(t *testing.T) { 1063 initJSON() 1064 // shoud take precedence over batters defined in jsonExample 1065 r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`)) 1066 unmarshalReader(r, v.config) 1067 1068 actual := Get("batters.batter") 1069 expected := []interface{}{map[string]interface{}{"type": "Small"}} 1070 assert.Equal(t, expected, actual) 1071 } 1072 1073 func TestCaseInSensitive(t *testing.T) { 1074 for _, config := range []struct { 1075 typ string 1076 content string 1077 }{ 1078 {"yaml", ` 1079 aBcD: 1 1080 eF: 1081 gH: 2 1082 iJk: 3 1083 Lm: 1084 nO: 4 1085 P: 1086 Q: 5 1087 R: 6 1088 `}, 1089 {"json", `{ 1090 "aBcD": 1, 1091 "eF": { 1092 "iJk": 3, 1093 "Lm": { 1094 "P": { 1095 "Q": 5, 1096 "R": 6 1097 }, 1098 "nO": 4 1099 }, 1100 "gH": 2 1101 } 1102 }`}, 1103 {"toml", `aBcD = 1 1104 [eF] 1105 gH = 2 1106 iJk = 3 1107 [eF.Lm] 1108 nO = 4 1109 [eF.Lm.P] 1110 Q = 5 1111 R = 6 1112 `}, 1113 } { 1114 doTestCaseInSensitive(t, config.typ, config.content) 1115 } 1116 } 1117 1118 func doTestCaseInSensitive(t *testing.T, typ, config string) { 1119 initConfig(typ, config) 1120 Set("RfD", true) 1121 assert.Equal(t, true, Get("rfd")) 1122 assert.Equal(t, true, Get("rFD")) 1123 assert.Equal(t, 1, cast.ToInt(Get("abcd"))) 1124 assert.Equal(t, 1, cast.ToInt(Get("Abcd"))) 1125 assert.Equal(t, 2, cast.ToInt(Get("ef.gh"))) 1126 assert.Equal(t, 3, cast.ToInt(Get("ef.ijk"))) 1127 assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no"))) 1128 assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q"))) 1129 1130 } 1131 1132 func BenchmarkGetBool(b *testing.B) { 1133 key := "BenchmarkGetBool" 1134 v = New() 1135 v.Set(key, true) 1136 1137 for i := 0; i < b.N; i++ { 1138 if !v.GetBool(key) { 1139 b.Fatal("GetBool returned false") 1140 } 1141 } 1142 } 1143 1144 func BenchmarkGet(b *testing.B) { 1145 key := "BenchmarkGet" 1146 v = New() 1147 v.Set(key, true) 1148 1149 for i := 0; i < b.N; i++ { 1150 if !v.Get(key).(bool) { 1151 b.Fatal("Get returned false") 1152 } 1153 } 1154 } 1155 1156 // This is the "perfect result" for the above. 1157 func BenchmarkGetBoolFromMap(b *testing.B) { 1158 m := make(map[string]bool) 1159 key := "BenchmarkGetBool" 1160 m[key] = true 1161 1162 for i := 0; i < b.N; i++ { 1163 if !m[key] { 1164 b.Fatal("Map value was false") 1165 } 1166 } 1167 }