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