viper_test.go 46 KB

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