1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161 |
- // Copyright © 2014 Steve Francia <spf@spf13.com>.
- //
- // Use of this source code is governed by an MIT-style
- // license that can be found in the LICENSE file.
- package viper
- import (
- "bytes"
- "encoding/json"
- "io"
- "io/ioutil"
- "os"
- "os/exec"
- "path"
- "path/filepath"
- "reflect"
- "runtime"
- "sort"
- "strings"
- "sync"
- "testing"
- "time"
- "github.com/fsnotify/fsnotify"
- "github.com/mitchellh/mapstructure"
- "github.com/spf13/afero"
- "github.com/spf13/cast"
- "github.com/spf13/pflag"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- var yamlExample = []byte(`Hacker: true
- name: steve
- hobbies:
- - skateboarding
- - snowboarding
- - go
- clothing:
- jacket: leather
- trousers: denim
- pants:
- size: large
- age: 35
- eyes : brown
- beard: true
- `)
- var yamlExampleWithExtras = []byte(`Existing: true
- Bogus: true
- `)
- type testUnmarshalExtra struct {
- Existing bool
- }
- var tomlExample = []byte(`
- title = "TOML Example"
- [owner]
- organization = "MongoDB"
- Bio = "MongoDB Chief Developer Advocate & Hacker at Large"
- dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)
- var dotenvExample = []byte(`
- TITLE_DOTENV="DotEnv Example"
- TYPE_DOTENV=donut
- NAME_DOTENV=Cake`)
- var jsonExample = []byte(`{
- "id": "0001",
- "type": "donut",
- "name": "Cake",
- "ppu": 0.55,
- "batters": {
- "batter": [
- { "type": "Regular" },
- { "type": "Chocolate" },
- { "type": "Blueberry" },
- { "type": "Devil's Food" }
- ]
- }
- }`)
- var hclExample = []byte(`
- id = "0001"
- type = "donut"
- name = "Cake"
- ppu = 0.55
- foos {
- foo {
- key = 1
- }
- foo {
- key = 2
- }
- foo {
- key = 3
- }
- foo {
- key = 4
- }
- }`)
- var propertiesExample = []byte(`
- p_id: 0001
- p_type: donut
- p_name: Cake
- p_ppu: 0.55
- p_batters.batter.type: Regular
- `)
- var remoteExample = []byte(`{
- "id":"0002",
- "type":"cronut",
- "newkey":"remote"
- }`)
- var iniExample = []byte(`; Package name
- NAME = ini
- ; Package version
- VERSION = v1
- ; Package import path
- IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
- # Information about package author
- # Bio can be written in multiple lines.
- [author]
- NAME = Unknown ; Succeeding comment
- E-MAIL = fake@localhost
- GITHUB = https://github.com/%(NAME)s
- BIO = """Gopher.
- Coding addict.
- Good man.
- """ # Succeeding comment`)
- func initConfigs() {
- Reset()
- var r io.Reader
- SetConfigType("yaml")
- r = bytes.NewReader(yamlExample)
- unmarshalReader(r, v.config)
- SetConfigType("json")
- r = bytes.NewReader(jsonExample)
- unmarshalReader(r, v.config)
- SetConfigType("hcl")
- r = bytes.NewReader(hclExample)
- unmarshalReader(r, v.config)
- SetConfigType("properties")
- r = bytes.NewReader(propertiesExample)
- unmarshalReader(r, v.config)
- SetConfigType("toml")
- r = bytes.NewReader(tomlExample)
- unmarshalReader(r, v.config)
- SetConfigType("env")
- r = bytes.NewReader(dotenvExample)
- unmarshalReader(r, v.config)
- SetConfigType("json")
- remote := bytes.NewReader(remoteExample)
- unmarshalReader(remote, v.kvstore)
- SetConfigType("ini")
- r = bytes.NewReader(iniExample)
- unmarshalReader(r, v.config)
- }
- func initConfig(typ, config string) {
- Reset()
- SetConfigType(typ)
- r := strings.NewReader(config)
- if err := unmarshalReader(r, v.config); err != nil {
- panic(err)
- }
- }
- func initYAML() {
- initConfig("yaml", string(yamlExample))
- }
- func initJSON() {
- Reset()
- SetConfigType("json")
- r := bytes.NewReader(jsonExample)
- unmarshalReader(r, v.config)
- }
- func initProperties() {
- Reset()
- SetConfigType("properties")
- r := bytes.NewReader(propertiesExample)
- unmarshalReader(r, v.config)
- }
- func initTOML() {
- Reset()
- SetConfigType("toml")
- r := bytes.NewReader(tomlExample)
- unmarshalReader(r, v.config)
- }
- func initDotEnv() {
- Reset()
- SetConfigType("env")
- r := bytes.NewReader(dotenvExample)
- unmarshalReader(r, v.config)
- }
- func initHcl() {
- Reset()
- SetConfigType("hcl")
- r := bytes.NewReader(hclExample)
- unmarshalReader(r, v.config)
- }
- func initIni() {
- Reset()
- SetConfigType("ini")
- r := bytes.NewReader(iniExample)
- unmarshalReader(r, v.config)
- }
- // make directories for testing
- func initDirs(t *testing.T) (string, string, func()) {
- var (
- testDirs = []string{`a a`, `b`, `C_`}
- config = `improbable`
- )
- if runtime.GOOS != "windows" {
- testDirs = append(testDirs, `d\d`)
- }
- root, err := ioutil.TempDir("", "")
- require.NoError(t, err, "Failed to create temporary directory")
- cleanup := true
- defer func() {
- if cleanup {
- os.Chdir("..")
- os.RemoveAll(root)
- }
- }()
- assert.Nil(t, err)
- err = os.Chdir(root)
- require.Nil(t, err)
- for _, dir := range testDirs {
- err = os.Mkdir(dir, 0750)
- assert.Nil(t, err)
- err = ioutil.WriteFile(
- path.Join(dir, config+".toml"),
- []byte("key = \"value is "+dir+"\"\n"),
- 0640)
- assert.Nil(t, err)
- }
- cleanup = false
- return root, config, func() {
- os.Chdir("..")
- os.RemoveAll(root)
- }
- }
- // stubs for PFlag Values
- type stringValue string
- func newStringValue(val string, p *string) *stringValue {
- *p = val
- return (*stringValue)(p)
- }
- func (s *stringValue) Set(val string) error {
- *s = stringValue(val)
- return nil
- }
- func (s *stringValue) Type() string {
- return "string"
- }
- func (s *stringValue) String() string {
- return string(*s)
- }
- func TestBasics(t *testing.T) {
- SetConfigFile("/tmp/config.yaml")
- filename, err := v.getConfigFile()
- assert.Equal(t, "/tmp/config.yaml", filename)
- assert.NoError(t, err)
- }
- func TestSearchInPath_WithoutConfigTypeSet(t *testing.T) {
- filename := ".dotfilenoext"
- path := "/tmp"
- file := filepath.Join(path, filename)
- SetConfigName(filename)
- AddConfigPath(path)
- _, createErr := v.fs.Create(file)
- defer func() {
- _ = v.fs.Remove(file)
- }()
- assert.NoError(t, createErr)
- _, err := v.getConfigFile()
- // unless config type is set, files without extension
- // are not considered
- assert.Error(t, err)
- }
- func TestSearchInPath(t *testing.T) {
- filename := ".dotfilenoext"
- path := "/tmp"
- file := filepath.Join(path, filename)
- SetConfigName(filename)
- SetConfigType("yaml")
- AddConfigPath(path)
- _, createErr := v.fs.Create(file)
- defer func() {
- _ = v.fs.Remove(file)
- }()
- assert.NoError(t, createErr)
- filename, err := v.getConfigFile()
- assert.Equal(t, file, filename)
- assert.NoError(t, err)
- }
- func TestSearchInPath_FilesOnly(t *testing.T) {
- fs := afero.NewMemMapFs()
- err := fs.Mkdir("/tmp/config", 0777)
- require.NoError(t, err)
- _, err = fs.Create("/tmp/config/config.yaml")
- require.NoError(t, err)
- v := New()
- v.SetFs(fs)
- v.AddConfigPath("/tmp")
- v.AddConfigPath("/tmp/config")
- filename, err := v.getConfigFile()
- assert.Equal(t, "/tmp/config/config.yaml", filename)
- assert.NoError(t, err)
- }
- func TestDefault(t *testing.T) {
- SetDefault("age", 45)
- assert.Equal(t, 45, Get("age"))
- SetDefault("clothing.jacket", "slacks")
- assert.Equal(t, "slacks", Get("clothing.jacket"))
- SetConfigType("yaml")
- err := ReadConfig(bytes.NewBuffer(yamlExample))
- assert.NoError(t, err)
- assert.Equal(t, "leather", Get("clothing.jacket"))
- }
- func TestUnmarshaling(t *testing.T) {
- SetConfigType("yaml")
- r := bytes.NewReader(yamlExample)
- unmarshalReader(r, v.config)
- assert.True(t, InConfig("name"))
- assert.False(t, InConfig("state"))
- assert.Equal(t, "steve", Get("name"))
- assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
- assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
- assert.Equal(t, 35, Get("age"))
- }
- func TestUnmarshalExact(t *testing.T) {
- vip := New()
- target := &testUnmarshalExtra{}
- vip.SetConfigType("yaml")
- r := bytes.NewReader(yamlExampleWithExtras)
- vip.ReadConfig(r)
- err := vip.UnmarshalExact(target)
- if err == nil {
- t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields")
- }
- }
- func TestOverrides(t *testing.T) {
- Set("age", 40)
- assert.Equal(t, 40, Get("age"))
- }
- func TestDefaultPost(t *testing.T) {
- assert.NotEqual(t, "NYC", Get("state"))
- SetDefault("state", "NYC")
- assert.Equal(t, "NYC", Get("state"))
- }
- func TestAliases(t *testing.T) {
- RegisterAlias("years", "age")
- assert.Equal(t, 40, Get("years"))
- Set("years", 45)
- assert.Equal(t, 45, Get("age"))
- }
- func TestAliasInConfigFile(t *testing.T) {
- // the config file specifies "beard". If we make this an alias for
- // "hasbeard", we still want the old config file to work with beard.
- RegisterAlias("beard", "hasbeard")
- assert.Equal(t, true, Get("hasbeard"))
- Set("hasbeard", false)
- assert.Equal(t, false, Get("beard"))
- }
- func TestYML(t *testing.T) {
- initYAML()
- assert.Equal(t, "steve", Get("name"))
- }
- func TestJSON(t *testing.T) {
- initJSON()
- assert.Equal(t, "0001", Get("id"))
- }
- func TestProperties(t *testing.T) {
- initProperties()
- assert.Equal(t, "0001", Get("p_id"))
- }
- func TestTOML(t *testing.T) {
- initTOML()
- assert.Equal(t, "TOML Example", Get("title"))
- }
- func TestDotEnv(t *testing.T) {
- initDotEnv()
- assert.Equal(t, "DotEnv Example", Get("title_dotenv"))
- }
- func TestHCL(t *testing.T) {
- initHcl()
- assert.Equal(t, "0001", Get("id"))
- assert.Equal(t, 0.55, Get("ppu"))
- assert.Equal(t, "donut", Get("type"))
- assert.Equal(t, "Cake", Get("name"))
- Set("id", "0002")
- assert.Equal(t, "0002", Get("id"))
- assert.NotEqual(t, "cronut", Get("type"))
- }
- func TestIni(t *testing.T) {
- initIni()
- assert.Equal(t, "ini", Get("default.name"))
- }
- func TestRemotePrecedence(t *testing.T) {
- initJSON()
- remote := bytes.NewReader(remoteExample)
- assert.Equal(t, "0001", Get("id"))
- unmarshalReader(remote, v.kvstore)
- assert.Equal(t, "0001", Get("id"))
- assert.NotEqual(t, "cronut", Get("type"))
- assert.Equal(t, "remote", Get("newkey"))
- Set("newkey", "newvalue")
- assert.NotEqual(t, "remote", Get("newkey"))
- assert.Equal(t, "newvalue", Get("newkey"))
- Set("newkey", "remote")
- }
- func TestEnv(t *testing.T) {
- initJSON()
- BindEnv("id")
- BindEnv("f", "FOOD")
- os.Setenv("ID", "13")
- os.Setenv("FOOD", "apple")
- os.Setenv("NAME", "crunk")
- assert.Equal(t, "13", Get("id"))
- assert.Equal(t, "apple", Get("f"))
- assert.Equal(t, "Cake", Get("name"))
- AutomaticEnv()
- assert.Equal(t, "crunk", Get("name"))
- }
- func TestEmptyEnv(t *testing.T) {
- initJSON()
- BindEnv("type") // Empty environment variable
- BindEnv("name") // Bound, but not set environment variable
- os.Unsetenv("type")
- os.Unsetenv("TYPE")
- os.Unsetenv("name")
- os.Unsetenv("NAME")
- os.Setenv("TYPE", "")
- assert.Equal(t, "donut", Get("type"))
- assert.Equal(t, "Cake", Get("name"))
- }
- func TestEmptyEnv_Allowed(t *testing.T) {
- initJSON()
- AllowEmptyEnv(true)
- BindEnv("type") // Empty environment variable
- BindEnv("name") // Bound, but not set environment variable
- os.Unsetenv("type")
- os.Unsetenv("TYPE")
- os.Unsetenv("name")
- os.Unsetenv("NAME")
- os.Setenv("TYPE", "")
- assert.Equal(t, "", Get("type"))
- assert.Equal(t, "Cake", Get("name"))
- }
- func TestEnvPrefix(t *testing.T) {
- initJSON()
- SetEnvPrefix("foo") // will be uppercased automatically
- BindEnv("id")
- BindEnv("f", "FOOD") // not using prefix
- os.Setenv("FOO_ID", "13")
- os.Setenv("FOOD", "apple")
- os.Setenv("FOO_NAME", "crunk")
- assert.Equal(t, "13", Get("id"))
- assert.Equal(t, "apple", Get("f"))
- assert.Equal(t, "Cake", Get("name"))
- AutomaticEnv()
- assert.Equal(t, "crunk", Get("name"))
- }
- func TestAutoEnv(t *testing.T) {
- Reset()
- AutomaticEnv()
- os.Setenv("FOO_BAR", "13")
- assert.Equal(t, "13", Get("foo_bar"))
- }
- func TestAutoEnvWithPrefix(t *testing.T) {
- Reset()
- AutomaticEnv()
- SetEnvPrefix("Baz")
- os.Setenv("BAZ_BAR", "13")
- assert.Equal(t, "13", Get("bar"))
- }
- func TestSetEnvKeyReplacer(t *testing.T) {
- Reset()
- AutomaticEnv()
- os.Setenv("REFRESH_INTERVAL", "30s")
- replacer := strings.NewReplacer("-", "_")
- SetEnvKeyReplacer(replacer)
- assert.Equal(t, "30s", Get("refresh-interval"))
- }
- func TestEnvKeyReplacer(t *testing.T) {
- v := NewWithOptions(EnvKeyReplacer(strings.NewReplacer("-", "_")))
- v.AutomaticEnv()
- _ = os.Setenv("REFRESH_INTERVAL", "30s")
- assert.Equal(t, "30s", v.Get("refresh-interval"))
- }
- func TestAllKeys(t *testing.T) {
- initConfigs()
- ks := sort.StringSlice{
- "title",
- "author.bio",
- "author.e-mail",
- "author.github",
- "author.name",
- "newkey",
- "owner.organization",
- "owner.dob",
- "owner.bio",
- "name",
- "beard",
- "ppu",
- "batters.batter",
- "hobbies",
- "clothing.jacket",
- "clothing.trousers",
- "default.import_path",
- "default.name",
- "default.version",
- "clothing.pants.size",
- "age",
- "hacker",
- "id",
- "type",
- "eyes",
- "p_id",
- "p_ppu",
- "p_batters.batter.type",
- "p_type",
- "p_name",
- "foos",
- "title_dotenv",
- "type_dotenv",
- "name_dotenv",
- }
- dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
- all := map[string]interface{}{
- "owner": map[string]interface{}{
- "organization": "MongoDB",
- "bio": "MongoDB Chief Developer Advocate & Hacker at Large",
- "dob": dob,
- },
- "title": "TOML Example",
- "author": map[string]interface{}{
- "e-mail": "fake@localhost",
- "github": "https://github.com/Unknown",
- "name": "Unknown",
- "bio": "Gopher.\nCoding addict.\nGood man.\n",
- },
- "ppu": 0.55,
- "eyes": "brown",
- "clothing": map[string]interface{}{
- "trousers": "denim",
- "jacket": "leather",
- "pants": map[string]interface{}{"size": "large"},
- },
- "default": map[string]interface{}{
- "import_path": "gopkg.in/ini.v1",
- "name": "ini",
- "version": "v1",
- },
- "id": "0001",
- "batters": map[string]interface{}{
- "batter": []interface{}{
- map[string]interface{}{"type": "Regular"},
- map[string]interface{}{"type": "Chocolate"},
- map[string]interface{}{"type": "Blueberry"},
- map[string]interface{}{"type": "Devil's Food"},
- },
- },
- "hacker": true,
- "beard": true,
- "hobbies": []interface{}{
- "skateboarding",
- "snowboarding",
- "go",
- },
- "age": 35,
- "type": "donut",
- "newkey": "remote",
- "name": "Cake",
- "p_id": "0001",
- "p_ppu": "0.55",
- "p_name": "Cake",
- "p_batters": map[string]interface{}{
- "batter": map[string]interface{}{"type": "Regular"},
- },
- "p_type": "donut",
- "foos": []map[string]interface{}{
- {
- "foo": []map[string]interface{}{
- {"key": 1},
- {"key": 2},
- {"key": 3},
- {"key": 4}},
- },
- },
- "title_dotenv": "DotEnv Example",
- "type_dotenv": "donut",
- "name_dotenv": "Cake",
- }
- allkeys := sort.StringSlice(AllKeys())
- allkeys.Sort()
- ks.Sort()
- assert.Equal(t, ks, allkeys)
- assert.Equal(t, all, AllSettings())
- }
- func TestAllKeysWithEnv(t *testing.T) {
- v := New()
- // bind and define environment variables (including a nested one)
- v.BindEnv("id")
- v.BindEnv("foo.bar")
- v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
- os.Setenv("ID", "13")
- os.Setenv("FOO_BAR", "baz")
- expectedKeys := sort.StringSlice{"id", "foo.bar"}
- expectedKeys.Sort()
- keys := sort.StringSlice(v.AllKeys())
- keys.Sort()
- assert.Equal(t, expectedKeys, keys)
- }
- func TestAliasesOfAliases(t *testing.T) {
- Set("Title", "Checking Case")
- RegisterAlias("Foo", "Bar")
- RegisterAlias("Bar", "Title")
- assert.Equal(t, "Checking Case", Get("FOO"))
- }
- func TestRecursiveAliases(t *testing.T) {
- RegisterAlias("Baz", "Roo")
- RegisterAlias("Roo", "baz")
- }
- func TestUnmarshal(t *testing.T) {
- SetDefault("port", 1313)
- Set("name", "Steve")
- Set("duration", "1s1ms")
- Set("modes", []int{1, 2, 3})
- type config struct {
- Port int
- Name string
- Duration time.Duration
- Modes []int
- }
- var C config
- err := Unmarshal(&C)
- if err != nil {
- t.Fatalf("unable to decode into struct, %v", err)
- }
- assert.Equal(
- t,
- &config{
- Name: "Steve",
- Port: 1313,
- Duration: time.Second + time.Millisecond,
- Modes: []int{1, 2, 3},
- },
- &C,
- )
- Set("port", 1234)
- err = Unmarshal(&C)
- if err != nil {
- t.Fatalf("unable to decode into struct, %v", err)
- }
- assert.Equal(
- t,
- &config{
- Name: "Steve",
- Port: 1234,
- Duration: time.Second + time.Millisecond,
- Modes: []int{1, 2, 3},
- },
- &C,
- )
- }
- func TestUnmarshalWithDecoderOptions(t *testing.T) {
- Set("credentials", "{\"foo\":\"bar\"}")
- opt := DecodeHook(mapstructure.ComposeDecodeHookFunc(
- mapstructure.StringToTimeDurationHookFunc(),
- mapstructure.StringToSliceHookFunc(","),
- // Custom Decode Hook Function
- func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) {
- if rf != reflect.String || rt != reflect.Map {
- return data, nil
- }
- m := map[string]string{}
- raw := data.(string)
- if raw == "" {
- return m, nil
- }
- return m, json.Unmarshal([]byte(raw), &m)
- },
- ))
- type config struct {
- Credentials map[string]string
- }
- var C config
- err := Unmarshal(&C, opt)
- if err != nil {
- t.Fatalf("unable to decode into struct, %v", err)
- }
- assert.Equal(t, &config{
- Credentials: map[string]string{"foo": "bar"},
- }, &C)
- }
- func TestBindPFlags(t *testing.T) {
- v := New() // create independent Viper object
- flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
- var testValues = map[string]*string{
- "host": nil,
- "port": nil,
- "endpoint": nil,
- }
- var mutatedTestValues = map[string]string{
- "host": "localhost",
- "port": "6060",
- "endpoint": "/public",
- }
- for name := range testValues {
- testValues[name] = flagSet.String(name, "", "test")
- }
- err := v.BindPFlags(flagSet)
- if err != nil {
- t.Fatalf("error binding flag set, %v", err)
- }
- flagSet.VisitAll(func(flag *pflag.Flag) {
- flag.Value.Set(mutatedTestValues[flag.Name])
- flag.Changed = true
- })
- for name, expected := range mutatedTestValues {
- assert.Equal(t, expected, v.Get(name))
- }
- }
- func TestBindPFlagsStringSlice(t *testing.T) {
- tests := []struct {
- Expected []string
- Value string
- }{
- {nil, ""},
- {[]string{"jeden"}, "jeden"},
- {[]string{"dwa", "trzy"}, "dwa,trzy"},
- {[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""},
- }
- v := New() // create independent Viper object
- defaultVal := []string{"default"}
- v.SetDefault("stringslice", defaultVal)
- for _, testValue := range tests {
- flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
- flagSet.StringSlice("stringslice", testValue.Expected, "test")
- for _, changed := range []bool{true, false} {
- flagSet.VisitAll(func(f *pflag.Flag) {
- f.Value.Set(testValue.Value)
- f.Changed = changed
- })
- err := v.BindPFlags(flagSet)
- if err != nil {
- t.Fatalf("error binding flag set, %v", err)
- }
- type TestStr struct {
- StringSlice []string
- }
- val := &TestStr{}
- if err := v.Unmarshal(val); err != nil {
- t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
- }
- if changed {
- assert.Equal(t, testValue.Expected, val.StringSlice)
- } else {
- assert.Equal(t, defaultVal, val.StringSlice)
- }
- }
- }
- }
- func TestBindPFlagsIntSlice(t *testing.T) {
- tests := []struct {
- Expected []int
- Value string
- }{
- {nil, ""},
- {[]int{1}, "1"},
- {[]int{2, 3}, "2,3"},
- }
- v := New() // create independent Viper object
- defaultVal := []int{0}
- v.SetDefault("intslice", defaultVal)
- for _, testValue := range tests {
- flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
- flagSet.IntSlice("intslice", testValue.Expected, "test")
- for _, changed := range []bool{true, false} {
- flagSet.VisitAll(func(f *pflag.Flag) {
- f.Value.Set(testValue.Value)
- f.Changed = changed
- })
- err := v.BindPFlags(flagSet)
- if err != nil {
- t.Fatalf("error binding flag set, %v", err)
- }
- type TestInt struct {
- IntSlice []int
- }
- val := &TestInt{}
- if err := v.Unmarshal(val); err != nil {
- t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
- }
- if changed {
- assert.Equal(t, testValue.Expected, val.IntSlice)
- } else {
- assert.Equal(t, defaultVal, val.IntSlice)
- }
- }
- }
- }
- func TestBindPFlag(t *testing.T) {
- var testString = "testing"
- var testValue = newStringValue(testString, &testString)
- flag := &pflag.Flag{
- Name: "testflag",
- Value: testValue,
- Changed: false,
- }
- BindPFlag("testvalue", flag)
- assert.Equal(t, testString, Get("testvalue"))
- flag.Value.Set("testing_mutate")
- flag.Changed = true // hack for pflag usage
- assert.Equal(t, "testing_mutate", Get("testvalue"))
- }
- func TestBoundCaseSensitivity(t *testing.T) {
- assert.Equal(t, "brown", Get("eyes"))
- BindEnv("eYEs", "TURTLE_EYES")
- os.Setenv("TURTLE_EYES", "blue")
- assert.Equal(t, "blue", Get("eyes"))
- var testString = "green"
- var testValue = newStringValue(testString, &testString)
- flag := &pflag.Flag{
- Name: "eyeballs",
- Value: testValue,
- Changed: true,
- }
- BindPFlag("eYEs", flag)
- assert.Equal(t, "green", Get("eyes"))
- }
- func TestSizeInBytes(t *testing.T) {
- input := map[string]uint{
- "": 0,
- "b": 0,
- "12 bytes": 0,
- "200000000000gb": 0,
- "12 b": 12,
- "43 MB": 43 * (1 << 20),
- "10mb": 10 * (1 << 20),
- "1gb": 1 << 30,
- }
- for str, expected := range input {
- assert.Equal(t, expected, parseSizeInBytes(str), str)
- }
- }
- func TestFindsNestedKeys(t *testing.T) {
- initConfigs()
- dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
- Set("super", map[string]interface{}{
- "deep": map[string]interface{}{
- "nested": "value",
- },
- })
- expected := map[string]interface{}{
- "super": map[string]interface{}{
- "deep": map[string]interface{}{
- "nested": "value",
- },
- },
- "super.deep": map[string]interface{}{
- "nested": "value",
- },
- "super.deep.nested": "value",
- "owner.organization": "MongoDB",
- "batters.batter": []interface{}{
- map[string]interface{}{
- "type": "Regular",
- },
- map[string]interface{}{
- "type": "Chocolate",
- },
- map[string]interface{}{
- "type": "Blueberry",
- },
- map[string]interface{}{
- "type": "Devil's Food",
- },
- },
- "hobbies": []interface{}{
- "skateboarding", "snowboarding", "go",
- },
- "TITLE_DOTENV": "DotEnv Example",
- "TYPE_DOTENV": "donut",
- "NAME_DOTENV": "Cake",
- "title": "TOML Example",
- "newkey": "remote",
- "batters": map[string]interface{}{
- "batter": []interface{}{
- map[string]interface{}{
- "type": "Regular",
- },
- map[string]interface{}{
- "type": "Chocolate",
- }, map[string]interface{}{
- "type": "Blueberry",
- }, map[string]interface{}{
- "type": "Devil's Food",
- },
- },
- },
- "eyes": "brown",
- "age": 35,
- "owner": map[string]interface{}{
- "organization": "MongoDB",
- "bio": "MongoDB Chief Developer Advocate & Hacker at Large",
- "dob": dob,
- },
- "owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large",
- "type": "donut",
- "id": "0001",
- "name": "Cake",
- "hacker": true,
- "ppu": 0.55,
- "clothing": map[string]interface{}{
- "jacket": "leather",
- "trousers": "denim",
- "pants": map[string]interface{}{
- "size": "large",
- },
- },
- "clothing.jacket": "leather",
- "clothing.pants.size": "large",
- "clothing.trousers": "denim",
- "owner.dob": dob,
- "beard": true,
- "foos": []map[string]interface{}{
- {
- "foo": []map[string]interface{}{
- {
- "key": 1,
- },
- {
- "key": 2,
- },
- {
- "key": 3,
- },
- {
- "key": 4,
- },
- },
- },
- },
- }
- for key, expectedValue := range expected {
- assert.Equal(t, expectedValue, v.Get(key))
- }
- }
- func TestReadBufConfig(t *testing.T) {
- v := New()
- v.SetConfigType("yaml")
- v.ReadConfig(bytes.NewBuffer(yamlExample))
- t.Log(v.AllKeys())
- assert.True(t, v.InConfig("name"))
- assert.False(t, v.InConfig("state"))
- assert.Equal(t, "steve", v.Get("name"))
- assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
- assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
- assert.Equal(t, 35, v.Get("age"))
- }
- func TestIsSet(t *testing.T) {
- v := New()
- v.SetConfigType("yaml")
- /* config and defaults */
- v.ReadConfig(bytes.NewBuffer(yamlExample))
- v.SetDefault("clothing.shoes", "sneakers")
- assert.True(t, v.IsSet("clothing"))
- assert.True(t, v.IsSet("clothing.jacket"))
- assert.False(t, v.IsSet("clothing.jackets"))
- assert.True(t, v.IsSet("clothing.shoes"))
- /* state change */
- assert.False(t, v.IsSet("helloworld"))
- v.Set("helloworld", "fubar")
- assert.True(t, v.IsSet("helloworld"))
- /* env */
- v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
- v.BindEnv("eyes")
- v.BindEnv("foo")
- v.BindEnv("clothing.hat")
- v.BindEnv("clothing.hats")
- os.Setenv("FOO", "bar")
- os.Setenv("CLOTHING_HAT", "bowler")
- assert.True(t, v.IsSet("eyes")) // in the config file
- assert.True(t, v.IsSet("foo")) // in the environment
- assert.True(t, v.IsSet("clothing.hat")) // in the environment
- assert.False(t, v.IsSet("clothing.hats")) // not defined
- /* flags */
- flagset := pflag.NewFlagSet("testisset", pflag.ContinueOnError)
- flagset.Bool("foobaz", false, "foobaz")
- flagset.Bool("barbaz", false, "barbaz")
- foobaz, barbaz := flagset.Lookup("foobaz"), flagset.Lookup("barbaz")
- v.BindPFlag("foobaz", foobaz)
- v.BindPFlag("barbaz", barbaz)
- barbaz.Value.Set("true")
- barbaz.Changed = true // hack for pflag usage
- assert.False(t, v.IsSet("foobaz"))
- assert.True(t, v.IsSet("barbaz"))
- }
- func TestDirsSearch(t *testing.T) {
- root, config, cleanup := initDirs(t)
- defer cleanup()
- v := New()
- v.SetConfigName(config)
- v.SetDefault(`key`, `default`)
- entries, err := ioutil.ReadDir(root)
- assert.Nil(t, err)
- for _, e := range entries {
- if e.IsDir() {
- v.AddConfigPath(e.Name())
- }
- }
- err = v.ReadInConfig()
- assert.Nil(t, err)
- assert.Equal(t, `value is `+filepath.Base(v.configPaths[0]), v.GetString(`key`))
- }
- func TestWrongDirsSearchNotFound(t *testing.T) {
- _, config, cleanup := initDirs(t)
- defer cleanup()
- v := New()
- v.SetConfigName(config)
- v.SetDefault(`key`, `default`)
- v.AddConfigPath(`whattayoutalkingbout`)
- v.AddConfigPath(`thispathaintthere`)
- err := v.ReadInConfig()
- assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
- // Even though config did not load and the error might have
- // been ignored by the client, the default still loads
- assert.Equal(t, `default`, v.GetString(`key`))
- }
- func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
- _, config, cleanup := initDirs(t)
- defer cleanup()
- v := New()
- v.SetConfigName(config)
- v.SetDefault(`key`, `default`)
- v.AddConfigPath(`whattayoutalkingbout`)
- v.AddConfigPath(`thispathaintthere`)
- err := v.MergeInConfig()
- assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
- // Even though config did not load and the error might have
- // been ignored by the client, the default still loads
- assert.Equal(t, `default`, v.GetString(`key`))
- }
- func TestSub(t *testing.T) {
- v := New()
- v.SetConfigType("yaml")
- v.ReadConfig(bytes.NewBuffer(yamlExample))
- subv := v.Sub("clothing")
- assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
- subv = v.Sub("clothing.pants")
- assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size"))
- subv = v.Sub("clothing.pants.size")
- assert.Equal(t, (*Viper)(nil), subv)
- subv = v.Sub("missing.key")
- assert.Equal(t, (*Viper)(nil), subv)
- }
- var hclWriteExpected = []byte(`"foos" = {
- "foo" = {
- "key" = 1
- }
- "foo" = {
- "key" = 2
- }
- "foo" = {
- "key" = 3
- }
- "foo" = {
- "key" = 4
- }
- }
- "id" = "0001"
- "name" = "Cake"
- "ppu" = 0.55
- "type" = "donut"`)
- func TestWriteConfigHCL(t *testing.T) {
- v := New()
- fs := afero.NewMemMapFs()
- v.SetFs(fs)
- v.SetConfigName("c")
- v.SetConfigType("hcl")
- err := v.ReadConfig(bytes.NewBuffer(hclExample))
- if err != nil {
- t.Fatal(err)
- }
- if err := v.WriteConfigAs("c.hcl"); err != nil {
- t.Fatal(err)
- }
- read, err := afero.ReadFile(fs, "c.hcl")
- if err != nil {
- t.Fatal(err)
- }
- assert.Equal(t, hclWriteExpected, read)
- }
- var jsonWriteExpected = []byte(`{
- "batters": {
- "batter": [
- {
- "type": "Regular"
- },
- {
- "type": "Chocolate"
- },
- {
- "type": "Blueberry"
- },
- {
- "type": "Devil's Food"
- }
- ]
- },
- "id": "0001",
- "name": "Cake",
- "ppu": 0.55,
- "type": "donut"
- }`)
- func TestWriteConfigJson(t *testing.T) {
- v := New()
- fs := afero.NewMemMapFs()
- v.SetFs(fs)
- v.SetConfigName("c")
- v.SetConfigType("json")
- err := v.ReadConfig(bytes.NewBuffer(jsonExample))
- if err != nil {
- t.Fatal(err)
- }
- if err := v.WriteConfigAs("c.json"); err != nil {
- t.Fatal(err)
- }
- read, err := afero.ReadFile(fs, "c.json")
- if err != nil {
- t.Fatal(err)
- }
- assert.Equal(t, jsonWriteExpected, read)
- }
- var propertiesWriteExpected = []byte(`p_id = 0001
- p_type = donut
- p_name = Cake
- p_ppu = 0.55
- p_batters.batter.type = Regular
- `)
- func TestWriteConfigProperties(t *testing.T) {
- v := New()
- fs := afero.NewMemMapFs()
- v.SetFs(fs)
- v.SetConfigName("c")
- v.SetConfigType("properties")
- err := v.ReadConfig(bytes.NewBuffer(propertiesExample))
- if err != nil {
- t.Fatal(err)
- }
- if err := v.WriteConfigAs("c.properties"); err != nil {
- t.Fatal(err)
- }
- read, err := afero.ReadFile(fs, "c.properties")
- if err != nil {
- t.Fatal(err)
- }
- assert.Equal(t, propertiesWriteExpected, read)
- }
- func TestWriteConfigTOML(t *testing.T) {
- fs := afero.NewMemMapFs()
- v := New()
- v.SetFs(fs)
- v.SetConfigName("c")
- v.SetConfigType("toml")
- err := v.ReadConfig(bytes.NewBuffer(tomlExample))
- if err != nil {
- t.Fatal(err)
- }
- if err := v.WriteConfigAs("c.toml"); err != nil {
- t.Fatal(err)
- }
- // The TOML String method does not order the contents.
- // Therefore, we must read the generated file and compare the data.
- v2 := New()
- v2.SetFs(fs)
- v2.SetConfigName("c")
- v2.SetConfigType("toml")
- v2.SetConfigFile("c.toml")
- err = v2.ReadInConfig()
- if err != nil {
- t.Fatal(err)
- }
- assert.Equal(t, v.GetString("title"), v2.GetString("title"))
- assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio"))
- assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob"))
- assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization"))
- }
- var dotenvWriteExpected = []byte(`
- TITLE="DotEnv Write Example"
- NAME=Oreo
- KIND=Biscuit
- `)
- func TestWriteConfigDotEnv(t *testing.T) {
- fs := afero.NewMemMapFs()
- v := New()
- v.SetFs(fs)
- v.SetConfigName("c")
- v.SetConfigType("env")
- err := v.ReadConfig(bytes.NewBuffer(dotenvWriteExpected))
- if err != nil {
- t.Fatal(err)
- }
- if err := v.WriteConfigAs("c.env"); err != nil {
- t.Fatal(err)
- }
- // The TOML String method does not order the contents.
- // Therefore, we must read the generated file and compare the data.
- v2 := New()
- v2.SetFs(fs)
- v2.SetConfigName("c")
- v2.SetConfigType("env")
- v2.SetConfigFile("c.env")
- err = v2.ReadInConfig()
- if err != nil {
- t.Fatal(err)
- }
- assert.Equal(t, v.GetString("title"), v2.GetString("title"))
- assert.Equal(t, v.GetString("type"), v2.GetString("type"))
- assert.Equal(t, v.GetString("kind"), v2.GetString("kind"))
- }
- var yamlWriteExpected = []byte(`age: 35
- beard: true
- clothing:
- jacket: leather
- pants:
- size: large
- trousers: denim
- eyes: brown
- hacker: true
- hobbies:
- - skateboarding
- - snowboarding
- - go
- name: steve
- `)
- func TestWriteConfigYAML(t *testing.T) {
- v := New()
- fs := afero.NewMemMapFs()
- v.SetFs(fs)
- v.SetConfigName("c")
- v.SetConfigType("yaml")
- err := v.ReadConfig(bytes.NewBuffer(yamlExample))
- if err != nil {
- t.Fatal(err)
- }
- if err := v.WriteConfigAs("c.yaml"); err != nil {
- t.Fatal(err)
- }
- read, err := afero.ReadFile(fs, "c.yaml")
- if err != nil {
- t.Fatal(err)
- }
- assert.Equal(t, yamlWriteExpected, read)
- }
- func TestSafeWriteConfig(t *testing.T) {
- v := New()
- fs := afero.NewMemMapFs()
- v.SetFs(fs)
- v.AddConfigPath("/test")
- v.SetConfigName("c")
- v.SetConfigType("yaml")
- require.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)))
- require.NoError(t, v.SafeWriteConfig())
- read, err := afero.ReadFile(fs, "/test/c.yaml")
- require.NoError(t, err)
- assert.Equal(t, yamlWriteExpected, read)
- }
- func TestSafeWriteConfigWithMissingConfigPath(t *testing.T) {
- v := New()
- fs := afero.NewMemMapFs()
- v.SetFs(fs)
- v.SetConfigName("c")
- v.SetConfigType("yaml")
- require.EqualError(t, v.SafeWriteConfig(), "missing configuration for 'configPath'")
- }
- func TestSafeWriteConfigWithExistingFile(t *testing.T) {
- v := New()
- fs := afero.NewMemMapFs()
- fs.Create("/test/c.yaml")
- v.SetFs(fs)
- v.AddConfigPath("/test")
- v.SetConfigName("c")
- v.SetConfigType("yaml")
- err := v.SafeWriteConfig()
- require.Error(t, err)
- _, ok := err.(ConfigFileAlreadyExistsError)
- assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
- }
- func TestSafeWriteAsConfig(t *testing.T) {
- v := New()
- fs := afero.NewMemMapFs()
- v.SetFs(fs)
- err := v.ReadConfig(bytes.NewBuffer(yamlExample))
- if err != nil {
- t.Fatal(err)
- }
- require.NoError(t, v.SafeWriteConfigAs("/test/c.yaml"))
- if _, err = afero.ReadFile(fs, "/test/c.yaml"); err != nil {
- t.Fatal(err)
- }
- }
- func TestSafeWriteConfigAsWithExistingFile(t *testing.T) {
- v := New()
- fs := afero.NewMemMapFs()
- fs.Create("/test/c.yaml")
- v.SetFs(fs)
- err := v.SafeWriteConfigAs("/test/c.yaml")
- require.Error(t, err)
- _, ok := err.(ConfigFileAlreadyExistsError)
- assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
- }
- var yamlMergeExampleTgt = []byte(`
- hello:
- pop: 37890
- largenum: 765432101234567
- num2pow63: 9223372036854775808
- world:
- - us
- - uk
- - fr
- - de
- `)
- var yamlMergeExampleSrc = []byte(`
- hello:
- pop: 45000
- largenum: 7654321001234567
- universe:
- - mw
- - ad
- ints:
- - 1
- - 2
- fu: bar
- `)
- func TestMergeConfig(t *testing.T) {
- v := New()
- v.SetConfigType("yml")
- if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
- t.Fatal(err)
- }
- if pop := v.GetInt("hello.pop"); pop != 37890 {
- t.Fatalf("pop != 37890, = %d", pop)
- }
- if pop := v.GetInt32("hello.pop"); pop != int32(37890) {
- t.Fatalf("pop != 37890, = %d", pop)
- }
- if pop := v.GetInt64("hello.largenum"); pop != int64(765432101234567) {
- t.Fatalf("int64 largenum != 765432101234567, = %d", pop)
- }
- if pop := v.GetUint("hello.pop"); pop != 37890 {
- t.Fatalf("uint pop != 37890, = %d", pop)
- }
- if pop := v.GetUint32("hello.pop"); pop != 37890 {
- t.Fatalf("uint32 pop != 37890, = %d", pop)
- }
- if pop := v.GetUint64("hello.num2pow63"); pop != 9223372036854775808 {
- t.Fatalf("uint64 num2pow63 != 9223372036854775808, = %d", pop)
- }
- if world := v.GetStringSlice("hello.world"); len(world) != 4 {
- t.Fatalf("len(world) != 4, = %d", len(world))
- }
- if fu := v.GetString("fu"); fu != "" {
- t.Fatalf("fu != \"\", = %s", fu)
- }
- if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
- t.Fatal(err)
- }
- if pop := v.GetInt("hello.pop"); pop != 45000 {
- t.Fatalf("pop != 45000, = %d", pop)
- }
- if pop := v.GetInt32("hello.pop"); pop != int32(45000) {
- t.Fatalf("pop != 45000, = %d", pop)
- }
- if pop := v.GetInt64("hello.largenum"); pop != int64(7654321001234567) {
- t.Fatalf("int64 largenum != 7654321001234567, = %d", pop)
- }
- if world := v.GetStringSlice("hello.world"); len(world) != 4 {
- t.Fatalf("len(world) != 4, = %d", len(world))
- }
- if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
- t.Fatalf("len(universe) != 2, = %d", len(universe))
- }
- if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
- t.Fatalf("len(ints) != 2, = %d", len(ints))
- }
- if fu := v.GetString("fu"); fu != "bar" {
- t.Fatalf("fu != \"bar\", = %s", fu)
- }
- }
- func TestMergeConfigNoMerge(t *testing.T) {
- v := New()
- v.SetConfigType("yml")
- if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
- t.Fatal(err)
- }
- if pop := v.GetInt("hello.pop"); pop != 37890 {
- t.Fatalf("pop != 37890, = %d", pop)
- }
- if world := v.GetStringSlice("hello.world"); len(world) != 4 {
- t.Fatalf("len(world) != 4, = %d", len(world))
- }
- if fu := v.GetString("fu"); fu != "" {
- t.Fatalf("fu != \"\", = %s", fu)
- }
- if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
- t.Fatal(err)
- }
- if pop := v.GetInt("hello.pop"); pop != 45000 {
- t.Fatalf("pop != 45000, = %d", pop)
- }
- if world := v.GetStringSlice("hello.world"); len(world) != 0 {
- t.Fatalf("len(world) != 0, = %d", len(world))
- }
- if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
- t.Fatalf("len(universe) != 2, = %d", len(universe))
- }
- if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
- t.Fatalf("len(ints) != 2, = %d", len(ints))
- }
- if fu := v.GetString("fu"); fu != "bar" {
- t.Fatalf("fu != \"bar\", = %s", fu)
- }
- }
- func TestMergeConfigMap(t *testing.T) {
- v := New()
- v.SetConfigType("yml")
- if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
- t.Fatal(err)
- }
- assert := func(i int) {
- large := v.GetInt64("hello.largenum")
- pop := v.GetInt("hello.pop")
- if large != 765432101234567 {
- t.Fatal("Got large num:", large)
- }
- if pop != i {
- t.Fatal("Got pop:", pop)
- }
- }
- assert(37890)
- update := map[string]interface{}{
- "Hello": map[string]interface{}{
- "Pop": 1234,
- },
- "World": map[interface{}]interface{}{
- "Rock": 345,
- },
- }
- if err := v.MergeConfigMap(update); err != nil {
- t.Fatal(err)
- }
- if rock := v.GetInt("world.rock"); rock != 345 {
- t.Fatal("Got rock:", rock)
- }
- assert(1234)
- }
- func TestUnmarshalingWithAliases(t *testing.T) {
- v := New()
- v.SetDefault("ID", 1)
- v.Set("name", "Steve")
- v.Set("lastname", "Owen")
- v.RegisterAlias("UserID", "ID")
- v.RegisterAlias("Firstname", "name")
- v.RegisterAlias("Surname", "lastname")
- type config struct {
- ID int
- FirstName string
- Surname string
- }
- var C config
- err := v.Unmarshal(&C)
- if err != nil {
- t.Fatalf("unable to decode into struct, %v", err)
- }
- assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C)
- }
- func TestSetConfigNameClearsFileCache(t *testing.T) {
- SetConfigFile("/tmp/config.yaml")
- SetConfigName("default")
- f, err := v.getConfigFile()
- if err == nil {
- t.Fatalf("config file cache should have been cleared")
- }
- assert.Empty(t, f)
- }
- func TestShadowedNestedValue(t *testing.T) {
- config := `name: steve
- clothing:
- jacket: leather
- trousers: denim
- pants:
- size: large
- `
- initConfig("yaml", config)
- assert.Equal(t, "steve", GetString("name"))
- polyester := "polyester"
- SetDefault("clothing.shirt", polyester)
- SetDefault("clothing.jacket.price", 100)
- assert.Equal(t, "leather", GetString("clothing.jacket"))
- assert.Nil(t, Get("clothing.jacket.price"))
- assert.Equal(t, polyester, GetString("clothing.shirt"))
- clothingSettings := AllSettings()["clothing"].(map[string]interface{})
- assert.Equal(t, "leather", clothingSettings["jacket"])
- assert.Equal(t, polyester, clothingSettings["shirt"])
- }
- func TestDotParameter(t *testing.T) {
- initJSON()
- // shoud take precedence over batters defined in jsonExample
- r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
- unmarshalReader(r, v.config)
- actual := Get("batters.batter")
- expected := []interface{}{map[string]interface{}{"type": "Small"}}
- assert.Equal(t, expected, actual)
- }
- func TestCaseInsensitive(t *testing.T) {
- for _, config := range []struct {
- typ string
- content string
- }{
- {"yaml", `
- aBcD: 1
- eF:
- gH: 2
- iJk: 3
- Lm:
- nO: 4
- P:
- Q: 5
- R: 6
- `},
- {"json", `{
- "aBcD": 1,
- "eF": {
- "iJk": 3,
- "Lm": {
- "P": {
- "Q": 5,
- "R": 6
- },
- "nO": 4
- },
- "gH": 2
- }
- }`},
- {"toml", `aBcD = 1
- [eF]
- gH = 2
- iJk = 3
- [eF.Lm]
- nO = 4
- [eF.Lm.P]
- Q = 5
- R = 6
- `},
- } {
- doTestCaseInsensitive(t, config.typ, config.content)
- }
- }
- func TestCaseInsensitiveSet(t *testing.T) {
- Reset()
- m1 := map[string]interface{}{
- "Foo": 32,
- "Bar": map[interface{}]interface {
- }{
- "ABc": "A",
- "cDE": "B"},
- }
- m2 := map[string]interface{}{
- "Foo": 52,
- "Bar": map[interface{}]interface {
- }{
- "bCd": "A",
- "eFG": "B"},
- }
- Set("Given1", m1)
- Set("Number1", 42)
- SetDefault("Given2", m2)
- SetDefault("Number2", 52)
- // Verify SetDefault
- if v := Get("number2"); v != 52 {
- t.Fatalf("Expected 52 got %q", v)
- }
- if v := Get("given2.foo"); v != 52 {
- t.Fatalf("Expected 52 got %q", v)
- }
- if v := Get("given2.bar.bcd"); v != "A" {
- t.Fatalf("Expected A got %q", v)
- }
- if _, ok := m2["Foo"]; !ok {
- t.Fatal("Input map changed")
- }
- // Verify Set
- if v := Get("number1"); v != 42 {
- t.Fatalf("Expected 42 got %q", v)
- }
- if v := Get("given1.foo"); v != 32 {
- t.Fatalf("Expected 32 got %q", v)
- }
- if v := Get("given1.bar.abc"); v != "A" {
- t.Fatalf("Expected A got %q", v)
- }
- if _, ok := m1["Foo"]; !ok {
- t.Fatal("Input map changed")
- }
- }
- func TestParseNested(t *testing.T) {
- type duration struct {
- Delay time.Duration
- }
- type item struct {
- Name string
- Delay time.Duration
- Nested duration
- }
- config := `[[parent]]
- delay="100ms"
- [parent.nested]
- delay="200ms"
- `
- initConfig("toml", config)
- var items []item
- err := v.UnmarshalKey("parent", &items)
- if err != nil {
- t.Fatalf("unable to decode into struct, %v", err)
- }
- assert.Equal(t, 1, len(items))
- assert.Equal(t, 100*time.Millisecond, items[0].Delay)
- assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
- }
- func doTestCaseInsensitive(t *testing.T, typ, config string) {
- initConfig(typ, config)
- Set("RfD", true)
- assert.Equal(t, true, Get("rfd"))
- assert.Equal(t, true, Get("rFD"))
- assert.Equal(t, 1, cast.ToInt(Get("abcd")))
- assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
- assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
- assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
- assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
- assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
- }
- func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) {
- watchDir, err := ioutil.TempDir("", "")
- require.Nil(t, err)
- configFile := path.Join(watchDir, "config.yaml")
- err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640)
- require.Nil(t, err)
- cleanup := func() {
- os.RemoveAll(watchDir)
- }
- v := New()
- v.SetConfigFile(configFile)
- err = v.ReadInConfig()
- require.Nil(t, err)
- require.Equal(t, "bar", v.Get("foo"))
- return v, configFile, cleanup
- }
- func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) {
- watchDir, err := ioutil.TempDir("", "")
- require.Nil(t, err)
- dataDir1 := path.Join(watchDir, "data1")
- err = os.Mkdir(dataDir1, 0777)
- require.Nil(t, err)
- realConfigFile := path.Join(dataDir1, "config.yaml")
- t.Logf("Real config file location: %s\n", realConfigFile)
- err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640)
- require.Nil(t, err)
- cleanup := func() {
- os.RemoveAll(watchDir)
- }
- // now, symlink the tm `data1` dir to `data` in the baseDir
- os.Symlink(dataDir1, path.Join(watchDir, "data"))
- // and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`
- configFile := path.Join(watchDir, "config.yaml")
- os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile)
- t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml"))
- // init Viper
- v := New()
- v.SetConfigFile(configFile)
- err = v.ReadInConfig()
- require.Nil(t, err)
- require.Equal(t, "bar", v.Get("foo"))
- return v, watchDir, configFile, cleanup
- }
- func TestWatchFile(t *testing.T) {
- if runtime.GOOS == "linux" {
- // TODO(bep) FIX ME
- t.Skip("Skip test on Linux ...")
- }
- t.Run("file content changed", func(t *testing.T) {
- // given a `config.yaml` file being watched
- v, configFile, cleanup := newViperWithConfigFile(t)
- defer cleanup()
- _, err := os.Stat(configFile)
- require.NoError(t, err)
- t.Logf("test config file: %s\n", configFile)
- wg := sync.WaitGroup{}
- wg.Add(1)
- v.OnConfigChange(func(in fsnotify.Event) {
- t.Logf("config file changed")
- wg.Done()
- })
- v.WatchConfig()
- // when overwriting the file and waiting for the custom change notification handler to be triggered
- err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640)
- wg.Wait()
- // then the config value should have changed
- require.Nil(t, err)
- assert.Equal(t, "baz", v.Get("foo"))
- })
- t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) {
- // skip if not executed on Linux
- if runtime.GOOS != "linux" {
- t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
- }
- v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t)
- // defer cleanup()
- wg := sync.WaitGroup{}
- v.WatchConfig()
- v.OnConfigChange(func(in fsnotify.Event) {
- t.Logf("config file changed")
- wg.Done()
- })
- wg.Add(1)
- // when link to another `config.yaml` file
- dataDir2 := path.Join(watchDir, "data2")
- err := os.Mkdir(dataDir2, 0777)
- require.Nil(t, err)
- configFile2 := path.Join(dataDir2, "config.yaml")
- err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640)
- require.Nil(t, err)
- // change the symlink using the `ln -sfn` command
- err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
- require.Nil(t, err)
- wg.Wait()
- // then
- require.Nil(t, err)
- assert.Equal(t, "baz", v.Get("foo"))
- })
- }
- func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
- flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
- flags.String("foo.bar", "cobra_flag", "")
- v := New()
- assert.NoError(t, v.BindPFlags(flags))
- config := &struct {
- Foo struct {
- Bar string
- }
- }{}
- assert.NoError(t, v.Unmarshal(config))
- assert.Equal(t, "cobra_flag", config.Foo.Bar)
- }
- var yamlExampleWithDot = []byte(`Hacker: true
- name: steve
- hobbies:
- - skateboarding
- - snowboarding
- - go
- clothing:
- jacket: leather
- trousers: denim
- pants:
- size: large
- age: 35
- eyes : brown
- beard: true
- emails:
- steve@hacker.com:
- created: 01/02/03
- active: true
- `)
- func TestKeyDelimiter(t *testing.T) {
- v := NewWithOptions(KeyDelimiter("::"))
- v.SetConfigType("yaml")
- r := strings.NewReader(string(yamlExampleWithDot))
- err := v.unmarshalReader(r, v.config)
- require.NoError(t, err)
- values := map[string]interface{}{
- "image": map[string]interface{}{
- "repository": "someImage",
- "tag": "1.0.0",
- },
- "ingress": map[string]interface{}{
- "annotations": map[string]interface{}{
- "traefik.frontend.rule.type": "PathPrefix",
- "traefik.ingress.kubernetes.io/ssl-redirect": "true",
- },
- },
- }
- v.SetDefault("charts::values", values)
- assert.Equal(t, "leather", v.GetString("clothing::jacket"))
- assert.Equal(t, "01/02/03", v.GetString("emails::steve@hacker.com::created"))
- type config struct {
- Charts struct {
- Values map[string]interface{}
- }
- }
- expected := config{
- Charts: struct {
- Values map[string]interface{}
- }{
- Values: values,
- },
- }
- var actual config
- assert.NoError(t, v.Unmarshal(&actual))
- assert.Equal(t, expected, actual)
- }
- func BenchmarkGetBool(b *testing.B) {
- key := "BenchmarkGetBool"
- v = New()
- v.Set(key, true)
- for i := 0; i < b.N; i++ {
- if !v.GetBool(key) {
- b.Fatal("GetBool returned false")
- }
- }
- }
- func BenchmarkGet(b *testing.B) {
- key := "BenchmarkGet"
- v = New()
- v.Set(key, true)
- for i := 0; i < b.N; i++ {
- if !v.Get(key).(bool) {
- b.Fatal("Get returned false")
- }
- }
- }
- // BenchmarkGetBoolFromMap is the "perfect result" for the above.
- func BenchmarkGetBoolFromMap(b *testing.B) {
- m := make(map[string]bool)
- key := "BenchmarkGetBool"
- m[key] = true
- for i := 0; i < b.N; i++ {
- if !m[key] {
- b.Fatal("Map value was false")
- }
- }
- }
|