viper_test.go - viper - [fork] go viper port for 9front
 (HTM) git clone https://git.drkhsh.at/viper.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       viper_test.go (53078B)
       ---
            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         "io"
           12         "io/ioutil"
           13         "os"
           14         "os/exec"
           15         "path"
           16         "path/filepath"
           17         "reflect"
           18         "runtime"
           19         "sort"
           20         "strings"
           21         "sync"
           22         "testing"
           23         "time"
           24 
           25         "github.com/fsnotify/fsnotify"
           26         "github.com/mitchellh/mapstructure"
           27         "github.com/spf13/afero"
           28         "github.com/spf13/cast"
           29         "github.com/spf13/pflag"
           30         "github.com/stretchr/testify/assert"
           31         "github.com/stretchr/testify/require"
           32 
           33         "github.com/spf13/viper/internal/testutil"
           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 dotenvExample = []byte(`
           69 TITLE_DOTENV="DotEnv Example"
           70 TYPE_DOTENV=donut
           71 NAME_DOTENV=Cake`)
           72 
           73 var jsonExample = []byte(`{
           74 "id": "0001",
           75 "type": "donut",
           76 "name": "Cake",
           77 "ppu": 0.55,
           78 "batters": {
           79         "batter": [
           80                 { "type": "Regular" },
           81                 { "type": "Chocolate" },
           82                 { "type": "Blueberry" },
           83                 { "type": "Devil's Food" }
           84             ]
           85     }
           86 }`)
           87 
           88 var hclExample = []byte(`
           89 id = "0001"
           90 type = "donut"
           91 name = "Cake"
           92 ppu = 0.55
           93 foos {
           94         foo {
           95                 key = 1
           96         }
           97         foo {
           98                 key = 2
           99         }
          100         foo {
          101                 key = 3
          102         }
          103         foo {
          104                 key = 4
          105         }
          106 }`)
          107 
          108 var propertiesExample = []byte(`
          109 p_id: 0001
          110 p_type: donut
          111 p_name: Cake
          112 p_ppu: 0.55
          113 p_batters.batter.type: Regular
          114 `)
          115 
          116 var remoteExample = []byte(`{
          117 "id":"0002",
          118 "type":"cronut",
          119 "newkey":"remote"
          120 }`)
          121 
          122 var iniExample = []byte(`; Package name
          123 NAME        = ini
          124 ; Package version
          125 VERSION     = v1
          126 ; Package import path
          127 IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
          128 
          129 # Information about package author
          130 # Bio can be written in multiple lines.
          131 [author]
          132 NAME   = Unknown  ; Succeeding comment
          133 E-MAIL = fake@localhost
          134 GITHUB = https://github.com/%(NAME)s
          135 BIO    = """Gopher.
          136 Coding addict.
          137 Good man.
          138 """  # Succeeding comment`)
          139 
          140 func initConfigs() {
          141         Reset()
          142         var r io.Reader
          143         SetConfigType("yaml")
          144         r = bytes.NewReader(yamlExample)
          145         unmarshalReader(r, v.config)
          146 
          147         SetConfigType("json")
          148         r = bytes.NewReader(jsonExample)
          149         unmarshalReader(r, v.config)
          150 
          151         SetConfigType("hcl")
          152         r = bytes.NewReader(hclExample)
          153         unmarshalReader(r, v.config)
          154 
          155         SetConfigType("properties")
          156         r = bytes.NewReader(propertiesExample)
          157         unmarshalReader(r, v.config)
          158 
          159         SetConfigType("toml")
          160         r = bytes.NewReader(tomlExample)
          161         unmarshalReader(r, v.config)
          162 
          163         SetConfigType("env")
          164         r = bytes.NewReader(dotenvExample)
          165         unmarshalReader(r, v.config)
          166 
          167         SetConfigType("json")
          168         remote := bytes.NewReader(remoteExample)
          169         unmarshalReader(remote, v.kvstore)
          170 
          171         SetConfigType("ini")
          172         r = bytes.NewReader(iniExample)
          173         unmarshalReader(r, v.config)
          174 }
          175 
          176 func initConfig(typ, config string) {
          177         Reset()
          178         SetConfigType(typ)
          179         r := strings.NewReader(config)
          180 
          181         if err := unmarshalReader(r, v.config); err != nil {
          182                 panic(err)
          183         }
          184 }
          185 
          186 func initYAML() {
          187         initConfig("yaml", string(yamlExample))
          188 }
          189 
          190 func initJSON() {
          191         Reset()
          192         SetConfigType("json")
          193         r := bytes.NewReader(jsonExample)
          194 
          195         unmarshalReader(r, v.config)
          196 }
          197 
          198 func initProperties() {
          199         Reset()
          200         SetConfigType("properties")
          201         r := bytes.NewReader(propertiesExample)
          202 
          203         unmarshalReader(r, v.config)
          204 }
          205 
          206 func initTOML() {
          207         Reset()
          208         SetConfigType("toml")
          209         r := bytes.NewReader(tomlExample)
          210 
          211         unmarshalReader(r, v.config)
          212 }
          213 
          214 func initDotEnv() {
          215         Reset()
          216         SetConfigType("env")
          217         r := bytes.NewReader(dotenvExample)
          218 
          219         unmarshalReader(r, v.config)
          220 }
          221 
          222 func initHcl() {
          223         Reset()
          224         SetConfigType("hcl")
          225         r := bytes.NewReader(hclExample)
          226 
          227         unmarshalReader(r, v.config)
          228 }
          229 
          230 func initIni() {
          231         Reset()
          232         SetConfigType("ini")
          233         r := bytes.NewReader(iniExample)
          234 
          235         unmarshalReader(r, v.config)
          236 }
          237 
          238 // make directories for testing
          239 func initDirs(t *testing.T) (string, string, func()) {
          240         var (
          241                 testDirs = []string{`a a`, `b`, `C_`}
          242                 config   = `improbable`
          243         )
          244 
          245         if runtime.GOOS != "windows" {
          246                 testDirs = append(testDirs, `d\d`)
          247         }
          248 
          249         root, err := ioutil.TempDir("", "")
          250         require.NoError(t, err, "Failed to create temporary directory")
          251 
          252         cleanup := true
          253         defer func() {
          254                 if cleanup {
          255                         os.Chdir("..")
          256                         os.RemoveAll(root)
          257                 }
          258         }()
          259 
          260         assert.Nil(t, err)
          261 
          262         err = os.Chdir(root)
          263         require.Nil(t, err)
          264 
          265         for _, dir := range testDirs {
          266                 err = os.Mkdir(dir, 0750)
          267                 assert.Nil(t, err)
          268 
          269                 err = ioutil.WriteFile(
          270                         path.Join(dir, config+".toml"),
          271                         []byte("key = \"value is "+dir+"\"\n"),
          272                         0640)
          273                 assert.Nil(t, err)
          274         }
          275 
          276         cleanup = false
          277         return root, config, func() {
          278                 os.Chdir("..")
          279                 os.RemoveAll(root)
          280         }
          281 }
          282 
          283 // stubs for PFlag Values
          284 type stringValue string
          285 
          286 func newStringValue(val string, p *string) *stringValue {
          287         *p = val
          288         return (*stringValue)(p)
          289 }
          290 
          291 func (s *stringValue) Set(val string) error {
          292         *s = stringValue(val)
          293         return nil
          294 }
          295 
          296 func (s *stringValue) Type() string {
          297         return "string"
          298 }
          299 
          300 func (s *stringValue) String() string {
          301         return string(*s)
          302 }
          303 
          304 func TestBasics(t *testing.T) {
          305         SetConfigFile("/tmp/config.yaml")
          306         filename, err := v.getConfigFile()
          307         assert.Equal(t, "/tmp/config.yaml", filename)
          308         assert.NoError(t, err)
          309 }
          310 
          311 func TestSearchInPath_WithoutConfigTypeSet(t *testing.T) {
          312         filename := ".dotfilenoext"
          313         path := "/tmp"
          314         file := filepath.Join(path, filename)
          315         SetConfigName(filename)
          316         AddConfigPath(path)
          317         _, createErr := v.fs.Create(file)
          318         defer func() {
          319                 _ = v.fs.Remove(file)
          320         }()
          321         assert.NoError(t, createErr)
          322         _, err := v.getConfigFile()
          323         // unless config type is set, files without extension
          324         // are not considered
          325         assert.Error(t, err)
          326 }
          327 
          328 func TestSearchInPath(t *testing.T) {
          329         filename := ".dotfilenoext"
          330         path := "/tmp"
          331         file := filepath.Join(path, filename)
          332         SetConfigName(filename)
          333         SetConfigType("yaml")
          334         AddConfigPath(path)
          335         _, createErr := v.fs.Create(file)
          336         defer func() {
          337                 _ = v.fs.Remove(file)
          338         }()
          339         assert.NoError(t, createErr)
          340         filename, err := v.getConfigFile()
          341         assert.Equal(t, file, filename)
          342         assert.NoError(t, err)
          343 }
          344 
          345 func TestSearchInPath_FilesOnly(t *testing.T) {
          346         fs := afero.NewMemMapFs()
          347 
          348         err := fs.Mkdir("/tmp/config", 0777)
          349         require.NoError(t, err)
          350 
          351         _, err = fs.Create("/tmp/config/config.yaml")
          352         require.NoError(t, err)
          353 
          354         v := New()
          355 
          356         v.SetFs(fs)
          357         v.AddConfigPath("/tmp")
          358         v.AddConfigPath("/tmp/config")
          359 
          360         filename, err := v.getConfigFile()
          361         assert.Equal(t, "/tmp/config/config.yaml", filename)
          362         assert.NoError(t, err)
          363 }
          364 
          365 func TestDefault(t *testing.T) {
          366         SetDefault("age", 45)
          367         assert.Equal(t, 45, Get("age"))
          368 
          369         SetDefault("clothing.jacket", "slacks")
          370         assert.Equal(t, "slacks", Get("clothing.jacket"))
          371 
          372         SetConfigType("yaml")
          373         err := ReadConfig(bytes.NewBuffer(yamlExample))
          374 
          375         assert.NoError(t, err)
          376         assert.Equal(t, "leather", Get("clothing.jacket"))
          377 }
          378 
          379 func TestUnmarshaling(t *testing.T) {
          380         SetConfigType("yaml")
          381         r := bytes.NewReader(yamlExample)
          382 
          383         unmarshalReader(r, v.config)
          384         assert.True(t, InConfig("name"))
          385         assert.False(t, InConfig("state"))
          386         assert.Equal(t, "steve", Get("name"))
          387         assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
          388         assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
          389         assert.Equal(t, 35, Get("age"))
          390 }
          391 
          392 func TestUnmarshalExact(t *testing.T) {
          393         vip := New()
          394         target := &testUnmarshalExtra{}
          395         vip.SetConfigType("yaml")
          396         r := bytes.NewReader(yamlExampleWithExtras)
          397         vip.ReadConfig(r)
          398         err := vip.UnmarshalExact(target)
          399         if err == nil {
          400                 t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields")
          401         }
          402 }
          403 
          404 func TestOverrides(t *testing.T) {
          405         Set("age", 40)
          406         assert.Equal(t, 40, Get("age"))
          407 }
          408 
          409 func TestDefaultPost(t *testing.T) {
          410         assert.NotEqual(t, "NYC", Get("state"))
          411         SetDefault("state", "NYC")
          412         assert.Equal(t, "NYC", Get("state"))
          413 }
          414 
          415 func TestAliases(t *testing.T) {
          416         RegisterAlias("years", "age")
          417         assert.Equal(t, 40, Get("years"))
          418         Set("years", 45)
          419         assert.Equal(t, 45, Get("age"))
          420 }
          421 
          422 func TestAliasInConfigFile(t *testing.T) {
          423         // the config file specifies "beard".  If we make this an alias for
          424         // "hasbeard", we still want the old config file to work with beard.
          425         RegisterAlias("beard", "hasbeard")
          426         assert.Equal(t, true, Get("hasbeard"))
          427         Set("hasbeard", false)
          428         assert.Equal(t, false, Get("beard"))
          429 }
          430 
          431 func TestYML(t *testing.T) {
          432         initYAML()
          433         assert.Equal(t, "steve", Get("name"))
          434 }
          435 
          436 func TestJSON(t *testing.T) {
          437         initJSON()
          438         assert.Equal(t, "0001", Get("id"))
          439 }
          440 
          441 func TestProperties(t *testing.T) {
          442         initProperties()
          443         assert.Equal(t, "0001", Get("p_id"))
          444 }
          445 
          446 func TestTOML(t *testing.T) {
          447         initTOML()
          448         assert.Equal(t, "TOML Example", Get("title"))
          449 }
          450 
          451 func TestDotEnv(t *testing.T) {
          452         initDotEnv()
          453         assert.Equal(t, "DotEnv Example", Get("title_dotenv"))
          454 }
          455 
          456 func TestHCL(t *testing.T) {
          457         initHcl()
          458         assert.Equal(t, "0001", Get("id"))
          459         assert.Equal(t, 0.55, Get("ppu"))
          460         assert.Equal(t, "donut", Get("type"))
          461         assert.Equal(t, "Cake", Get("name"))
          462         Set("id", "0002")
          463         assert.Equal(t, "0002", Get("id"))
          464         assert.NotEqual(t, "cronut", Get("type"))
          465 }
          466 
          467 func TestIni(t *testing.T) {
          468         initIni()
          469         assert.Equal(t, "ini", Get("default.name"))
          470 }
          471 
          472 func TestRemotePrecedence(t *testing.T) {
          473         initJSON()
          474 
          475         remote := bytes.NewReader(remoteExample)
          476         assert.Equal(t, "0001", Get("id"))
          477         unmarshalReader(remote, v.kvstore)
          478         assert.Equal(t, "0001", Get("id"))
          479         assert.NotEqual(t, "cronut", Get("type"))
          480         assert.Equal(t, "remote", Get("newkey"))
          481         Set("newkey", "newvalue")
          482         assert.NotEqual(t, "remote", Get("newkey"))
          483         assert.Equal(t, "newvalue", Get("newkey"))
          484         Set("newkey", "remote")
          485 }
          486 
          487 func TestEnv(t *testing.T) {
          488         initJSON()
          489 
          490         BindEnv("id")
          491         BindEnv("f", "FOOD", "OLD_FOOD")
          492 
          493         testutil.Setenv(t, "ID", "13")
          494         testutil.Setenv(t, "FOOD", "apple")
          495         testutil.Setenv(t, "OLD_FOOD", "banana")
          496         testutil.Setenv(t, "NAME", "crunk")
          497 
          498         assert.Equal(t, "13", Get("id"))
          499         assert.Equal(t, "apple", Get("f"))
          500         assert.Equal(t, "Cake", Get("name"))
          501 
          502         AutomaticEnv()
          503 
          504         assert.Equal(t, "crunk", Get("name"))
          505 }
          506 
          507 func TestMultipleEnv(t *testing.T) {
          508         initJSON()
          509 
          510         BindEnv("f", "FOOD", "OLD_FOOD")
          511 
          512         testutil.Setenv(t, "OLD_FOOD", "banana")
          513 
          514         assert.Equal(t, "banana", Get("f"))
          515 }
          516 
          517 func TestEmptyEnv(t *testing.T) {
          518         initJSON()
          519 
          520         BindEnv("type") // Empty environment variable
          521         BindEnv("name") // Bound, but not set environment variable
          522 
          523         testutil.Setenv(t, "TYPE", "")
          524 
          525         assert.Equal(t, "donut", Get("type"))
          526         assert.Equal(t, "Cake", Get("name"))
          527 }
          528 
          529 func TestEmptyEnv_Allowed(t *testing.T) {
          530         initJSON()
          531 
          532         AllowEmptyEnv(true)
          533 
          534         BindEnv("type") // Empty environment variable
          535         BindEnv("name") // Bound, but not set environment variable
          536 
          537         testutil.Setenv(t, "TYPE", "")
          538 
          539         assert.Equal(t, "", Get("type"))
          540         assert.Equal(t, "Cake", Get("name"))
          541 }
          542 
          543 func TestEnvPrefix(t *testing.T) {
          544         initJSON()
          545 
          546         SetEnvPrefix("foo") // will be uppercased automatically
          547         BindEnv("id")
          548         BindEnv("f", "FOOD") // not using prefix
          549 
          550         testutil.Setenv(t, "FOO_ID", "13")
          551         testutil.Setenv(t, "FOOD", "apple")
          552         testutil.Setenv(t, "FOO_NAME", "crunk")
          553 
          554         assert.Equal(t, "13", Get("id"))
          555         assert.Equal(t, "apple", Get("f"))
          556         assert.Equal(t, "Cake", Get("name"))
          557 
          558         AutomaticEnv()
          559 
          560         assert.Equal(t, "crunk", Get("name"))
          561 }
          562 
          563 func TestAutoEnv(t *testing.T) {
          564         Reset()
          565 
          566         AutomaticEnv()
          567 
          568         testutil.Setenv(t, "FOO_BAR", "13")
          569 
          570         assert.Equal(t, "13", Get("foo_bar"))
          571 }
          572 
          573 func TestAutoEnvWithPrefix(t *testing.T) {
          574         Reset()
          575 
          576         AutomaticEnv()
          577         SetEnvPrefix("Baz")
          578 
          579         testutil.Setenv(t, "BAZ_BAR", "13")
          580 
          581         assert.Equal(t, "13", Get("bar"))
          582 }
          583 
          584 func TestSetEnvKeyReplacer(t *testing.T) {
          585         Reset()
          586 
          587         AutomaticEnv()
          588 
          589         testutil.Setenv(t, "REFRESH_INTERVAL", "30s")
          590 
          591         replacer := strings.NewReplacer("-", "_")
          592         SetEnvKeyReplacer(replacer)
          593 
          594         assert.Equal(t, "30s", Get("refresh-interval"))
          595 }
          596 
          597 func TestEnvKeyReplacer(t *testing.T) {
          598         v := NewWithOptions(EnvKeyReplacer(strings.NewReplacer("-", "_")))
          599 
          600         v.AutomaticEnv()
          601 
          602         testutil.Setenv(t, "REFRESH_INTERVAL", "30s")
          603 
          604         assert.Equal(t, "30s", v.Get("refresh-interval"))
          605 }
          606 
          607 func TestAllKeys(t *testing.T) {
          608         initConfigs()
          609 
          610         ks := sort.StringSlice{
          611                 "title",
          612                 "author.bio",
          613                 "author.e-mail",
          614                 "author.github",
          615                 "author.name",
          616                 "newkey",
          617                 "owner.organization",
          618                 "owner.dob",
          619                 "owner.bio",
          620                 "name",
          621                 "beard",
          622                 "ppu",
          623                 "batters.batter",
          624                 "hobbies",
          625                 "clothing.jacket",
          626                 "clothing.trousers",
          627                 "default.import_path",
          628                 "default.name",
          629                 "default.version",
          630                 "clothing.pants.size",
          631                 "age",
          632                 "hacker",
          633                 "id",
          634                 "type",
          635                 "eyes",
          636                 "p_id",
          637                 "p_ppu",
          638                 "p_batters.batter.type",
          639                 "p_type",
          640                 "p_name",
          641                 "foos",
          642                 "title_dotenv",
          643                 "type_dotenv",
          644                 "name_dotenv",
          645         }
          646         dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
          647         all := map[string]interface{}{
          648                 "owner": map[string]interface{}{
          649                         "organization": "MongoDB",
          650                         "bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
          651                         "dob":          dob,
          652                 },
          653                 "title": "TOML Example",
          654                 "author": map[string]interface{}{
          655                         "e-mail": "fake@localhost",
          656                         "github": "https://github.com/Unknown",
          657                         "name":   "Unknown",
          658                         "bio":    "Gopher.\nCoding addict.\nGood man.\n",
          659                 },
          660                 "ppu":  0.55,
          661                 "eyes": "brown",
          662                 "clothing": map[string]interface{}{
          663                         "trousers": "denim",
          664                         "jacket":   "leather",
          665                         "pants":    map[string]interface{}{"size": "large"},
          666                 },
          667                 "default": map[string]interface{}{
          668                         "import_path": "gopkg.in/ini.v1",
          669                         "name":        "ini",
          670                         "version":     "v1",
          671                 },
          672                 "id": "0001",
          673                 "batters": map[string]interface{}{
          674                         "batter": []interface{}{
          675                                 map[string]interface{}{"type": "Regular"},
          676                                 map[string]interface{}{"type": "Chocolate"},
          677                                 map[string]interface{}{"type": "Blueberry"},
          678                                 map[string]interface{}{"type": "Devil's Food"},
          679                         },
          680                 },
          681                 "hacker": true,
          682                 "beard":  true,
          683                 "hobbies": []interface{}{
          684                         "skateboarding",
          685                         "snowboarding",
          686                         "go",
          687                 },
          688                 "age":    35,
          689                 "type":   "donut",
          690                 "newkey": "remote",
          691                 "name":   "Cake",
          692                 "p_id":   "0001",
          693                 "p_ppu":  "0.55",
          694                 "p_name": "Cake",
          695                 "p_batters": map[string]interface{}{
          696                         "batter": map[string]interface{}{"type": "Regular"},
          697                 },
          698                 "p_type": "donut",
          699                 "foos": []map[string]interface{}{
          700                         {
          701                                 "foo": []map[string]interface{}{
          702                                         {"key": 1},
          703                                         {"key": 2},
          704                                         {"key": 3},
          705                                         {"key": 4},
          706                                 },
          707                         },
          708                 },
          709                 "title_dotenv": "DotEnv Example",
          710                 "type_dotenv":  "donut",
          711                 "name_dotenv":  "Cake",
          712         }
          713 
          714         allkeys := sort.StringSlice(AllKeys())
          715         allkeys.Sort()
          716         ks.Sort()
          717 
          718         assert.Equal(t, ks, allkeys)
          719         assert.Equal(t, all, AllSettings())
          720 }
          721 
          722 func TestAllKeysWithEnv(t *testing.T) {
          723         v := New()
          724 
          725         // bind and define environment variables (including a nested one)
          726         v.BindEnv("id")
          727         v.BindEnv("foo.bar")
          728         v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
          729 
          730         testutil.Setenv(t, "ID", "13")
          731         testutil.Setenv(t, "FOO_BAR", "baz")
          732 
          733         expectedKeys := sort.StringSlice{"id", "foo.bar"}
          734         expectedKeys.Sort()
          735         keys := sort.StringSlice(v.AllKeys())
          736         keys.Sort()
          737         assert.Equal(t, expectedKeys, keys)
          738 }
          739 
          740 func TestAliasesOfAliases(t *testing.T) {
          741         Set("Title", "Checking Case")
          742         RegisterAlias("Foo", "Bar")
          743         RegisterAlias("Bar", "Title")
          744         assert.Equal(t, "Checking Case", Get("FOO"))
          745 }
          746 
          747 func TestRecursiveAliases(t *testing.T) {
          748         RegisterAlias("Baz", "Roo")
          749         RegisterAlias("Roo", "baz")
          750 }
          751 
          752 func TestUnmarshal(t *testing.T) {
          753         SetDefault("port", 1313)
          754         Set("name", "Steve")
          755         Set("duration", "1s1ms")
          756         Set("modes", []int{1, 2, 3})
          757 
          758         type config struct {
          759                 Port     int
          760                 Name     string
          761                 Duration time.Duration
          762                 Modes    []int
          763         }
          764 
          765         var C config
          766 
          767         err := Unmarshal(&C)
          768         if err != nil {
          769                 t.Fatalf("unable to decode into struct, %v", err)
          770         }
          771 
          772         assert.Equal(
          773                 t,
          774                 &config{
          775                         Name:     "Steve",
          776                         Port:     1313,
          777                         Duration: time.Second + time.Millisecond,
          778                         Modes:    []int{1, 2, 3},
          779                 },
          780                 &C,
          781         )
          782 
          783         Set("port", 1234)
          784         err = Unmarshal(&C)
          785         if err != nil {
          786                 t.Fatalf("unable to decode into struct, %v", err)
          787         }
          788 
          789         assert.Equal(
          790                 t,
          791                 &config{
          792                         Name:     "Steve",
          793                         Port:     1234,
          794                         Duration: time.Second + time.Millisecond,
          795                         Modes:    []int{1, 2, 3},
          796                 },
          797                 &C,
          798         )
          799 }
          800 
          801 func TestUnmarshalWithDecoderOptions(t *testing.T) {
          802         Set("credentials", "{\"foo\":\"bar\"}")
          803 
          804         opt := DecodeHook(mapstructure.ComposeDecodeHookFunc(
          805                 mapstructure.StringToTimeDurationHookFunc(),
          806                 mapstructure.StringToSliceHookFunc(","),
          807                 // Custom Decode Hook Function
          808                 func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) {
          809                         if rf != reflect.String || rt != reflect.Map {
          810                                 return data, nil
          811                         }
          812                         m := map[string]string{}
          813                         raw := data.(string)
          814                         if raw == "" {
          815                                 return m, nil
          816                         }
          817                         return m, json.Unmarshal([]byte(raw), &m)
          818                 },
          819         ))
          820 
          821         type config struct {
          822                 Credentials map[string]string
          823         }
          824 
          825         var C config
          826 
          827         err := Unmarshal(&C, opt)
          828         if err != nil {
          829                 t.Fatalf("unable to decode into struct, %v", err)
          830         }
          831 
          832         assert.Equal(t, &config{
          833                 Credentials: map[string]string{"foo": "bar"},
          834         }, &C)
          835 }
          836 
          837 func TestBindPFlags(t *testing.T) {
          838         v := New() // create independent Viper object
          839         flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
          840 
          841         testValues := map[string]*string{
          842                 "host":     nil,
          843                 "port":     nil,
          844                 "endpoint": nil,
          845         }
          846 
          847         mutatedTestValues := map[string]string{
          848                 "host":     "localhost",
          849                 "port":     "6060",
          850                 "endpoint": "/public",
          851         }
          852 
          853         for name := range testValues {
          854                 testValues[name] = flagSet.String(name, "", "test")
          855         }
          856 
          857         err := v.BindPFlags(flagSet)
          858         if err != nil {
          859                 t.Fatalf("error binding flag set, %v", err)
          860         }
          861 
          862         flagSet.VisitAll(func(flag *pflag.Flag) {
          863                 flag.Value.Set(mutatedTestValues[flag.Name])
          864                 flag.Changed = true
          865         })
          866 
          867         for name, expected := range mutatedTestValues {
          868                 assert.Equal(t, expected, v.Get(name))
          869         }
          870 }
          871 
          872 // nolint: dupl
          873 func TestBindPFlagsStringSlice(t *testing.T) {
          874         tests := []struct {
          875                 Expected []string
          876                 Value    string
          877         }{
          878                 {[]string{}, ""},
          879                 {[]string{"jeden"}, "jeden"},
          880                 {[]string{"dwa", "trzy"}, "dwa,trzy"},
          881                 {[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""},
          882         }
          883 
          884         v := New() // create independent Viper object
          885         defaultVal := []string{"default"}
          886         v.SetDefault("stringslice", defaultVal)
          887 
          888         for _, testValue := range tests {
          889                 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
          890                 flagSet.StringSlice("stringslice", testValue.Expected, "test")
          891 
          892                 for _, changed := range []bool{true, false} {
          893                         flagSet.VisitAll(func(f *pflag.Flag) {
          894                                 f.Value.Set(testValue.Value)
          895                                 f.Changed = changed
          896                         })
          897 
          898                         err := v.BindPFlags(flagSet)
          899                         if err != nil {
          900                                 t.Fatalf("error binding flag set, %v", err)
          901                         }
          902 
          903                         type TestStr struct {
          904                                 StringSlice []string
          905                         }
          906                         val := &TestStr{}
          907                         if err := v.Unmarshal(val); err != nil {
          908                                 t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
          909                         }
          910                         if changed {
          911                                 assert.Equal(t, testValue.Expected, val.StringSlice)
          912                                 assert.Equal(t, testValue.Expected, v.Get("stringslice"))
          913                         } else {
          914                                 assert.Equal(t, defaultVal, val.StringSlice)
          915                         }
          916                 }
          917         }
          918 }
          919 
          920 // nolint: dupl
          921 func TestBindPFlagsIntSlice(t *testing.T) {
          922         tests := []struct {
          923                 Expected []int
          924                 Value    string
          925         }{
          926                 {[]int{}, ""},
          927                 {[]int{1}, "1"},
          928                 {[]int{2, 3}, "2,3"},
          929         }
          930 
          931         v := New() // create independent Viper object
          932         defaultVal := []int{0}
          933         v.SetDefault("intslice", defaultVal)
          934 
          935         for _, testValue := range tests {
          936                 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
          937                 flagSet.IntSlice("intslice", testValue.Expected, "test")
          938 
          939                 for _, changed := range []bool{true, false} {
          940                         flagSet.VisitAll(func(f *pflag.Flag) {
          941                                 f.Value.Set(testValue.Value)
          942                                 f.Changed = changed
          943                         })
          944 
          945                         err := v.BindPFlags(flagSet)
          946                         if err != nil {
          947                                 t.Fatalf("error binding flag set, %v", err)
          948                         }
          949 
          950                         type TestInt struct {
          951                                 IntSlice []int
          952                         }
          953                         val := &TestInt{}
          954                         if err := v.Unmarshal(val); err != nil {
          955                                 t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
          956                         }
          957                         if changed {
          958                                 assert.Equal(t, testValue.Expected, val.IntSlice)
          959                                 assert.Equal(t, testValue.Expected, v.Get("intslice"))
          960                         } else {
          961                                 assert.Equal(t, defaultVal, val.IntSlice)
          962                         }
          963                 }
          964         }
          965 }
          966 
          967 func TestBindPFlag(t *testing.T) {
          968         testString := "testing"
          969         testValue := newStringValue(testString, &testString)
          970 
          971         flag := &pflag.Flag{
          972                 Name:    "testflag",
          973                 Value:   testValue,
          974                 Changed: false,
          975         }
          976 
          977         BindPFlag("testvalue", flag)
          978 
          979         assert.Equal(t, testString, Get("testvalue"))
          980 
          981         flag.Value.Set("testing_mutate")
          982         flag.Changed = true // hack for pflag usage
          983 
          984         assert.Equal(t, "testing_mutate", Get("testvalue"))
          985 }
          986 
          987 func TestBindPFlagDetectNilFlag(t *testing.T) {
          988         result := BindPFlag("testvalue", nil)
          989         assert.Error(t, result)
          990 }
          991 
          992 func TestBindPFlagStringToString(t *testing.T) {
          993         tests := []struct {
          994                 Expected map[string]string
          995                 Value    string
          996         }{
          997                 {map[string]string{}, ""},
          998                 {map[string]string{"yo": "hi"}, "yo=hi"},
          999                 {map[string]string{"yo": "hi", "oh": "hi=there"}, "yo=hi,oh=hi=there"},
         1000                 {map[string]string{"yo": ""}, "yo="},
         1001                 {map[string]string{"yo": "", "oh": "hi=there"}, "yo=,oh=hi=there"},
         1002         }
         1003 
         1004         v := New() // create independent Viper object
         1005         defaultVal := map[string]string{}
         1006         v.SetDefault("stringtostring", defaultVal)
         1007 
         1008         for _, testValue := range tests {
         1009                 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
         1010                 flagSet.StringToString("stringtostring", testValue.Expected, "test")
         1011 
         1012                 for _, changed := range []bool{true, false} {
         1013                         flagSet.VisitAll(func(f *pflag.Flag) {
         1014                                 f.Value.Set(testValue.Value)
         1015                                 f.Changed = changed
         1016                         })
         1017 
         1018                         err := v.BindPFlags(flagSet)
         1019                         if err != nil {
         1020                                 t.Fatalf("error binding flag set, %v", err)
         1021                         }
         1022 
         1023                         type TestMap struct {
         1024                                 StringToString map[string]string
         1025                         }
         1026                         val := &TestMap{}
         1027                         if err := v.Unmarshal(val); err != nil {
         1028                                 t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
         1029                         }
         1030                         if changed {
         1031                                 assert.Equal(t, testValue.Expected, val.StringToString)
         1032                         } else {
         1033                                 assert.Equal(t, defaultVal, val.StringToString)
         1034                         }
         1035                 }
         1036         }
         1037 }
         1038 
         1039 func TestBoundCaseSensitivity(t *testing.T) {
         1040         assert.Equal(t, "brown", Get("eyes"))
         1041 
         1042         BindEnv("eYEs", "TURTLE_EYES")
         1043 
         1044         testutil.Setenv(t, "TURTLE_EYES", "blue")
         1045 
         1046         assert.Equal(t, "blue", Get("eyes"))
         1047 
         1048         testString := "green"
         1049         testValue := newStringValue(testString, &testString)
         1050 
         1051         flag := &pflag.Flag{
         1052                 Name:    "eyeballs",
         1053                 Value:   testValue,
         1054                 Changed: true,
         1055         }
         1056 
         1057         BindPFlag("eYEs", flag)
         1058         assert.Equal(t, "green", Get("eyes"))
         1059 }
         1060 
         1061 func TestSizeInBytes(t *testing.T) {
         1062         input := map[string]uint{
         1063                 "":               0,
         1064                 "b":              0,
         1065                 "12 bytes":       0,
         1066                 "200000000000gb": 0,
         1067                 "12 b":           12,
         1068                 "43 MB":          43 * (1 << 20),
         1069                 "10mb":           10 * (1 << 20),
         1070                 "1gb":            1 << 30,
         1071         }
         1072 
         1073         for str, expected := range input {
         1074                 assert.Equal(t, expected, parseSizeInBytes(str), str)
         1075         }
         1076 }
         1077 
         1078 func TestFindsNestedKeys(t *testing.T) {
         1079         initConfigs()
         1080         dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
         1081 
         1082         Set("super", map[string]interface{}{
         1083                 "deep": map[string]interface{}{
         1084                         "nested": "value",
         1085                 },
         1086         })
         1087 
         1088         expected := map[string]interface{}{
         1089                 "super": map[string]interface{}{
         1090                         "deep": map[string]interface{}{
         1091                                 "nested": "value",
         1092                         },
         1093                 },
         1094                 "super.deep": map[string]interface{}{
         1095                         "nested": "value",
         1096                 },
         1097                 "super.deep.nested":  "value",
         1098                 "owner.organization": "MongoDB",
         1099                 "batters.batter": []interface{}{
         1100                         map[string]interface{}{
         1101                                 "type": "Regular",
         1102                         },
         1103                         map[string]interface{}{
         1104                                 "type": "Chocolate",
         1105                         },
         1106                         map[string]interface{}{
         1107                                 "type": "Blueberry",
         1108                         },
         1109                         map[string]interface{}{
         1110                                 "type": "Devil's Food",
         1111                         },
         1112                 },
         1113                 "hobbies": []interface{}{
         1114                         "skateboarding", "snowboarding", "go",
         1115                 },
         1116                 "TITLE_DOTENV": "DotEnv Example",
         1117                 "TYPE_DOTENV":  "donut",
         1118                 "NAME_DOTENV":  "Cake",
         1119                 "title":        "TOML Example",
         1120                 "newkey":       "remote",
         1121                 "batters": map[string]interface{}{
         1122                         "batter": []interface{}{
         1123                                 map[string]interface{}{
         1124                                         "type": "Regular",
         1125                                 },
         1126                                 map[string]interface{}{
         1127                                         "type": "Chocolate",
         1128                                 },
         1129                                 map[string]interface{}{
         1130                                         "type": "Blueberry",
         1131                                 },
         1132                                 map[string]interface{}{
         1133                                         "type": "Devil's Food",
         1134                                 },
         1135                         },
         1136                 },
         1137                 "eyes": "brown",
         1138                 "age":  35,
         1139                 "owner": map[string]interface{}{
         1140                         "organization": "MongoDB",
         1141                         "bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
         1142                         "dob":          dob,
         1143                 },
         1144                 "owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large",
         1145                 "type":      "donut",
         1146                 "id":        "0001",
         1147                 "name":      "Cake",
         1148                 "hacker":    true,
         1149                 "ppu":       0.55,
         1150                 "clothing": map[string]interface{}{
         1151                         "jacket":   "leather",
         1152                         "trousers": "denim",
         1153                         "pants": map[string]interface{}{
         1154                                 "size": "large",
         1155                         },
         1156                 },
         1157                 "clothing.jacket":     "leather",
         1158                 "clothing.pants.size": "large",
         1159                 "clothing.trousers":   "denim",
         1160                 "owner.dob":           dob,
         1161                 "beard":               true,
         1162                 "foos": []map[string]interface{}{
         1163                         {
         1164                                 "foo": []map[string]interface{}{
         1165                                         {
         1166                                                 "key": 1,
         1167                                         },
         1168                                         {
         1169                                                 "key": 2,
         1170                                         },
         1171                                         {
         1172                                                 "key": 3,
         1173                                         },
         1174                                         {
         1175                                                 "key": 4,
         1176                                         },
         1177                                 },
         1178                         },
         1179                 },
         1180         }
         1181 
         1182         for key, expectedValue := range expected {
         1183                 assert.Equal(t, expectedValue, v.Get(key))
         1184         }
         1185 }
         1186 
         1187 func TestReadBufConfig(t *testing.T) {
         1188         v := New()
         1189         v.SetConfigType("yaml")
         1190         v.ReadConfig(bytes.NewBuffer(yamlExample))
         1191         t.Log(v.AllKeys())
         1192 
         1193         assert.True(t, v.InConfig("name"))
         1194         assert.False(t, v.InConfig("state"))
         1195         assert.Equal(t, "steve", v.Get("name"))
         1196         assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
         1197         assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
         1198         assert.Equal(t, 35, v.Get("age"))
         1199 }
         1200 
         1201 func TestIsSet(t *testing.T) {
         1202         v := New()
         1203         v.SetConfigType("yaml")
         1204 
         1205         /* config and defaults */
         1206         v.ReadConfig(bytes.NewBuffer(yamlExample))
         1207         v.SetDefault("clothing.shoes", "sneakers")
         1208 
         1209         assert.True(t, v.IsSet("clothing"))
         1210         assert.True(t, v.IsSet("clothing.jacket"))
         1211         assert.False(t, v.IsSet("clothing.jackets"))
         1212         assert.True(t, v.IsSet("clothing.shoes"))
         1213 
         1214         /* state change */
         1215         assert.False(t, v.IsSet("helloworld"))
         1216         v.Set("helloworld", "fubar")
         1217         assert.True(t, v.IsSet("helloworld"))
         1218 
         1219         /* env */
         1220         v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
         1221         v.BindEnv("eyes")
         1222         v.BindEnv("foo")
         1223         v.BindEnv("clothing.hat")
         1224         v.BindEnv("clothing.hats")
         1225 
         1226         testutil.Setenv(t, "FOO", "bar")
         1227         testutil.Setenv(t, "CLOTHING_HAT", "bowler")
         1228 
         1229         assert.True(t, v.IsSet("eyes"))           // in the config file
         1230         assert.True(t, v.IsSet("foo"))            // in the environment
         1231         assert.True(t, v.IsSet("clothing.hat"))   // in the environment
         1232         assert.False(t, v.IsSet("clothing.hats")) // not defined
         1233 
         1234         /* flags */
         1235         flagset := pflag.NewFlagSet("testisset", pflag.ContinueOnError)
         1236         flagset.Bool("foobaz", false, "foobaz")
         1237         flagset.Bool("barbaz", false, "barbaz")
         1238         foobaz, barbaz := flagset.Lookup("foobaz"), flagset.Lookup("barbaz")
         1239         v.BindPFlag("foobaz", foobaz)
         1240         v.BindPFlag("barbaz", barbaz)
         1241         barbaz.Value.Set("true")
         1242         barbaz.Changed = true // hack for pflag usage
         1243 
         1244         assert.False(t, v.IsSet("foobaz"))
         1245         assert.True(t, v.IsSet("barbaz"))
         1246 }
         1247 
         1248 func TestDirsSearch(t *testing.T) {
         1249         root, config, cleanup := initDirs(t)
         1250         defer cleanup()
         1251 
         1252         v := New()
         1253         v.SetConfigName(config)
         1254         v.SetDefault(`key`, `default`)
         1255 
         1256         entries, err := ioutil.ReadDir(root)
         1257         assert.Nil(t, err)
         1258         for _, e := range entries {
         1259                 if e.IsDir() {
         1260                         v.AddConfigPath(e.Name())
         1261                 }
         1262         }
         1263 
         1264         err = v.ReadInConfig()
         1265         assert.Nil(t, err)
         1266 
         1267         assert.Equal(t, `value is `+filepath.Base(v.configPaths[0]), v.GetString(`key`))
         1268 }
         1269 
         1270 func TestWrongDirsSearchNotFound(t *testing.T) {
         1271         _, config, cleanup := initDirs(t)
         1272         defer cleanup()
         1273 
         1274         v := New()
         1275         v.SetConfigName(config)
         1276         v.SetDefault(`key`, `default`)
         1277 
         1278         v.AddConfigPath(`whattayoutalkingbout`)
         1279         v.AddConfigPath(`thispathaintthere`)
         1280 
         1281         err := v.ReadInConfig()
         1282         assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
         1283 
         1284         // Even though config did not load and the error might have
         1285         // been ignored by the client, the default still loads
         1286         assert.Equal(t, `default`, v.GetString(`key`))
         1287 }
         1288 
         1289 func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
         1290         _, config, cleanup := initDirs(t)
         1291         defer cleanup()
         1292 
         1293         v := New()
         1294         v.SetConfigName(config)
         1295         v.SetDefault(`key`, `default`)
         1296 
         1297         v.AddConfigPath(`whattayoutalkingbout`)
         1298         v.AddConfigPath(`thispathaintthere`)
         1299 
         1300         err := v.MergeInConfig()
         1301         assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
         1302 
         1303         // Even though config did not load and the error might have
         1304         // been ignored by the client, the default still loads
         1305         assert.Equal(t, `default`, v.GetString(`key`))
         1306 }
         1307 
         1308 func TestSub(t *testing.T) {
         1309         v := New()
         1310         v.SetConfigType("yaml")
         1311         v.ReadConfig(bytes.NewBuffer(yamlExample))
         1312 
         1313         subv := v.Sub("clothing")
         1314         assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
         1315 
         1316         subv = v.Sub("clothing.pants")
         1317         assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size"))
         1318 
         1319         subv = v.Sub("clothing.pants.size")
         1320         assert.Equal(t, (*Viper)(nil), subv)
         1321 
         1322         subv = v.Sub("missing.key")
         1323         assert.Equal(t, (*Viper)(nil), subv)
         1324 }
         1325 
         1326 var hclWriteExpected = []byte(`"foos" = {
         1327   "foo" = {
         1328     "key" = 1
         1329   }
         1330 
         1331   "foo" = {
         1332     "key" = 2
         1333   }
         1334 
         1335   "foo" = {
         1336     "key" = 3
         1337   }
         1338 
         1339   "foo" = {
         1340     "key" = 4
         1341   }
         1342 }
         1343 
         1344 "id" = "0001"
         1345 
         1346 "name" = "Cake"
         1347 
         1348 "ppu" = 0.55
         1349 
         1350 "type" = "donut"`)
         1351 
         1352 var jsonWriteExpected = []byte(`{
         1353   "batters": {
         1354     "batter": [
         1355       {
         1356         "type": "Regular"
         1357       },
         1358       {
         1359         "type": "Chocolate"
         1360       },
         1361       {
         1362         "type": "Blueberry"
         1363       },
         1364       {
         1365         "type": "Devil's Food"
         1366       }
         1367     ]
         1368   },
         1369   "id": "0001",
         1370   "name": "Cake",
         1371   "ppu": 0.55,
         1372   "type": "donut"
         1373 }`)
         1374 
         1375 var propertiesWriteExpected = []byte(`p_id = 0001
         1376 p_type = donut
         1377 p_name = Cake
         1378 p_ppu = 0.55
         1379 p_batters.batter.type = Regular
         1380 `)
         1381 
         1382 var yamlWriteExpected = []byte(`age: 35
         1383 beard: true
         1384 clothing:
         1385   jacket: leather
         1386   pants:
         1387     size: large
         1388   trousers: denim
         1389 eyes: brown
         1390 hacker: true
         1391 hobbies:
         1392 - skateboarding
         1393 - snowboarding
         1394 - go
         1395 name: steve
         1396 `)
         1397 
         1398 func TestWriteConfig(t *testing.T) {
         1399         fs := afero.NewMemMapFs()
         1400         testCases := map[string]struct {
         1401                 configName      string
         1402                 inConfigType    string
         1403                 outConfigType   string
         1404                 fileName        string
         1405                 input           []byte
         1406                 expectedContent []byte
         1407         }{
         1408                 "hcl with file extension": {
         1409                         configName:      "c",
         1410                         inConfigType:    "hcl",
         1411                         outConfigType:   "hcl",
         1412                         fileName:        "c.hcl",
         1413                         input:           hclExample,
         1414                         expectedContent: hclWriteExpected,
         1415                 },
         1416                 "hcl without file extension": {
         1417                         configName:      "c",
         1418                         inConfigType:    "hcl",
         1419                         outConfigType:   "hcl",
         1420                         fileName:        "c",
         1421                         input:           hclExample,
         1422                         expectedContent: hclWriteExpected,
         1423                 },
         1424                 "hcl with file extension and mismatch type": {
         1425                         configName:      "c",
         1426                         inConfigType:    "hcl",
         1427                         outConfigType:   "json",
         1428                         fileName:        "c.hcl",
         1429                         input:           hclExample,
         1430                         expectedContent: hclWriteExpected,
         1431                 },
         1432                 "json with file extension": {
         1433                         configName:      "c",
         1434                         inConfigType:    "json",
         1435                         outConfigType:   "json",
         1436                         fileName:        "c.json",
         1437                         input:           jsonExample,
         1438                         expectedContent: jsonWriteExpected,
         1439                 },
         1440                 "json without file extension": {
         1441                         configName:      "c",
         1442                         inConfigType:    "json",
         1443                         outConfigType:   "json",
         1444                         fileName:        "c",
         1445                         input:           jsonExample,
         1446                         expectedContent: jsonWriteExpected,
         1447                 },
         1448                 "json with file extension and mismatch type": {
         1449                         configName:      "c",
         1450                         inConfigType:    "json",
         1451                         outConfigType:   "hcl",
         1452                         fileName:        "c.json",
         1453                         input:           jsonExample,
         1454                         expectedContent: jsonWriteExpected,
         1455                 },
         1456                 "properties with file extension": {
         1457                         configName:      "c",
         1458                         inConfigType:    "properties",
         1459                         outConfigType:   "properties",
         1460                         fileName:        "c.properties",
         1461                         input:           propertiesExample,
         1462                         expectedContent: propertiesWriteExpected,
         1463                 },
         1464                 "properties without file extension": {
         1465                         configName:      "c",
         1466                         inConfigType:    "properties",
         1467                         outConfigType:   "properties",
         1468                         fileName:        "c",
         1469                         input:           propertiesExample,
         1470                         expectedContent: propertiesWriteExpected,
         1471                 },
         1472                 "yaml with file extension": {
         1473                         configName:      "c",
         1474                         inConfigType:    "yaml",
         1475                         outConfigType:   "yaml",
         1476                         fileName:        "c.yaml",
         1477                         input:           yamlExample,
         1478                         expectedContent: yamlWriteExpected,
         1479                 },
         1480                 "yaml without file extension": {
         1481                         configName:      "c",
         1482                         inConfigType:    "yaml",
         1483                         outConfigType:   "yaml",
         1484                         fileName:        "c",
         1485                         input:           yamlExample,
         1486                         expectedContent: yamlWriteExpected,
         1487                 },
         1488                 "yaml with file extension and mismatch type": {
         1489                         configName:      "c",
         1490                         inConfigType:    "yaml",
         1491                         outConfigType:   "json",
         1492                         fileName:        "c.yaml",
         1493                         input:           yamlExample,
         1494                         expectedContent: yamlWriteExpected,
         1495                 },
         1496         }
         1497         for name, tc := range testCases {
         1498                 t.Run(name, func(t *testing.T) {
         1499                         v := New()
         1500                         v.SetFs(fs)
         1501                         v.SetConfigName(tc.fileName)
         1502                         v.SetConfigType(tc.inConfigType)
         1503 
         1504                         err := v.ReadConfig(bytes.NewBuffer(tc.input))
         1505                         if err != nil {
         1506                                 t.Fatal(err)
         1507                         }
         1508                         v.SetConfigType(tc.outConfigType)
         1509                         if err := v.WriteConfigAs(tc.fileName); err != nil {
         1510                                 t.Fatal(err)
         1511                         }
         1512                         read, err := afero.ReadFile(fs, tc.fileName)
         1513                         if err != nil {
         1514                                 t.Fatal(err)
         1515                         }
         1516                         assert.Equal(t, tc.expectedContent, read)
         1517                 })
         1518         }
         1519 }
         1520 
         1521 func TestWriteConfigTOML(t *testing.T) {
         1522         fs := afero.NewMemMapFs()
         1523 
         1524         testCases := map[string]struct {
         1525                 configName string
         1526                 configType string
         1527                 fileName   string
         1528                 input      []byte
         1529         }{
         1530                 "with file extension": {
         1531                         configName: "c",
         1532                         configType: "toml",
         1533                         fileName:   "c.toml",
         1534                         input:      tomlExample,
         1535                 },
         1536                 "without file extension": {
         1537                         configName: "c",
         1538                         configType: "toml",
         1539                         fileName:   "c",
         1540                         input:      tomlExample,
         1541                 },
         1542         }
         1543         for name, tc := range testCases {
         1544                 t.Run(name, func(t *testing.T) {
         1545                         v := New()
         1546                         v.SetFs(fs)
         1547                         v.SetConfigName(tc.configName)
         1548                         v.SetConfigType(tc.configType)
         1549                         err := v.ReadConfig(bytes.NewBuffer(tc.input))
         1550                         if err != nil {
         1551                                 t.Fatal(err)
         1552                         }
         1553                         if err := v.WriteConfigAs(tc.fileName); err != nil {
         1554                                 t.Fatal(err)
         1555                         }
         1556 
         1557                         // The TOML String method does not order the contents.
         1558                         // Therefore, we must read the generated file and compare the data.
         1559                         v2 := New()
         1560                         v2.SetFs(fs)
         1561                         v2.SetConfigName(tc.configName)
         1562                         v2.SetConfigType(tc.configType)
         1563                         v2.SetConfigFile(tc.fileName)
         1564                         err = v2.ReadInConfig()
         1565                         if err != nil {
         1566                                 t.Fatal(err)
         1567                         }
         1568 
         1569                         assert.Equal(t, v.GetString("title"), v2.GetString("title"))
         1570                         assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio"))
         1571                         assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob"))
         1572                         assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization"))
         1573                 })
         1574         }
         1575 }
         1576 
         1577 func TestWriteConfigDotEnv(t *testing.T) {
         1578         fs := afero.NewMemMapFs()
         1579         testCases := map[string]struct {
         1580                 configName string
         1581                 configType string
         1582                 fileName   string
         1583                 input      []byte
         1584         }{
         1585                 "with file extension": {
         1586                         configName: "c",
         1587                         configType: "env",
         1588                         fileName:   "c.env",
         1589                         input:      dotenvExample,
         1590                 },
         1591                 "without file extension": {
         1592                         configName: "c",
         1593                         configType: "env",
         1594                         fileName:   "c",
         1595                         input:      dotenvExample,
         1596                 },
         1597         }
         1598         for name, tc := range testCases {
         1599                 t.Run(name, func(t *testing.T) {
         1600                         v := New()
         1601                         v.SetFs(fs)
         1602                         v.SetConfigName(tc.configName)
         1603                         v.SetConfigType(tc.configType)
         1604                         err := v.ReadConfig(bytes.NewBuffer(tc.input))
         1605                         if err != nil {
         1606                                 t.Fatal(err)
         1607                         }
         1608                         if err := v.WriteConfigAs(tc.fileName); err != nil {
         1609                                 t.Fatal(err)
         1610                         }
         1611 
         1612                         // The TOML String method does not order the contents.
         1613                         // Therefore, we must read the generated file and compare the data.
         1614                         v2 := New()
         1615                         v2.SetFs(fs)
         1616                         v2.SetConfigName(tc.configName)
         1617                         v2.SetConfigType(tc.configType)
         1618                         v2.SetConfigFile(tc.fileName)
         1619                         err = v2.ReadInConfig()
         1620                         if err != nil {
         1621                                 t.Fatal(err)
         1622                         }
         1623 
         1624                         assert.Equal(t, v.GetString("title_dotenv"), v2.GetString("title_dotenv"))
         1625                         assert.Equal(t, v.GetString("type_dotenv"), v2.GetString("type_dotenv"))
         1626                         assert.Equal(t, v.GetString("kind_dotenv"), v2.GetString("kind_dotenv"))
         1627                 })
         1628         }
         1629 }
         1630 
         1631 func TestSafeWriteConfig(t *testing.T) {
         1632         v := New()
         1633         fs := afero.NewMemMapFs()
         1634         v.SetFs(fs)
         1635         v.AddConfigPath("/test")
         1636         v.SetConfigName("c")
         1637         v.SetConfigType("yaml")
         1638         require.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)))
         1639         require.NoError(t, v.SafeWriteConfig())
         1640         read, err := afero.ReadFile(fs, "/test/c.yaml")
         1641         require.NoError(t, err)
         1642         assert.Equal(t, yamlWriteExpected, read)
         1643 }
         1644 
         1645 func TestSafeWriteConfigWithMissingConfigPath(t *testing.T) {
         1646         v := New()
         1647         fs := afero.NewMemMapFs()
         1648         v.SetFs(fs)
         1649         v.SetConfigName("c")
         1650         v.SetConfigType("yaml")
         1651         require.EqualError(t, v.SafeWriteConfig(), "missing configuration for 'configPath'")
         1652 }
         1653 
         1654 func TestSafeWriteConfigWithExistingFile(t *testing.T) {
         1655         v := New()
         1656         fs := afero.NewMemMapFs()
         1657         fs.Create("/test/c.yaml")
         1658         v.SetFs(fs)
         1659         v.AddConfigPath("/test")
         1660         v.SetConfigName("c")
         1661         v.SetConfigType("yaml")
         1662         err := v.SafeWriteConfig()
         1663         require.Error(t, err)
         1664         _, ok := err.(ConfigFileAlreadyExistsError)
         1665         assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
         1666 }
         1667 
         1668 func TestSafeWriteAsConfig(t *testing.T) {
         1669         v := New()
         1670         fs := afero.NewMemMapFs()
         1671         v.SetFs(fs)
         1672         err := v.ReadConfig(bytes.NewBuffer(yamlExample))
         1673         if err != nil {
         1674                 t.Fatal(err)
         1675         }
         1676         require.NoError(t, v.SafeWriteConfigAs("/test/c.yaml"))
         1677         if _, err = afero.ReadFile(fs, "/test/c.yaml"); err != nil {
         1678                 t.Fatal(err)
         1679         }
         1680 }
         1681 
         1682 func TestSafeWriteConfigAsWithExistingFile(t *testing.T) {
         1683         v := New()
         1684         fs := afero.NewMemMapFs()
         1685         fs.Create("/test/c.yaml")
         1686         v.SetFs(fs)
         1687         err := v.SafeWriteConfigAs("/test/c.yaml")
         1688         require.Error(t, err)
         1689         _, ok := err.(ConfigFileAlreadyExistsError)
         1690         assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
         1691 }
         1692 
         1693 var yamlMergeExampleTgt = []byte(`
         1694 hello:
         1695     pop: 37890
         1696     largenum: 765432101234567
         1697     num2pow63: 9223372036854775808
         1698     world:
         1699     - us
         1700     - uk
         1701     - fr
         1702     - de
         1703 `)
         1704 
         1705 var yamlMergeExampleSrc = []byte(`
         1706 hello:
         1707     pop: 45000
         1708     largenum: 7654321001234567
         1709     universe:
         1710     - mw
         1711     - ad
         1712     ints:
         1713     - 1
         1714     - 2
         1715 fu: bar
         1716 `)
         1717 
         1718 func TestMergeConfig(t *testing.T) {
         1719         v := New()
         1720         v.SetConfigType("yml")
         1721         if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
         1722                 t.Fatal(err)
         1723         }
         1724 
         1725         if pop := v.GetInt("hello.pop"); pop != 37890 {
         1726                 t.Fatalf("pop != 37890, = %d", pop)
         1727         }
         1728 
         1729         if pop := v.GetInt32("hello.pop"); pop != int32(37890) {
         1730                 t.Fatalf("pop != 37890, = %d", pop)
         1731         }
         1732 
         1733         if pop := v.GetInt64("hello.largenum"); pop != int64(765432101234567) {
         1734                 t.Fatalf("int64 largenum != 765432101234567, = %d", pop)
         1735         }
         1736 
         1737         if pop := v.GetUint("hello.pop"); pop != 37890 {
         1738                 t.Fatalf("uint pop != 37890, = %d", pop)
         1739         }
         1740 
         1741         if pop := v.GetUint32("hello.pop"); pop != 37890 {
         1742                 t.Fatalf("uint32 pop != 37890, = %d", pop)
         1743         }
         1744 
         1745         if pop := v.GetUint64("hello.num2pow63"); pop != 9223372036854775808 {
         1746                 t.Fatalf("uint64 num2pow63 != 9223372036854775808, = %d", pop)
         1747         }
         1748 
         1749         if world := v.GetStringSlice("hello.world"); len(world) != 4 {
         1750                 t.Fatalf("len(world) != 4, = %d", len(world))
         1751         }
         1752 
         1753         if fu := v.GetString("fu"); fu != "" {
         1754                 t.Fatalf("fu != \"\", = %s", fu)
         1755         }
         1756 
         1757         if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
         1758                 t.Fatal(err)
         1759         }
         1760 
         1761         if pop := v.GetInt("hello.pop"); pop != 45000 {
         1762                 t.Fatalf("pop != 45000, = %d", pop)
         1763         }
         1764 
         1765         if pop := v.GetInt32("hello.pop"); pop != int32(45000) {
         1766                 t.Fatalf("pop != 45000, = %d", pop)
         1767         }
         1768 
         1769         if pop := v.GetInt64("hello.largenum"); pop != int64(7654321001234567) {
         1770                 t.Fatalf("int64 largenum != 7654321001234567, = %d", pop)
         1771         }
         1772 
         1773         if world := v.GetStringSlice("hello.world"); len(world) != 4 {
         1774                 t.Fatalf("len(world) != 4, = %d", len(world))
         1775         }
         1776 
         1777         if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
         1778                 t.Fatalf("len(universe) != 2, = %d", len(universe))
         1779         }
         1780 
         1781         if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
         1782                 t.Fatalf("len(ints) != 2, = %d", len(ints))
         1783         }
         1784 
         1785         if fu := v.GetString("fu"); fu != "bar" {
         1786                 t.Fatalf("fu != \"bar\", = %s", fu)
         1787         }
         1788 }
         1789 
         1790 func TestMergeConfigNoMerge(t *testing.T) {
         1791         v := New()
         1792         v.SetConfigType("yml")
         1793         if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
         1794                 t.Fatal(err)
         1795         }
         1796 
         1797         if pop := v.GetInt("hello.pop"); pop != 37890 {
         1798                 t.Fatalf("pop != 37890, = %d", pop)
         1799         }
         1800 
         1801         if world := v.GetStringSlice("hello.world"); len(world) != 4 {
         1802                 t.Fatalf("len(world) != 4, = %d", len(world))
         1803         }
         1804 
         1805         if fu := v.GetString("fu"); fu != "" {
         1806                 t.Fatalf("fu != \"\", = %s", fu)
         1807         }
         1808 
         1809         if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
         1810                 t.Fatal(err)
         1811         }
         1812 
         1813         if pop := v.GetInt("hello.pop"); pop != 45000 {
         1814                 t.Fatalf("pop != 45000, = %d", pop)
         1815         }
         1816 
         1817         if world := v.GetStringSlice("hello.world"); len(world) != 0 {
         1818                 t.Fatalf("len(world) != 0, = %d", len(world))
         1819         }
         1820 
         1821         if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
         1822                 t.Fatalf("len(universe) != 2, = %d", len(universe))
         1823         }
         1824 
         1825         if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
         1826                 t.Fatalf("len(ints) != 2, = %d", len(ints))
         1827         }
         1828 
         1829         if fu := v.GetString("fu"); fu != "bar" {
         1830                 t.Fatalf("fu != \"bar\", = %s", fu)
         1831         }
         1832 }
         1833 
         1834 func TestMergeConfigMap(t *testing.T) {
         1835         v := New()
         1836         v.SetConfigType("yml")
         1837         if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
         1838                 t.Fatal(err)
         1839         }
         1840 
         1841         assert := func(i int) {
         1842                 large := v.GetInt64("hello.largenum")
         1843                 pop := v.GetInt("hello.pop")
         1844                 if large != 765432101234567 {
         1845                         t.Fatal("Got large num:", large)
         1846                 }
         1847 
         1848                 if pop != i {
         1849                         t.Fatal("Got pop:", pop)
         1850                 }
         1851         }
         1852 
         1853         assert(37890)
         1854 
         1855         update := map[string]interface{}{
         1856                 "Hello": map[string]interface{}{
         1857                         "Pop": 1234,
         1858                 },
         1859                 "World": map[interface{}]interface{}{
         1860                         "Rock": 345,
         1861                 },
         1862         }
         1863 
         1864         if err := v.MergeConfigMap(update); err != nil {
         1865                 t.Fatal(err)
         1866         }
         1867 
         1868         if rock := v.GetInt("world.rock"); rock != 345 {
         1869                 t.Fatal("Got rock:", rock)
         1870         }
         1871 
         1872         assert(1234)
         1873 }
         1874 
         1875 func TestUnmarshalingWithAliases(t *testing.T) {
         1876         v := New()
         1877         v.SetDefault("ID", 1)
         1878         v.Set("name", "Steve")
         1879         v.Set("lastname", "Owen")
         1880 
         1881         v.RegisterAlias("UserID", "ID")
         1882         v.RegisterAlias("Firstname", "name")
         1883         v.RegisterAlias("Surname", "lastname")
         1884 
         1885         type config struct {
         1886                 ID        int
         1887                 FirstName string
         1888                 Surname   string
         1889         }
         1890 
         1891         var C config
         1892         err := v.Unmarshal(&C)
         1893         if err != nil {
         1894                 t.Fatalf("unable to decode into struct, %v", err)
         1895         }
         1896 
         1897         assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C)
         1898 }
         1899 
         1900 func TestSetConfigNameClearsFileCache(t *testing.T) {
         1901         SetConfigFile("/tmp/config.yaml")
         1902         SetConfigName("default")
         1903         f, err := v.getConfigFile()
         1904         if err == nil {
         1905                 t.Fatalf("config file cache should have been cleared")
         1906         }
         1907         assert.Empty(t, f)
         1908 }
         1909 
         1910 func TestShadowedNestedValue(t *testing.T) {
         1911         config := `name: steve
         1912 clothing:
         1913   jacket: leather
         1914   trousers: denim
         1915   pants:
         1916     size: large
         1917 `
         1918         initConfig("yaml", config)
         1919 
         1920         assert.Equal(t, "steve", GetString("name"))
         1921 
         1922         polyester := "polyester"
         1923         SetDefault("clothing.shirt", polyester)
         1924         SetDefault("clothing.jacket.price", 100)
         1925 
         1926         assert.Equal(t, "leather", GetString("clothing.jacket"))
         1927         assert.Nil(t, Get("clothing.jacket.price"))
         1928         assert.Equal(t, polyester, GetString("clothing.shirt"))
         1929 
         1930         clothingSettings := AllSettings()["clothing"].(map[string]interface{})
         1931         assert.Equal(t, "leather", clothingSettings["jacket"])
         1932         assert.Equal(t, polyester, clothingSettings["shirt"])
         1933 }
         1934 
         1935 func TestDotParameter(t *testing.T) {
         1936         initJSON()
         1937         // shoud take precedence over batters defined in jsonExample
         1938         r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
         1939         unmarshalReader(r, v.config)
         1940 
         1941         actual := Get("batters.batter")
         1942         expected := []interface{}{map[string]interface{}{"type": "Small"}}
         1943         assert.Equal(t, expected, actual)
         1944 }
         1945 
         1946 func TestCaseInsensitive(t *testing.T) {
         1947         for _, config := range []struct {
         1948                 typ     string
         1949                 content string
         1950         }{
         1951                 {"yaml", `
         1952 aBcD: 1
         1953 eF:
         1954   gH: 2
         1955   iJk: 3
         1956   Lm:
         1957     nO: 4
         1958     P:
         1959       Q: 5
         1960       R: 6
         1961 `},
         1962                 {"json", `{
         1963   "aBcD": 1,
         1964   "eF": {
         1965     "iJk": 3,
         1966     "Lm": {
         1967       "P": {
         1968         "Q": 5,
         1969         "R": 6
         1970       },
         1971       "nO": 4
         1972     },
         1973     "gH": 2
         1974   }
         1975 }`},
         1976                 {"toml", `aBcD = 1
         1977 [eF]
         1978 gH = 2
         1979 iJk = 3
         1980 [eF.Lm]
         1981 nO = 4
         1982 [eF.Lm.P]
         1983 Q = 5
         1984 R = 6
         1985 `},
         1986         } {
         1987                 doTestCaseInsensitive(t, config.typ, config.content)
         1988         }
         1989 }
         1990 
         1991 func TestCaseInsensitiveSet(t *testing.T) {
         1992         Reset()
         1993         m1 := map[string]interface{}{
         1994                 "Foo": 32,
         1995                 "Bar": map[interface{}]interface {
         1996                 }{
         1997                         "ABc": "A",
         1998                         "cDE": "B",
         1999                 },
         2000         }
         2001 
         2002         m2 := map[string]interface{}{
         2003                 "Foo": 52,
         2004                 "Bar": map[interface{}]interface {
         2005                 }{
         2006                         "bCd": "A",
         2007                         "eFG": "B",
         2008                 },
         2009         }
         2010 
         2011         Set("Given1", m1)
         2012         Set("Number1", 42)
         2013 
         2014         SetDefault("Given2", m2)
         2015         SetDefault("Number2", 52)
         2016 
         2017         // Verify SetDefault
         2018         if v := Get("number2"); v != 52 {
         2019                 t.Fatalf("Expected 52 got %q", v)
         2020         }
         2021 
         2022         if v := Get("given2.foo"); v != 52 {
         2023                 t.Fatalf("Expected 52 got %q", v)
         2024         }
         2025 
         2026         if v := Get("given2.bar.bcd"); v != "A" {
         2027                 t.Fatalf("Expected A got %q", v)
         2028         }
         2029 
         2030         if _, ok := m2["Foo"]; !ok {
         2031                 t.Fatal("Input map changed")
         2032         }
         2033 
         2034         // Verify Set
         2035         if v := Get("number1"); v != 42 {
         2036                 t.Fatalf("Expected 42 got %q", v)
         2037         }
         2038 
         2039         if v := Get("given1.foo"); v != 32 {
         2040                 t.Fatalf("Expected 32 got %q", v)
         2041         }
         2042 
         2043         if v := Get("given1.bar.abc"); v != "A" {
         2044                 t.Fatalf("Expected A got %q", v)
         2045         }
         2046 
         2047         if _, ok := m1["Foo"]; !ok {
         2048                 t.Fatal("Input map changed")
         2049         }
         2050 }
         2051 
         2052 func TestParseNested(t *testing.T) {
         2053         type duration struct {
         2054                 Delay time.Duration
         2055         }
         2056 
         2057         type item struct {
         2058                 Name   string
         2059                 Delay  time.Duration
         2060                 Nested duration
         2061         }
         2062 
         2063         config := `[[parent]]
         2064         delay="100ms"
         2065         [parent.nested]
         2066         delay="200ms"
         2067 `
         2068         initConfig("toml", config)
         2069 
         2070         var items []item
         2071         err := v.UnmarshalKey("parent", &items)
         2072         if err != nil {
         2073                 t.Fatalf("unable to decode into struct, %v", err)
         2074         }
         2075 
         2076         assert.Equal(t, 1, len(items))
         2077         assert.Equal(t, 100*time.Millisecond, items[0].Delay)
         2078         assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
         2079 }
         2080 
         2081 func doTestCaseInsensitive(t *testing.T, typ, config string) {
         2082         initConfig(typ, config)
         2083         Set("RfD", true)
         2084         assert.Equal(t, true, Get("rfd"))
         2085         assert.Equal(t, true, Get("rFD"))
         2086         assert.Equal(t, 1, cast.ToInt(Get("abcd")))
         2087         assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
         2088         assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
         2089         assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
         2090         assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
         2091         assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
         2092 }
         2093 
         2094 func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) {
         2095         watchDir, err := ioutil.TempDir("", "")
         2096         require.Nil(t, err)
         2097         configFile := path.Join(watchDir, "config.yaml")
         2098         err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640)
         2099         require.Nil(t, err)
         2100         cleanup := func() {
         2101                 os.RemoveAll(watchDir)
         2102         }
         2103         v := New()
         2104         v.SetConfigFile(configFile)
         2105         err = v.ReadInConfig()
         2106         require.Nil(t, err)
         2107         require.Equal(t, "bar", v.Get("foo"))
         2108         return v, configFile, cleanup
         2109 }
         2110 
         2111 func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) {
         2112         watchDir, err := ioutil.TempDir("", "")
         2113         require.Nil(t, err)
         2114         dataDir1 := path.Join(watchDir, "data1")
         2115         err = os.Mkdir(dataDir1, 0777)
         2116         require.Nil(t, err)
         2117         realConfigFile := path.Join(dataDir1, "config.yaml")
         2118         t.Logf("Real config file location: %s\n", realConfigFile)
         2119         err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640)
         2120         require.Nil(t, err)
         2121         cleanup := func() {
         2122                 os.RemoveAll(watchDir)
         2123         }
         2124         // now, symlink the tm `data1` dir to `data` in the baseDir
         2125         os.Symlink(dataDir1, path.Join(watchDir, "data"))
         2126         // and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`
         2127         configFile := path.Join(watchDir, "config.yaml")
         2128         os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile)
         2129         t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml"))
         2130         // init Viper
         2131         v := New()
         2132         v.SetConfigFile(configFile)
         2133         err = v.ReadInConfig()
         2134         require.Nil(t, err)
         2135         require.Equal(t, "bar", v.Get("foo"))
         2136         return v, watchDir, configFile, cleanup
         2137 }
         2138 
         2139 func TestWatchFile(t *testing.T) {
         2140         if runtime.GOOS == "linux" {
         2141                 // TODO(bep) FIX ME
         2142                 t.Skip("Skip test on Linux ...")
         2143         }
         2144 
         2145         t.Run("file content changed", func(t *testing.T) {
         2146                 // given a `config.yaml` file being watched
         2147                 v, configFile, cleanup := newViperWithConfigFile(t)
         2148                 defer cleanup()
         2149                 _, err := os.Stat(configFile)
         2150                 require.NoError(t, err)
         2151                 t.Logf("test config file: %s\n", configFile)
         2152                 wg := sync.WaitGroup{}
         2153                 wg.Add(1)
         2154                 v.OnConfigChange(func(in fsnotify.Event) {
         2155                         t.Logf("config file changed")
         2156                         wg.Done()
         2157                 })
         2158                 v.WatchConfig()
         2159                 // when overwriting the file and waiting for the custom change notification handler to be triggered
         2160                 err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640)
         2161                 wg.Wait()
         2162                 // then the config value should have changed
         2163                 require.Nil(t, err)
         2164                 assert.Equal(t, "baz", v.Get("foo"))
         2165         })
         2166 
         2167         t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) {
         2168                 // skip if not executed on Linux
         2169                 if runtime.GOOS != "linux" {
         2170                         t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
         2171                 }
         2172                 v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t)
         2173                 // defer cleanup()
         2174                 wg := sync.WaitGroup{}
         2175                 v.WatchConfig()
         2176                 v.OnConfigChange(func(in fsnotify.Event) {
         2177                         t.Logf("config file changed")
         2178                         wg.Done()
         2179                 })
         2180                 wg.Add(1)
         2181                 // when link to another `config.yaml` file
         2182                 dataDir2 := path.Join(watchDir, "data2")
         2183                 err := os.Mkdir(dataDir2, 0777)
         2184                 require.Nil(t, err)
         2185                 configFile2 := path.Join(dataDir2, "config.yaml")
         2186                 err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640)
         2187                 require.Nil(t, err)
         2188                 // change the symlink using the `ln -sfn` command
         2189                 err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
         2190                 require.Nil(t, err)
         2191                 wg.Wait()
         2192                 // then
         2193                 require.Nil(t, err)
         2194                 assert.Equal(t, "baz", v.Get("foo"))
         2195         })
         2196 }
         2197 
         2198 func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
         2199         flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
         2200         flags.String("foo.bar", "cobra_flag", "")
         2201 
         2202         v := New()
         2203         assert.NoError(t, v.BindPFlags(flags))
         2204 
         2205         config := &struct {
         2206                 Foo struct {
         2207                         Bar string
         2208                 }
         2209         }{}
         2210 
         2211         assert.NoError(t, v.Unmarshal(config))
         2212         assert.Equal(t, "cobra_flag", config.Foo.Bar)
         2213 }
         2214 
         2215 var yamlExampleWithDot = []byte(`Hacker: true
         2216 name: steve
         2217 hobbies:
         2218   - skateboarding
         2219   - snowboarding
         2220   - go
         2221 clothing:
         2222   jacket: leather
         2223   trousers: denim
         2224   pants:
         2225     size: large
         2226 age: 35
         2227 eyes : brown
         2228 beard: true
         2229 emails:
         2230   steve@hacker.com:
         2231     created: 01/02/03
         2232     active: true
         2233 `)
         2234 
         2235 func TestKeyDelimiter(t *testing.T) {
         2236         v := NewWithOptions(KeyDelimiter("::"))
         2237         v.SetConfigType("yaml")
         2238         r := strings.NewReader(string(yamlExampleWithDot))
         2239 
         2240         err := v.unmarshalReader(r, v.config)
         2241         require.NoError(t, err)
         2242 
         2243         values := map[string]interface{}{
         2244                 "image": map[string]interface{}{
         2245                         "repository": "someImage",
         2246                         "tag":        "1.0.0",
         2247                 },
         2248                 "ingress": map[string]interface{}{
         2249                         "annotations": map[string]interface{}{
         2250                                 "traefik.frontend.rule.type":                 "PathPrefix",
         2251                                 "traefik.ingress.kubernetes.io/ssl-redirect": "true",
         2252                         },
         2253                 },
         2254         }
         2255 
         2256         v.SetDefault("charts::values", values)
         2257 
         2258         assert.Equal(t, "leather", v.GetString("clothing::jacket"))
         2259         assert.Equal(t, "01/02/03", v.GetString("emails::steve@hacker.com::created"))
         2260 
         2261         type config struct {
         2262                 Charts struct {
         2263                         Values map[string]interface{}
         2264                 }
         2265         }
         2266 
         2267         expected := config{
         2268                 Charts: struct {
         2269                         Values map[string]interface{}
         2270                 }{
         2271                         Values: values,
         2272                 },
         2273         }
         2274 
         2275         var actual config
         2276 
         2277         assert.NoError(t, v.Unmarshal(&actual))
         2278 
         2279         assert.Equal(t, expected, actual)
         2280 }
         2281 
         2282 var yamlDeepNestedSlices = []byte(`TV:
         2283 - title: "The expanse"
         2284   seasons:
         2285   - first_released: "December 14, 2015"
         2286     episodes:
         2287     - title: "Dulcinea"
         2288       air_date: "December 14, 2015"
         2289     - title: "The Big Empty"
         2290       air_date: "December 15, 2015"
         2291     - title: "Remember the Cant"
         2292       air_date: "December 22, 2015"
         2293   - first_released: "February 1, 2017"
         2294     episodes:
         2295     - title: "Safe"
         2296       air_date: "February 1, 2017"
         2297     - title: "Doors & Corners"
         2298       air_date: "February 1, 2017"
         2299     - title: "Static"
         2300       air_date: "February 8, 2017"
         2301   episodes:
         2302     - ["Dulcinea", "The Big Empty", "Remember the Cant"]
         2303     - ["Safe", "Doors & Corners", "Static"]
         2304 `)
         2305 
         2306 func TestSliceIndexAccess(t *testing.T) {
         2307         v.SetConfigType("yaml")
         2308         r := strings.NewReader(string(yamlDeepNestedSlices))
         2309 
         2310         err := v.unmarshalReader(r, v.config)
         2311         require.NoError(t, err)
         2312 
         2313         assert.Equal(t, "The expanse", v.GetString("tv.0.title"))
         2314         assert.Equal(t, "February 1, 2017", v.GetString("tv.0.seasons.1.first_released"))
         2315         assert.Equal(t, "Static", v.GetString("tv.0.seasons.1.episodes.2.title"))
         2316         assert.Equal(t, "December 15, 2015", v.GetString("tv.0.seasons.0.episodes.1.air_date"))
         2317 
         2318         // Test for index out of bounds
         2319         assert.Equal(t, "", v.GetString("tv.0.seasons.2.first_released"))
         2320 
         2321         // Accessing multidimensional arrays
         2322         assert.Equal(t, "Static", v.GetString("tv.0.episodes.1.2"))
         2323 }
         2324 
         2325 func BenchmarkGetBool(b *testing.B) {
         2326         key := "BenchmarkGetBool"
         2327         v = New()
         2328         v.Set(key, true)
         2329 
         2330         for i := 0; i < b.N; i++ {
         2331                 if !v.GetBool(key) {
         2332                         b.Fatal("GetBool returned false")
         2333                 }
         2334         }
         2335 }
         2336 
         2337 func BenchmarkGet(b *testing.B) {
         2338         key := "BenchmarkGet"
         2339         v = New()
         2340         v.Set(key, true)
         2341 
         2342         for i := 0; i < b.N; i++ {
         2343                 if !v.Get(key).(bool) {
         2344                         b.Fatal("Get returned false")
         2345                 }
         2346         }
         2347 }
         2348 
         2349 // BenchmarkGetBoolFromMap is the "perfect result" for the above.
         2350 func BenchmarkGetBoolFromMap(b *testing.B) {
         2351         m := make(map[string]bool)
         2352         key := "BenchmarkGetBool"
         2353         m[key] = true
         2354 
         2355         for i := 0; i < b.N; i++ {
         2356                 if !m[key] {
         2357                         b.Fatal("Map value was false")
         2358                 }
         2359         }
         2360 }