1 package ini
2
3 import (
4 "bytes"
5 "fmt"
6 "runtime"
7 "strings"
8 "testing"
9 "time"
10
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13 )
14
15 func TestKey_AddShadow(t *testing.T) {
16 t.Run("add shadow to a key", func(t *testing.T) {
17 f, err := ShadowLoad([]byte(`
18 [notes]
19 -: note1`))
20 require.NoError(t, err)
21 require.NotNil(t, f)
22
23 k, err := f.Section("").NewKey("NAME", "ini")
24 require.NoError(t, err)
25 require.NotNil(t, k)
26
27 assert.NoError(t, k.AddShadow("ini.v1"))
28 assert.Equal(t, []string{"ini", "ini.v1"}, k.ValueWithShadows())
29
30 t.Run("add shadow to boolean key", func(t *testing.T) {
31 k, err := f.Section("").NewBooleanKey("published")
32 require.NoError(t, err)
33 require.NotNil(t, k)
34 assert.Error(t, k.AddShadow("beta"))
35 })
36
37 t.Run("add shadow to auto-increment key", func(t *testing.T) {
38 assert.Error(t, f.Section("notes").Key("#1").AddShadow("beta"))
39 })
40
41 t.Run("deduplicate an existing value", func(t *testing.T) {
42 k := f.Section("").Key("NAME")
43 assert.NoError(t, k.AddShadow("ini"))
44 assert.Equal(t, []string{"ini", "ini.v1"}, k.ValueWithShadows())
45 })
46
47 t.Run("ignore empty shadow values", func(t *testing.T) {
48 k := f.Section("").Key("empty")
49 assert.NoError(t, k.AddShadow(""))
50 assert.NoError(t, k.AddShadow("ini"))
51 assert.Equal(t, []string{"ini"}, k.ValueWithShadows())
52 })
53 })
54
55 t.Run("allow duplicate shadowed values", func(t *testing.T) {
56 f := Empty(LoadOptions{
57 AllowShadows: true,
58 AllowDuplicateShadowValues: true,
59 })
60 require.NotNil(t, f)
61
62 k, err := f.Section("").NewKey("NAME", "ini")
63 require.NoError(t, err)
64 require.NotNil(t, k)
65
66 assert.NoError(t, k.AddShadow("ini.v1"))
67 assert.NoError(t, k.AddShadow("ini"))
68 assert.NoError(t, k.AddShadow("ini"))
69 assert.Equal(t, []string{"ini", "ini.v1", "ini", "ini"}, k.ValueWithShadows())
70 })
71
72 t.Run("shadow is not allowed", func(t *testing.T) {
73 f := Empty()
74 require.NotNil(t, f)
75
76 k, err := f.Section("").NewKey("NAME", "ini")
77 require.NoError(t, err)
78 require.NotNil(t, k)
79
80 assert.Error(t, k.AddShadow("ini.v1"))
81 })
82 }
83
84
85 func float64sEqual(t *testing.T, values []float64, expected ...float64) {
86 t.Helper()
87
88 assert.Len(t, values, len(expected))
89 for i, v := range expected {
90 assert.Equal(t, v, values[i])
91 }
92 }
93
94 func intsEqual(t *testing.T, values []int, expected ...int) {
95 t.Helper()
96
97 assert.Len(t, values, len(expected))
98 for i, v := range expected {
99 assert.Equal(t, v, values[i])
100 }
101 }
102
103 func int64sEqual(t *testing.T, values []int64, expected ...int64) {
104 t.Helper()
105
106 assert.Len(t, values, len(expected))
107 for i, v := range expected {
108 assert.Equal(t, v, values[i])
109 }
110 }
111
112 func uintsEqual(t *testing.T, values []uint, expected ...uint) {
113 t.Helper()
114
115 assert.Len(t, values, len(expected))
116 for i, v := range expected {
117 assert.Equal(t, v, values[i])
118 }
119 }
120
121 func uint64sEqual(t *testing.T, values []uint64, expected ...uint64) {
122 t.Helper()
123
124 assert.Len(t, values, len(expected))
125 for i, v := range expected {
126 assert.Equal(t, v, values[i])
127 }
128 }
129
130 func boolsEqual(t *testing.T, values []bool, expected ...bool) {
131 t.Helper()
132
133 assert.Len(t, values, len(expected))
134 for i, v := range expected {
135 assert.Equal(t, v, values[i])
136 }
137 }
138
139 func timesEqual(t *testing.T, values []time.Time, expected ...time.Time) {
140 t.Helper()
141
142 assert.Len(t, values, len(expected))
143 for i, v := range expected {
144 assert.Equal(t, v.String(), values[i].String())
145 }
146 }
147
148 func TestKey_Helpers(t *testing.T) {
149 t.Run("getting and setting values", func(t *testing.T) {
150 f, err := Load(fullConf)
151 require.NoError(t, err)
152 require.NotNil(t, f)
153
154 t.Run("get string representation", func(t *testing.T) {
155 sec := f.Section("")
156 require.NotNil(t, sec)
157 assert.Equal(t, "ini", sec.Key("NAME").Value())
158 assert.Equal(t, "ini", sec.Key("NAME").String())
159 assert.Equal(t, "ini", sec.Key("NAME").Validate(func(in string) string {
160 return in
161 }))
162 assert.Equal(t, "; Package name", sec.Key("NAME").Comment)
163 assert.Equal(t, "gopkg.in/ini.v1", sec.Key("IMPORT_PATH").String())
164
165 t.Run("with ValueMapper", func(t *testing.T) {
166 f.ValueMapper = func(in string) string {
167 if in == "gopkg.in/%(NAME)s.%(VERSION)s" {
168 return "github.com/go-ini/ini"
169 }
170 return in
171 }
172 assert.Equal(t, "github.com/go-ini/ini", sec.Key("IMPORT_PATH").String())
173 })
174 })
175
176 t.Run("get values in non-default section", func(t *testing.T) {
177 sec := f.Section("author")
178 require.NotNil(t, sec)
179 assert.Equal(t, "Unknwon", sec.Key("NAME").String())
180 assert.Equal(t, "https://github.com/Unknwon", sec.Key("GITHUB").String())
181
182 sec = f.Section("package")
183 require.NotNil(t, sec)
184 assert.Equal(t, "https://gopkg.in/ini.v1", sec.Key("CLONE_URL").String())
185 })
186
187 t.Run("get auto-increment key names", func(t *testing.T) {
188 keys := f.Section("features").Keys()
189 for i, k := range keys {
190 assert.Equal(t, fmt.Sprintf("#%d", i+1), k.Name())
191 }
192 })
193
194 t.Run("get parent-keys that are available to the child section", func(t *testing.T) {
195 parentKeys := f.Section("package.sub").ParentKeys()
196 for _, k := range parentKeys {
197 assert.Equal(t, "CLONE_URL", k.Name())
198 }
199 })
200
201 t.Run("get overwrite value", func(t *testing.T) {
202 assert.Equal(t, "u@gogs.io", f.Section("author").Key("E-MAIL").String())
203 })
204
205 t.Run("get sections", func(t *testing.T) {
206 sections := f.Sections()
207 for i, name := range []string{DefaultSection, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"} {
208 assert.Equal(t, name, sections[i].Name())
209 }
210 })
211
212 t.Run("get parent section value", func(t *testing.T) {
213 assert.Equal(t, "https://gopkg.in/ini.v1", f.Section("package.sub").Key("CLONE_URL").String())
214 assert.Equal(t, "https://gopkg.in/ini.v1", f.Section("package.fake.sub").Key("CLONE_URL").String())
215 })
216
217 t.Run("get multiple line value", func(t *testing.T) {
218 if runtime.GOOS == "windows" {
219 t.Skip("Skipping testing on Windows")
220 }
221
222 assert.Equal(t, "Gopher.\nCoding addict.\nGood man.\n", f.Section("author").Key("BIO").String())
223 })
224
225 t.Run("get values with type", func(t *testing.T) {
226 sec := f.Section("types")
227 v1, err := sec.Key("BOOL").Bool()
228 require.NoError(t, err)
229 assert.True(t, v1)
230
231 v1, err = sec.Key("BOOL_FALSE").Bool()
232 require.NoError(t, err)
233 assert.False(t, v1)
234
235 v2, err := sec.Key("FLOAT64").Float64()
236 require.NoError(t, err)
237 assert.Equal(t, 1.25, v2)
238
239 v3, err := sec.Key("INT").Int()
240 require.NoError(t, err)
241 assert.Equal(t, 10, v3)
242
243 v4, err := sec.Key("INT").Int64()
244 require.NoError(t, err)
245 assert.Equal(t, int64(10), v4)
246
247 v5, err := sec.Key("UINT").Uint()
248 require.NoError(t, err)
249 assert.Equal(t, uint(3), v5)
250
251 v6, err := sec.Key("UINT").Uint64()
252 require.NoError(t, err)
253 assert.Equal(t, uint64(3), v6)
254
255 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
256 require.NoError(t, err)
257 v7, err := sec.Key("TIME").Time()
258 require.NoError(t, err)
259 assert.Equal(t, ti.String(), v7.String())
260
261 v8, err := sec.Key("HEX_NUMBER").Int()
262 require.NoError(t, err)
263 assert.Equal(t, 0x3000, v8)
264
265 t.Run("must get values with type", func(t *testing.T) {
266 assert.Equal(t, "str", sec.Key("STRING").MustString("404"))
267 assert.True(t, sec.Key("BOOL").MustBool())
268 assert.Equal(t, float64(1.25), sec.Key("FLOAT64").MustFloat64())
269 assert.Equal(t, int(10), sec.Key("INT").MustInt())
270 assert.Equal(t, int64(10), sec.Key("INT").MustInt64())
271 assert.Equal(t, uint(3), sec.Key("UINT").MustUint())
272 assert.Equal(t, uint64(3), sec.Key("UINT").MustUint64())
273 assert.Equal(t, ti.String(), sec.Key("TIME").MustTime().String())
274 assert.Equal(t, 0x3000, sec.Key("HEX_NUMBER").MustInt())
275
276 dur, err := time.ParseDuration("2h45m")
277 require.NoError(t, err)
278 assert.Equal(t, dur.Seconds(), sec.Key("DURATION").MustDuration().Seconds())
279
280 t.Run("must get values with default value", func(t *testing.T) {
281 assert.Equal(t, "404", sec.Key("STRING_404").MustString("404"))
282 assert.True(t, sec.Key("BOOL_404").MustBool(true))
283 assert.Equal(t, float64(2.5), sec.Key("FLOAT64_404").MustFloat64(2.5))
284 assert.Equal(t, int(15), sec.Key("INT_404").MustInt(15))
285 assert.Equal(t, int64(15), sec.Key("INT64_404").MustInt64(15))
286 assert.Equal(t, uint(6), sec.Key("UINT_404").MustUint(6))
287 assert.Equal(t, uint64(6), sec.Key("UINT64_404").MustUint64(6))
288 assert.Equal(t, 0x3001, sec.Key("HEX_NUMBER_404").MustInt(0x3001))
289
290 ti, err := time.Parse(time.RFC3339, "2014-01-01T20:17:05Z")
291 require.NoError(t, err)
292 assert.Equal(t, ti.String(), sec.Key("TIME_404").MustTime(ti).String())
293
294 assert.Equal(t, dur.Seconds(), sec.Key("DURATION_404").MustDuration(dur).Seconds())
295
296 t.Run("must should set default as key value", func(t *testing.T) {
297 assert.Equal(t, "404", sec.Key("STRING_404").String())
298 assert.Equal(t, "true", sec.Key("BOOL_404").String())
299 assert.Equal(t, "2.5", sec.Key("FLOAT64_404").String())
300 assert.Equal(t, "15", sec.Key("INT_404").String())
301 assert.Equal(t, "15", sec.Key("INT64_404").String())
302 assert.Equal(t, "6", sec.Key("UINT_404").String())
303 assert.Equal(t, "6", sec.Key("UINT64_404").String())
304 assert.Equal(t, "2014-01-01T20:17:05Z", sec.Key("TIME_404").String())
305 assert.Equal(t, "2h45m0s", sec.Key("DURATION_404").String())
306 assert.Equal(t, "12289", sec.Key("HEX_NUMBER_404").String())
307 })
308 })
309 })
310 })
311
312 t.Run("get value with candidates", func(t *testing.T) {
313 sec := f.Section("types")
314 assert.Equal(t, "str", sec.Key("STRING").In("", []string{"str", "arr", "types"}))
315 assert.Equal(t, float64(1.25), sec.Key("FLOAT64").InFloat64(0, []float64{1.25, 2.5, 3.75}))
316 assert.Equal(t, int(10), sec.Key("INT").InInt(0, []int{10, 20, 30}))
317 assert.Equal(t, int64(10), sec.Key("INT").InInt64(0, []int64{10, 20, 30}))
318 assert.Equal(t, uint(3), sec.Key("UINT").InUint(0, []uint{3, 6, 9}))
319 assert.Equal(t, uint64(3), sec.Key("UINT").InUint64(0, []uint64{3, 6, 9}))
320
321 zt, err := time.Parse(time.RFC3339, "0001-01-01T01:00:00Z")
322 require.NoError(t, err)
323 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
324 require.NoError(t, err)
325 assert.Equal(t, ti.String(), sec.Key("TIME").InTime(zt, []time.Time{ti, time.Now(), time.Now().Add(1 * time.Second)}).String())
326
327 t.Run("get value with candidates and default value", func(t *testing.T) {
328 assert.Equal(t, "str", sec.Key("STRING_404_2").In("str", []string{"str", "arr", "types"}))
329 assert.Equal(t, float64(1.25), sec.Key("FLOAT64_404_2").InFloat64(1.25, []float64{1.25, 2.5, 3.75}))
330 assert.Equal(t, int(10), sec.Key("INT_404_2").InInt(10, []int{10, 20, 30}))
331 assert.Equal(t, int64(10), sec.Key("INT64_404_2").InInt64(10, []int64{10, 20, 30}))
332 assert.Equal(t, uint(3), sec.Key("UINT_404_2").InUint(3, []uint{3, 6, 9}))
333 assert.Equal(t, uint64(3), sec.Key("UINT_404_2").InUint64(3, []uint64{3, 6, 9}))
334 assert.Equal(t, ti.String(), sec.Key("TIME_404_2").InTime(ti, []time.Time{time.Now(), time.Now(), time.Now().Add(1 * time.Second)}).String())
335 })
336 })
337
338 t.Run("get values in range", func(t *testing.T) {
339 sec := f.Section("types")
340 assert.Equal(t, float64(1.25), sec.Key("FLOAT64").RangeFloat64(0, 1, 2))
341 assert.Equal(t, int(10), sec.Key("INT").RangeInt(0, 10, 20))
342 assert.Equal(t, int64(10), sec.Key("INT").RangeInt64(0, 10, 20))
343
344 minT, err := time.Parse(time.RFC3339, "0001-01-01T01:00:00Z")
345 require.NoError(t, err)
346 midT, err := time.Parse(time.RFC3339, "2013-01-01T01:00:00Z")
347 require.NoError(t, err)
348 maxT, err := time.Parse(time.RFC3339, "9999-01-01T01:00:00Z")
349 require.NoError(t, err)
350 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
351 require.NoError(t, err)
352 assert.Equal(t, ti.String(), sec.Key("TIME").RangeTime(ti, minT, maxT).String())
353
354 t.Run("get value in range with default value", func(t *testing.T) {
355 assert.Equal(t, float64(5), sec.Key("FLOAT64").RangeFloat64(5, 0, 1))
356 assert.Equal(t, 7, sec.Key("INT").RangeInt(7, 0, 5))
357 assert.Equal(t, int64(7), sec.Key("INT").RangeInt64(7, 0, 5))
358 assert.Equal(t, ti.String(), sec.Key("TIME").RangeTime(ti, minT, midT).String())
359 })
360 })
361
362 t.Run("get values into slice", func(t *testing.T) {
363 sec := f.Section("array")
364 assert.Equal(t, "en,zh,de", strings.Join(sec.Key("STRINGS").Strings(","), ","))
365 assert.Equal(t, 0, len(sec.Key("STRINGS_404").Strings(",")))
366
367 vals1 := sec.Key("FLOAT64S").Float64s(",")
368 float64sEqual(t, vals1, 1.1, 2.2, 3.3)
369
370 vals2 := sec.Key("INTS").Ints(",")
371 intsEqual(t, vals2, 1, 2, 3)
372
373 vals3 := sec.Key("INTS").Int64s(",")
374 int64sEqual(t, vals3, 1, 2, 3)
375
376 vals4 := sec.Key("UINTS").Uints(",")
377 uintsEqual(t, vals4, 1, 2, 3)
378
379 vals5 := sec.Key("UINTS").Uint64s(",")
380 uint64sEqual(t, vals5, 1, 2, 3)
381
382 vals6 := sec.Key("BOOLS").Bools(",")
383 boolsEqual(t, vals6, true, false, false)
384
385 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
386 require.NoError(t, err)
387 vals7 := sec.Key("TIMES").Times(",")
388 timesEqual(t, vals7, ti, ti, ti)
389 })
390
391 t.Run("test string slice escapes", func(t *testing.T) {
392 sec := f.Section("string escapes")
393 assert.Equal(t, []string{"value1", "value2", "value3"}, sec.Key("key1").Strings(","))
394 assert.Equal(t, []string{"value1, value2"}, sec.Key("key2").Strings(","))
395 assert.Equal(t, []string{`val\ue1`, "value2"}, sec.Key("key3").Strings(","))
396 assert.Equal(t, []string{`value1\`, `value\\2`}, sec.Key("key4").Strings(","))
397 assert.Equal(t, []string{"value1,, value2"}, sec.Key("key5").Strings(",,"))
398 assert.Equal(t, []string{"aaa", "bbb and space", "ccc"}, sec.Key("key6").Strings(" "))
399 })
400
401 t.Run("get valid values into slice", func(t *testing.T) {
402 sec := f.Section("array")
403 vals1 := sec.Key("FLOAT64S").ValidFloat64s(",")
404 float64sEqual(t, vals1, 1.1, 2.2, 3.3)
405
406 vals2 := sec.Key("INTS").ValidInts(",")
407 intsEqual(t, vals2, 1, 2, 3)
408
409 vals3 := sec.Key("INTS").ValidInt64s(",")
410 int64sEqual(t, vals3, 1, 2, 3)
411
412 vals4 := sec.Key("UINTS").ValidUints(",")
413 uintsEqual(t, vals4, 1, 2, 3)
414
415 vals5 := sec.Key("UINTS").ValidUint64s(",")
416 uint64sEqual(t, vals5, 1, 2, 3)
417
418 vals6 := sec.Key("BOOLS").ValidBools(",")
419 boolsEqual(t, vals6, true, false, false)
420
421 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
422 require.NoError(t, err)
423 vals7 := sec.Key("TIMES").ValidTimes(",")
424 timesEqual(t, vals7, ti, ti, ti)
425 })
426
427 t.Run("get values one type into slice of another type", func(t *testing.T) {
428 sec := f.Section("array")
429 vals1 := sec.Key("STRINGS").ValidFloat64s(",")
430 assert.Empty(t, vals1)
431
432 vals2 := sec.Key("STRINGS").ValidInts(",")
433 assert.Empty(t, vals2)
434
435 vals3 := sec.Key("STRINGS").ValidInt64s(",")
436 assert.Empty(t, vals3)
437
438 vals4 := sec.Key("STRINGS").ValidUints(",")
439 assert.Empty(t, vals4)
440
441 vals5 := sec.Key("STRINGS").ValidUint64s(",")
442 assert.Empty(t, vals5)
443
444 vals6 := sec.Key("STRINGS").ValidBools(",")
445 assert.Empty(t, vals6)
446
447 vals7 := sec.Key("STRINGS").ValidTimes(",")
448 assert.Empty(t, vals7)
449 })
450
451 t.Run("get valid values into slice without errors", func(t *testing.T) {
452 sec := f.Section("array")
453 vals1, err := sec.Key("FLOAT64S").StrictFloat64s(",")
454 require.NoError(t, err)
455 float64sEqual(t, vals1, 1.1, 2.2, 3.3)
456
457 vals2, err := sec.Key("INTS").StrictInts(",")
458 require.NoError(t, err)
459 intsEqual(t, vals2, 1, 2, 3)
460
461 vals3, err := sec.Key("INTS").StrictInt64s(",")
462 require.NoError(t, err)
463 int64sEqual(t, vals3, 1, 2, 3)
464
465 vals4, err := sec.Key("UINTS").StrictUints(",")
466 require.NoError(t, err)
467 uintsEqual(t, vals4, 1, 2, 3)
468
469 vals5, err := sec.Key("UINTS").StrictUint64s(",")
470 require.NoError(t, err)
471 uint64sEqual(t, vals5, 1, 2, 3)
472
473 vals6, err := sec.Key("BOOLS").StrictBools(",")
474 require.NoError(t, err)
475 boolsEqual(t, vals6, true, false, false)
476
477 ti, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
478 require.NoError(t, err)
479 vals7, err := sec.Key("TIMES").StrictTimes(",")
480 require.NoError(t, err)
481 timesEqual(t, vals7, ti, ti, ti)
482 })
483
484 t.Run("get invalid values into slice", func(t *testing.T) {
485 sec := f.Section("array")
486 vals1, err := sec.Key("STRINGS").StrictFloat64s(",")
487 assert.Empty(t, vals1)
488 assert.Error(t, err)
489
490 vals2, err := sec.Key("STRINGS").StrictInts(",")
491 assert.Empty(t, vals2)
492 assert.Error(t, err)
493
494 vals3, err := sec.Key("STRINGS").StrictInt64s(",")
495 assert.Empty(t, vals3)
496 assert.Error(t, err)
497
498 vals4, err := sec.Key("STRINGS").StrictUints(",")
499 assert.Empty(t, vals4)
500 assert.Error(t, err)
501
502 vals5, err := sec.Key("STRINGS").StrictUint64s(",")
503 assert.Empty(t, vals5)
504 assert.Error(t, err)
505
506 vals6, err := sec.Key("STRINGS").StrictBools(",")
507 assert.Empty(t, vals6)
508 assert.Error(t, err)
509
510 vals7, err := sec.Key("STRINGS").StrictTimes(",")
511 assert.Empty(t, vals7)
512 assert.Error(t, err)
513 })
514 })
515 }
516
517 func TestKey_ValueWithShadows(t *testing.T) {
518 t.Run("", func(t *testing.T) {
519 f, err := ShadowLoad([]byte(`
520 keyName = value1
521 keyName = value2
522 `))
523 require.NoError(t, err)
524 require.NotNil(t, f)
525
526 k := f.Section("").Key("FakeKey")
527 require.NotNil(t, k)
528 assert.Equal(t, []string{}, k.ValueWithShadows())
529
530 k = f.Section("").Key("keyName")
531 require.NotNil(t, k)
532 assert.Equal(t, []string{"value1", "value2"}, k.ValueWithShadows())
533 })
534 }
535
536 func TestKey_StringsWithShadows(t *testing.T) {
537 t.Run("get strings of shadows of a key", func(t *testing.T) {
538 f, err := ShadowLoad([]byte(""))
539 require.NoError(t, err)
540 require.NotNil(t, f)
541
542 k, err := f.Section("").NewKey("NUMS", "1,2")
543 require.NoError(t, err)
544 require.NotNil(t, k)
545 k, err = f.Section("").NewKey("NUMS", "4,5,6")
546 require.NoError(t, err)
547 require.NotNil(t, k)
548
549 assert.Equal(t, []string{"1", "2", "4", "5", "6"}, k.StringsWithShadows(","))
550 })
551 }
552
553 func TestKey_SetValue(t *testing.T) {
554 t.Run("set value of key", func(t *testing.T) {
555 f := Empty()
556 require.NotNil(t, f)
557
558 k, err := f.Section("").NewKey("NAME", "ini")
559 require.NoError(t, err)
560 require.NotNil(t, k)
561 assert.Equal(t, "ini", k.Value())
562
563 k.SetValue("ini.v1")
564 assert.Equal(t, "ini.v1", k.Value())
565 })
566 }
567
568 func TestKey_NestedValues(t *testing.T) {
569 t.Run("read and write nested values", func(t *testing.T) {
570 f, err := LoadSources(LoadOptions{
571 AllowNestedValues: true,
572 }, []byte(`
573 aws_access_key_id = foo
574 aws_secret_access_key = bar
575 region = us-west-2
576 s3 =
577 max_concurrent_requests=10
578 max_queue_size=1000`))
579 require.NoError(t, err)
580 require.NotNil(t, f)
581
582 assert.Equal(t, []string{"max_concurrent_requests=10", "max_queue_size=1000"}, f.Section("").Key("s3").NestedValues())
583
584 var buf bytes.Buffer
585 _, err = f.WriteTo(&buf)
586 require.NoError(t, err)
587 assert.Equal(t, `aws_access_key_id = foo
588 aws_secret_access_key = bar
589 region = us-west-2
590 s3 =
591 max_concurrent_requests=10
592 max_queue_size=1000
593 `,
594 buf.String(),
595 )
596 })
597 }
598
599 func TestRecursiveValues(t *testing.T) {
600 t.Run("recursive values should not reflect on same key", func(t *testing.T) {
601 f, err := Load([]byte(`
602 NAME = ini
603 expires = yes
604 [package]
605 NAME = %(NAME)s
606 expires = %(expires)s`))
607 require.NoError(t, err)
608 require.NotNil(t, f)
609
610 assert.Equal(t, "ini", f.Section("package").Key("NAME").String())
611 assert.Equal(t, "yes", f.Section("package").Key("expires").String())
612 })
613
614 t.Run("recursive value with no target found", func(t *testing.T) {
615 f, err := Load([]byte(`
616 [foo]
617 bar = %(missing)s
618 `))
619 require.NoError(t, err)
620 require.NotNil(t, f)
621
622 assert.Equal(t, "%(missing)s", f.Section("foo").Key("bar").String())
623 })
624 }
625
View as plain text