Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/river/internal/value/decode_test.go
4096 views
1
package value_test
2
3
import (
4
"fmt"
5
"reflect"
6
"testing"
7
"time"
8
"unsafe"
9
10
"github.com/grafana/agent/pkg/river/internal/value"
11
"github.com/stretchr/testify/assert"
12
"github.com/stretchr/testify/require"
13
)
14
15
func TestDecode_Numbers(t *testing.T) {
16
// There's a lot of values that can represent numbers, so we construct a
17
// matrix dynamically of all the combinations here.
18
vals := []interface{}{
19
int(15), int8(15), int16(15), int32(15), int64(15),
20
uint(15), uint8(15), uint16(15), uint32(15), uint64(15),
21
float32(15), float64(15),
22
string("15"), // string holding a valid number (which can be converted to a number)
23
}
24
25
for _, input := range vals {
26
for _, expect := range vals {
27
val := value.Encode(input)
28
29
name := fmt.Sprintf(
30
"%s to %s",
31
reflect.TypeOf(input),
32
reflect.TypeOf(expect),
33
)
34
35
t.Run(name, func(t *testing.T) {
36
vPtr := reflect.New(reflect.TypeOf(expect)).Interface()
37
require.NoError(t, value.Decode(val, vPtr))
38
39
actual := reflect.ValueOf(vPtr).Elem().Interface()
40
require.Equal(t, expect, actual)
41
})
42
}
43
}
44
}
45
46
func TestDecode(t *testing.T) {
47
// Declare some types to use for testing. Person2 is used as a struct
48
// equivalent to Person, but with a different Go type to force casting.
49
type Person struct {
50
Name string `river:"name,attr"`
51
}
52
53
type Person2 struct {
54
Name string `river:"name,attr"`
55
}
56
57
tt := []struct {
58
input, expect interface{}
59
}{
60
{nil, (*int)(nil)},
61
62
// Non-number primitives.
63
{string("Hello!"), string("Hello!")},
64
{bool(true), bool(true)},
65
66
// Arrays
67
{[]int{1, 2, 3}, []int{1, 2, 3}},
68
{[]int{1, 2, 3}, [...]int{1, 2, 3}},
69
{[...]int{1, 2, 3}, []int{1, 2, 3}},
70
{[...]int{1, 2, 3}, [...]int{1, 2, 3}},
71
72
// Maps
73
{map[string]int{"year": 2022}, map[string]uint{"year": 2022}},
74
{map[string]string{"name": "John"}, map[string]string{"name": "John"}},
75
{map[string]string{"name": "John"}, Person{Name: "John"}},
76
{Person{Name: "John"}, map[string]string{"name": "John"}},
77
{Person{Name: "John"}, Person{Name: "John"}},
78
{Person{Name: "John"}, Person2{Name: "John"}},
79
{Person2{Name: "John"}, Person{Name: "John"}},
80
81
// NOTE(rfratto): we don't test capsules or functions here because they're
82
// not comparable in the same way as we do the other tests.
83
//
84
// See TestDecode_Functions and TestDecode_Capsules for specific decoding
85
// tests of those types.
86
}
87
88
for _, tc := range tt {
89
val := value.Encode(tc.input)
90
91
name := fmt.Sprintf(
92
"%s (%s) to %s",
93
val.Type(),
94
reflect.TypeOf(tc.input),
95
reflect.TypeOf(tc.expect),
96
)
97
98
t.Run(name, func(t *testing.T) {
99
vPtr := reflect.New(reflect.TypeOf(tc.expect)).Interface()
100
require.NoError(t, value.Decode(val, vPtr))
101
102
actual := reflect.ValueOf(vPtr).Elem().Interface()
103
104
require.Equal(t, tc.expect, actual)
105
})
106
}
107
}
108
109
// TestDecode_PreservePointer ensures that pointer addresses can be preserved
110
// when decoding.
111
func TestDecode_PreservePointer(t *testing.T) {
112
num := 5
113
val := value.Encode(&num)
114
115
var nump *int
116
require.NoError(t, value.Decode(val, &nump))
117
require.Equal(t, unsafe.Pointer(nump), unsafe.Pointer(&num))
118
}
119
120
// TestDecode_PreserveMapReference ensures that map references can be preserved
121
// when decoding.
122
func TestDecode_PreserveMapReference(t *testing.T) {
123
m := make(map[string]string)
124
val := value.Encode(m)
125
126
var actual map[string]string
127
require.NoError(t, value.Decode(val, &actual))
128
129
// We can't check to see if the pointers of m and actual match, but we can
130
// modify m to see if actual is also modified.
131
m["foo"] = "bar"
132
require.Equal(t, "bar", actual["foo"])
133
}
134
135
// TestDecode_PreserveSliceReference ensures that slice references can be
136
// preserved when decoding.
137
func TestDecode_PreserveSliceReference(t *testing.T) {
138
s := make([]string, 3)
139
val := value.Encode(s)
140
141
var actual []string
142
require.NoError(t, value.Decode(val, &actual))
143
144
// We can't check to see if the pointers of m and actual match, but we can
145
// modify s to see if actual is also modified.
146
s[0] = "Hello, world!"
147
require.Equal(t, "Hello, world!", actual[0])
148
}
149
func TestDecode_Functions(t *testing.T) {
150
val := value.Encode(func() int { return 15 })
151
152
var f func() int
153
require.NoError(t, value.Decode(val, &f))
154
require.Equal(t, 15, f())
155
}
156
157
func TestDecode_Capsules(t *testing.T) {
158
expect := make(chan int, 5)
159
160
var actual chan int
161
require.NoError(t, value.Decode(value.Encode(expect), &actual))
162
require.Equal(t, expect, actual)
163
}
164
165
type ValueInterface interface{ SomeMethod() }
166
167
type Value1 struct{ test string }
168
169
func (c Value1) SomeMethod() {}
170
171
// TestDecode_CapsuleInterface tests that we are able to decode when
172
// the target `into` is an interface.
173
func TestDecode_CapsuleInterface(t *testing.T) {
174
tt := []struct {
175
name string
176
value ValueInterface
177
expected ValueInterface
178
}{
179
{
180
name: "Capsule to Capsule",
181
value: Value1{test: "true"},
182
expected: Value1{test: "true"},
183
},
184
{
185
name: "Capsule Pointer to Capsule",
186
value: &Value1{test: "true"},
187
expected: &Value1{test: "true"},
188
},
189
}
190
191
for _, tc := range tt {
192
t.Run(tc.name, func(t *testing.T) {
193
var actual ValueInterface
194
require.NoError(t, value.Decode(value.Encode(tc.value), &actual))
195
196
// require.Same validates the memory address matches after Decode.
197
if reflect.TypeOf(tc.value).Kind() == reflect.Pointer {
198
require.Same(t, tc.value, actual)
199
}
200
201
// We use tc.expected to validate the properties of actual match the
202
// original tc.value properties (nothing has mutated them during the test).
203
require.Equal(t, tc.expected, actual)
204
})
205
}
206
}
207
208
// TestDecode_CapsulesError tests that we are unable to decode when
209
// the target `into` is not an interface.
210
func TestDecode_CapsulesError(t *testing.T) {
211
type Capsule1 struct{ test string }
212
type Capsule2 Capsule1
213
214
v := Capsule1{test: "true"}
215
actual := Capsule2{}
216
217
require.EqualError(t, value.Decode(value.Encode(v), &actual), `expected capsule("value_test.Capsule2"), got capsule("value_test.Capsule1")`)
218
}
219
220
// TestDecodeCopy_SliceCopy ensures that copies are made during decoding
221
// instead of setting values directly.
222
func TestDecodeCopy_SliceCopy(t *testing.T) {
223
orig := []int{1, 2, 3}
224
225
var res []int
226
require.NoError(t, value.DecodeCopy(value.Encode(orig), &res))
227
228
res[0] = 10
229
require.Equal(t, []int{1, 2, 3}, orig, "Original slice should not have been modified")
230
}
231
232
// TestDecodeCopy_ArrayCopy ensures that copies are made during decoding
233
// instead of setting values directly.
234
func TestDecode_ArrayCopy(t *testing.T) {
235
orig := [...]int{1, 2, 3}
236
237
var res [3]int
238
require.NoError(t, value.DecodeCopy(value.Encode(orig), &res))
239
240
res[0] = 10
241
require.Equal(t, [3]int{1, 2, 3}, orig, "Original array should not have been modified")
242
}
243
244
func TestDecode_CustomTypes(t *testing.T) {
245
t.Run("object to Unmarshaler", func(t *testing.T) {
246
var actual customUnmarshaler
247
require.NoError(t, value.Decode(value.Object(nil), &actual))
248
require.True(t, actual.Called, "UnmarshalRiver was not invoked")
249
})
250
251
t.Run("TextMarshaler to TextUnmarshaler", func(t *testing.T) {
252
now := time.Now()
253
254
var actual time.Time
255
require.NoError(t, value.Decode(value.Encode(now), &actual))
256
require.True(t, now.Equal(actual))
257
})
258
259
t.Run("time.Duration to time.Duration", func(t *testing.T) {
260
dur := 15 * time.Second
261
262
var actual time.Duration
263
require.NoError(t, value.Decode(value.Encode(dur), &actual))
264
require.Equal(t, dur, actual)
265
})
266
267
t.Run("string to TextUnmarshaler", func(t *testing.T) {
268
now := time.Now()
269
nowBytes, _ := now.MarshalText()
270
271
var actual time.Time
272
require.NoError(t, value.Decode(value.String(string(nowBytes)), &actual))
273
274
actualBytes, _ := actual.MarshalText()
275
require.Equal(t, nowBytes, actualBytes)
276
})
277
278
t.Run("string to time.Duration", func(t *testing.T) {
279
dur := 15 * time.Second
280
281
var actual time.Duration
282
require.NoError(t, value.Decode(value.String(dur.String()), &actual))
283
require.Equal(t, dur.String(), actual.String())
284
})
285
}
286
287
type customUnmarshaler struct {
288
Called bool `river:"called,attr,optional"`
289
}
290
291
func (cu *customUnmarshaler) UnmarshalRiver(f func(interface{}) error) error {
292
cu.Called = true
293
294
type s customUnmarshaler
295
return f((*s)(cu))
296
}
297
298
type textEnumType bool
299
300
func (et *textEnumType) UnmarshalText(text []byte) error {
301
*et = false
302
303
switch string(text) {
304
case "accepted_value":
305
*et = true
306
return nil
307
default:
308
return fmt.Errorf("unrecognized value %q", string(text))
309
}
310
}
311
312
func TestDecode_TextUnmarshaler(t *testing.T) {
313
t.Run("valid type and value", func(t *testing.T) {
314
var et textEnumType
315
require.NoError(t, value.Decode(value.String("accepted_value"), &et))
316
require.Equal(t, textEnumType(true), et)
317
})
318
319
t.Run("invalid type", func(t *testing.T) {
320
var et textEnumType
321
err := value.Decode(value.Bool(true), &et)
322
require.EqualError(t, err, "expected string, got bool")
323
})
324
325
t.Run("invalid value", func(t *testing.T) {
326
var et textEnumType
327
err := value.Decode(value.String("bad_value"), &et)
328
require.EqualError(t, err, `unrecognized value "bad_value"`)
329
})
330
331
t.Run("unmarshaler nested in other value", func(t *testing.T) {
332
input := value.Array(
333
value.String("accepted_value"),
334
value.String("accepted_value"),
335
value.String("accepted_value"),
336
)
337
338
var ett []textEnumType
339
require.NoError(t, value.Decode(input, &ett))
340
require.Equal(t, []textEnumType{true, true, true}, ett)
341
})
342
}
343
344
func TestDecode_ErrorChain(t *testing.T) {
345
type Target struct {
346
Key struct {
347
Object struct {
348
Field1 []int `river:"field1,attr"`
349
} `river:"object,attr"`
350
} `river:"key,attr"`
351
}
352
353
val := value.Object(map[string]value.Value{
354
"key": value.Object(map[string]value.Value{
355
"object": value.Object(map[string]value.Value{
356
"field1": value.Array(
357
value.Int(15),
358
value.Int(30),
359
value.String("Hello, world!"),
360
),
361
}),
362
}),
363
})
364
365
// NOTE(rfratto): strings of errors from the value package are fairly limited
366
// in the amount of information they show, since the value package doesn't
367
// have a great way to pretty-print the chain of errors.
368
//
369
// For example, with the error below, the message doesn't explain where the
370
// string is coming from, even though the error values hold that context.
371
//
372
// Callers consuming errors should print the error chain with extra context
373
// so it's more useful to users.
374
err := value.Decode(val, &Target{})
375
expectErr := `expected number, got string`
376
require.EqualError(t, err, expectErr)
377
}
378
379
type boolish int
380
381
var _ value.ConvertibleFromCapsule = (*boolish)(nil)
382
var _ value.ConvertibleIntoCapsule = (boolish)(0)
383
384
func (b boolish) RiverCapsule() {}
385
386
func (b *boolish) ConvertFrom(src interface{}) error {
387
switch v := src.(type) {
388
case bool:
389
if v {
390
*b = 1
391
} else {
392
*b = 0
393
}
394
return nil
395
}
396
397
return value.ErrNoConversion
398
}
399
400
func (b boolish) ConvertInto(dst interface{}) error {
401
switch d := dst.(type) {
402
case *bool:
403
if b == 0 {
404
*d = false
405
} else {
406
*d = true
407
}
408
return nil
409
}
410
411
return value.ErrNoConversion
412
}
413
414
func TestDecode_CustomConvert(t *testing.T) {
415
t.Run("compatible type to custom", func(t *testing.T) {
416
var b boolish
417
err := value.Decode(value.Bool(true), &b)
418
require.NoError(t, err)
419
require.Equal(t, boolish(1), b)
420
})
421
422
t.Run("custom to compatible type", func(t *testing.T) {
423
var b bool
424
err := value.Decode(value.Encapsulate(boolish(10)), &b)
425
require.NoError(t, err)
426
require.Equal(t, true, b)
427
})
428
429
t.Run("incompatible type to custom", func(t *testing.T) {
430
var b boolish
431
err := value.Decode(value.String("true"), &b)
432
require.EqualError(t, err, "expected capsule, got string")
433
})
434
435
t.Run("custom to incompatible type", func(t *testing.T) {
436
src := boolish(10)
437
438
var s string
439
err := value.Decode(value.Encapsulate(&src), &s)
440
require.EqualError(t, err, "expected string, got capsule")
441
})
442
}
443
444
func TestDecode_SquashedFields(t *testing.T) {
445
type InnerStruct struct {
446
InnerField1 string `river:"inner_field_1,attr,optional"`
447
InnerField2 string `river:"inner_field_2,attr,optional"`
448
}
449
450
type OuterStruct struct {
451
OuterField1 string `river:"outer_field_1,attr,optional"`
452
Inner InnerStruct `river:",squash"`
453
OuterField2 string `river:"outer_field_2,attr,optional"`
454
}
455
456
var (
457
in = map[string]string{
458
"outer_field_1": "value1",
459
"outer_field_2": "value2",
460
"inner_field_1": "value3",
461
"inner_field_2": "value4",
462
}
463
expect = OuterStruct{
464
OuterField1: "value1",
465
Inner: InnerStruct{
466
InnerField1: "value3",
467
InnerField2: "value4",
468
},
469
OuterField2: "value2",
470
}
471
)
472
473
var out OuterStruct
474
err := value.Decode(value.Encode(in), &out)
475
require.NoError(t, err)
476
require.Equal(t, expect, out)
477
}
478
479
func TestDecode_SquashedFields_Pointer(t *testing.T) {
480
type InnerStruct struct {
481
InnerField1 string `river:"inner_field_1,attr,optional"`
482
InnerField2 string `river:"inner_field_2,attr,optional"`
483
}
484
485
type OuterStruct struct {
486
OuterField1 string `river:"outer_field_1,attr,optional"`
487
Inner *InnerStruct `river:",squash"`
488
OuterField2 string `river:"outer_field_2,attr,optional"`
489
}
490
491
var (
492
in = map[string]string{
493
"outer_field_1": "value1",
494
"outer_field_2": "value2",
495
"inner_field_1": "value3",
496
"inner_field_2": "value4",
497
}
498
expect = OuterStruct{
499
OuterField1: "value1",
500
Inner: &InnerStruct{
501
InnerField1: "value3",
502
InnerField2: "value4",
503
},
504
OuterField2: "value2",
505
}
506
)
507
508
var out OuterStruct
509
err := value.Decode(value.Encode(in), &out)
510
require.NoError(t, err)
511
require.Equal(t, expect, out)
512
}
513
514
func TestDecode_Slice(t *testing.T) {
515
type Block struct {
516
Attr int `river:"attr,attr"`
517
}
518
519
type Struct struct {
520
Blocks []Block `river:"block.a,block,optional"`
521
}
522
523
var (
524
in = map[string]interface{}{
525
"block": map[string]interface{}{
526
"a": []map[string]interface{}{
527
{"attr": 1},
528
{"attr": 2},
529
{"attr": 3},
530
{"attr": 4},
531
},
532
},
533
}
534
expect = Struct{
535
Blocks: []Block{
536
{Attr: 1},
537
{Attr: 2},
538
{Attr: 3},
539
{Attr: 4},
540
},
541
}
542
)
543
544
var out Struct
545
err := value.Decode(value.Encode(in), &out)
546
require.NoError(t, err)
547
require.Equal(t, expect, out)
548
}
549
550
func TestDecode_SquashedSlice(t *testing.T) {
551
type Block struct {
552
Attr int `river:"attr,attr"`
553
}
554
555
type InnerStruct struct {
556
BlockA Block `river:"a,block,optional"`
557
BlockB Block `river:"b,block,optional"`
558
BlockC Block `river:"c,block,optional"`
559
}
560
561
type OuterStruct struct {
562
OuterField1 string `river:"outer_field_1,attr,optional"`
563
Inner []InnerStruct `river:"block,enum"`
564
OuterField2 string `river:"outer_field_2,attr,optional"`
565
}
566
567
var (
568
in = map[string]interface{}{
569
"outer_field_1": "value1",
570
"outer_field_2": "value2",
571
572
"block": []map[string]interface{}{
573
{"a": map[string]interface{}{"attr": 1}},
574
{"b": map[string]interface{}{"attr": 2}},
575
{"c": map[string]interface{}{"attr": 3}},
576
{"a": map[string]interface{}{"attr": 4}},
577
},
578
}
579
expect = OuterStruct{
580
OuterField1: "value1",
581
OuterField2: "value2",
582
583
Inner: []InnerStruct{
584
{BlockA: Block{Attr: 1}},
585
{BlockB: Block{Attr: 2}},
586
{BlockC: Block{Attr: 3}},
587
{BlockA: Block{Attr: 4}},
588
},
589
}
590
)
591
592
var out OuterStruct
593
err := value.Decode(value.Encode(in), &out)
594
require.NoError(t, err)
595
require.Equal(t, expect, out)
596
}
597
598
func TestDecode_SquashedSlice_Pointer(t *testing.T) {
599
type Block struct {
600
Attr int `river:"attr,attr"`
601
}
602
603
type InnerStruct struct {
604
BlockA *Block `river:"a,block,optional"`
605
BlockB *Block `river:"b,block,optional"`
606
BlockC *Block `river:"c,block,optional"`
607
}
608
609
type OuterStruct struct {
610
OuterField1 string `river:"outer_field_1,attr,optional"`
611
Inner []InnerStruct `river:"block,enum"`
612
OuterField2 string `river:"outer_field_2,attr,optional"`
613
}
614
615
var (
616
in = map[string]interface{}{
617
"outer_field_1": "value1",
618
"outer_field_2": "value2",
619
620
"block": []map[string]interface{}{
621
{"a": map[string]interface{}{"attr": 1}},
622
{"b": map[string]interface{}{"attr": 2}},
623
{"c": map[string]interface{}{"attr": 3}},
624
{"a": map[string]interface{}{"attr": 4}},
625
},
626
}
627
expect = OuterStruct{
628
OuterField1: "value1",
629
OuterField2: "value2",
630
631
Inner: []InnerStruct{
632
{BlockA: &Block{Attr: 1}},
633
{BlockB: &Block{Attr: 2}},
634
{BlockC: &Block{Attr: 3}},
635
{BlockA: &Block{Attr: 4}},
636
},
637
}
638
)
639
640
var out OuterStruct
641
err := value.Decode(value.Encode(in), &out)
642
require.NoError(t, err)
643
require.Equal(t, expect, out)
644
}
645
646
// TestDecode_KnownTypes_Any asserts that decoding River values into an
647
// any/interface{} results in known types.
648
func TestDecode_KnownTypes_Any(t *testing.T) {
649
tt := []struct {
650
input any
651
expect any
652
}{
653
// All numbers must decode to float64.
654
{int(15), float64(15)},
655
{int8(15), float64(15)},
656
{int16(15), float64(15)},
657
{int32(15), float64(15)},
658
{int64(15), float64(15)},
659
{uint(15), float64(15)},
660
{uint8(15), float64(15)},
661
{uint16(15), float64(15)},
662
{uint32(15), float64(15)},
663
{uint64(15), float64(15)},
664
{float32(2.5), float64(2.5)},
665
{float64(2.5), float64(2.5)},
666
667
{bool(true), bool(true)},
668
{string("Hello"), string("Hello")},
669
670
{
671
input: []int{1, 2, 3},
672
expect: []any{float64(1), float64(2), float64(3)},
673
},
674
675
{
676
input: map[string]int{"number": 15},
677
expect: map[string]any{"number": float64(15)},
678
},
679
{
680
input: struct {
681
Name string `river:"name,attr"`
682
}{Name: "John"},
683
684
expect: map[string]any{"name": "John"},
685
},
686
}
687
688
t.Run("basic types", func(t *testing.T) {
689
for _, tc := range tt {
690
var actual any
691
err := value.Decode(value.Encode(tc.input), &actual)
692
693
if assert.NoError(t, err) {
694
assert.Equal(t, tc.expect, actual,
695
"Expected %[1]v (%[1]T) to transcode to %[2]v (%[2]T)", tc.input, tc.expect)
696
}
697
}
698
})
699
700
t.Run("inside maps", func(t *testing.T) {
701
for _, tc := range tt {
702
input := map[string]any{
703
"key": tc.input,
704
}
705
706
var actual map[string]any
707
err := value.Decode(value.Encode(input), &actual)
708
709
if assert.NoError(t, err) {
710
assert.Equal(t, tc.expect, actual["key"],
711
"Expected %[1]v (%[1]T) to transcode to %[2]v (%[2]T) inside a map", tc.input, tc.expect)
712
}
713
}
714
})
715
}
716
717
func TestRetainCapsulePointer(t *testing.T) {
718
capsuleVal := &capsule{}
719
720
in := map[string]any{
721
"foo": capsuleVal,
722
}
723
724
var actual map[string]any
725
err := value.Decode(value.Encode(in), &actual)
726
require.NoError(t, err)
727
728
expect := map[string]any{
729
"foo": capsuleVal,
730
}
731
require.Equal(t, expect, actual)
732
}
733
734
type capsule struct{}
735
736
func (*capsule) RiverCapsule() {}
737
738