Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/river/vm/vm_block_test.go
4095 views
1
package vm_test
2
3
import (
4
"testing"
5
6
"github.com/grafana/agent/pkg/river/ast"
7
"github.com/grafana/agent/pkg/river/parser"
8
"github.com/grafana/agent/pkg/river/vm"
9
"github.com/stretchr/testify/require"
10
)
11
12
// This file contains tests for decoding blocks.
13
14
func TestVM_File(t *testing.T) {
15
type block struct {
16
String string `river:"string,attr"`
17
Number int `river:"number,attr,optional"`
18
}
19
type file struct {
20
SettingA int `river:"setting_a,attr"`
21
SettingB int `river:"setting_b,attr,optional"`
22
23
Block block `river:"some_block,block,optional"`
24
}
25
26
input := `
27
setting_a = 15
28
29
some_block {
30
string = "Hello, world!"
31
}
32
`
33
34
expect := file{
35
SettingA: 15,
36
Block: block{
37
String: "Hello, world!",
38
},
39
}
40
41
res, err := parser.ParseFile(t.Name(), []byte(input))
42
require.NoError(t, err)
43
44
eval := vm.New(res)
45
46
var actual file
47
require.NoError(t, eval.Evaluate(nil, &actual))
48
require.Equal(t, expect, actual)
49
}
50
51
func TestVM_Block_Attributes(t *testing.T) {
52
t.Run("Decodes attributes", func(t *testing.T) {
53
type block struct {
54
Number int `river:"number,attr"`
55
String string `river:"string,attr"`
56
}
57
58
input := `some_block {
59
number = 15
60
string = "Hello, world!"
61
}`
62
eval := vm.New(parseBlock(t, input))
63
64
var actual block
65
require.NoError(t, eval.Evaluate(nil, &actual))
66
require.Equal(t, 15, actual.Number)
67
require.Equal(t, "Hello, world!", actual.String)
68
})
69
70
t.Run("Fails if attribute used as block", func(t *testing.T) {
71
type block struct {
72
Number int `river:"number,attr"`
73
}
74
75
input := `some_block {
76
number {}
77
}`
78
eval := vm.New(parseBlock(t, input))
79
80
err := eval.Evaluate(nil, &block{})
81
require.EqualError(t, err, `2:4: "number" must be an attribute, but is used as a block`)
82
})
83
84
t.Run("Fails if required attributes are not present", func(t *testing.T) {
85
type block struct {
86
Number int `river:"number,attr"`
87
String string `river:"string,attr"`
88
}
89
90
input := `some_block {
91
number = 15
92
}`
93
eval := vm.New(parseBlock(t, input))
94
95
err := eval.Evaluate(nil, &block{})
96
require.EqualError(t, err, `missing required attribute "string"`)
97
})
98
99
t.Run("Succeeds if optional attributes are not present", func(t *testing.T) {
100
type block struct {
101
Number int `river:"number,attr"`
102
String string `river:"string,attr,optional"`
103
}
104
105
input := `some_block {
106
number = 15
107
}`
108
eval := vm.New(parseBlock(t, input))
109
110
var actual block
111
require.NoError(t, eval.Evaluate(nil, &actual))
112
require.Equal(t, 15, actual.Number)
113
require.Equal(t, "", actual.String)
114
})
115
116
t.Run("Fails if attribute is not defined in struct", func(t *testing.T) {
117
type block struct {
118
Number int `river:"number,attr"`
119
}
120
121
input := `some_block {
122
number = 15
123
invalid = "This attribute does not exist!"
124
}`
125
eval := vm.New(parseBlock(t, input))
126
127
err := eval.Evaluate(nil, &block{})
128
require.EqualError(t, err, `3:4: unrecognized attribute name "invalid"`)
129
})
130
131
t.Run("Supports arbitrarily nested struct pointer fields", func(t *testing.T) {
132
type block struct {
133
NumberA int `river:"number_a,attr"`
134
NumberB *int `river:"number_b,attr"`
135
NumberC **int `river:"number_c,attr"`
136
NumberD ***int `river:"number_d,attr"`
137
}
138
139
input := `some_block {
140
number_a = 15
141
number_b = 20
142
number_c = 25
143
number_d = 30
144
}`
145
eval := vm.New(parseBlock(t, input))
146
147
var actual block
148
require.NoError(t, eval.Evaluate(nil, &actual))
149
require.Equal(t, 15, actual.NumberA)
150
require.Equal(t, 20, *actual.NumberB)
151
require.Equal(t, 25, **actual.NumberC)
152
require.Equal(t, 30, ***actual.NumberD)
153
})
154
155
t.Run("Supports squashed attributes", func(t *testing.T) {
156
type InnerStruct struct {
157
InnerField1 string `river:"inner_field_1,attr,optional"`
158
InnerField2 string `river:"inner_field_2,attr,optional"`
159
}
160
161
type OuterStruct struct {
162
OuterField1 string `river:"outer_field_1,attr,optional"`
163
Inner InnerStruct `river:",squash"`
164
OuterField2 string `river:"outer_field_2,attr,optional"`
165
}
166
167
var (
168
input = `some_block {
169
outer_field_1 = "value1"
170
outer_field_2 = "value2"
171
inner_field_1 = "value3"
172
inner_field_2 = "value4"
173
}`
174
175
expect = OuterStruct{
176
OuterField1: "value1",
177
Inner: InnerStruct{
178
InnerField1: "value3",
179
InnerField2: "value4",
180
},
181
OuterField2: "value2",
182
}
183
)
184
eval := vm.New(parseBlock(t, input))
185
186
var actual OuterStruct
187
require.NoError(t, eval.Evaluate(nil, &actual))
188
require.Equal(t, expect, actual)
189
})
190
191
t.Run("Supports squashed attributes in pointers", func(t *testing.T) {
192
type InnerStruct struct {
193
InnerField1 string `river:"inner_field_1,attr,optional"`
194
InnerField2 string `river:"inner_field_2,attr,optional"`
195
}
196
197
type OuterStruct struct {
198
OuterField1 string `river:"outer_field_1,attr,optional"`
199
Inner *InnerStruct `river:",squash"`
200
OuterField2 string `river:"outer_field_2,attr,optional"`
201
}
202
203
var (
204
input = `some_block {
205
outer_field_1 = "value1"
206
outer_field_2 = "value2"
207
inner_field_1 = "value3"
208
inner_field_2 = "value4"
209
}`
210
211
expect = OuterStruct{
212
OuterField1: "value1",
213
Inner: &InnerStruct{
214
InnerField1: "value3",
215
InnerField2: "value4",
216
},
217
OuterField2: "value2",
218
}
219
)
220
eval := vm.New(parseBlock(t, input))
221
222
var actual OuterStruct
223
require.NoError(t, eval.Evaluate(nil, &actual))
224
require.Equal(t, expect, actual)
225
})
226
}
227
228
func TestVM_Block_Children_Blocks(t *testing.T) {
229
type childBlock struct {
230
Attr bool `river:"attr,attr"`
231
}
232
233
t.Run("Decodes children blocks", func(t *testing.T) {
234
type block struct {
235
Value int `river:"value,attr"`
236
Child childBlock `river:"child.block,block"`
237
}
238
239
input := `some_block {
240
value = 15
241
242
child.block { attr = true }
243
}`
244
eval := vm.New(parseBlock(t, input))
245
246
var actual block
247
require.NoError(t, eval.Evaluate(nil, &actual))
248
require.Equal(t, 15, actual.Value)
249
require.True(t, actual.Child.Attr)
250
})
251
252
t.Run("Decodes multiple instances of children blocks", func(t *testing.T) {
253
type block struct {
254
Value int `river:"value,attr"`
255
Children []childBlock `river:"child.block,block"`
256
}
257
258
input := `some_block {
259
value = 10
260
261
child.block { attr = true }
262
child.block { attr = false }
263
child.block { attr = true }
264
}`
265
eval := vm.New(parseBlock(t, input))
266
267
var actual block
268
require.NoError(t, eval.Evaluate(nil, &actual))
269
require.Equal(t, 10, actual.Value)
270
require.Len(t, actual.Children, 3)
271
require.Equal(t, true, actual.Children[0].Attr)
272
require.Equal(t, false, actual.Children[1].Attr)
273
require.Equal(t, true, actual.Children[2].Attr)
274
})
275
276
t.Run("Decodes multiple instances of children blocks into an array", func(t *testing.T) {
277
type block struct {
278
Value int `river:"value,attr"`
279
Children [3]childBlock `river:"child.block,block"`
280
}
281
282
input := `some_block {
283
value = 15
284
285
child.block { attr = true }
286
child.block { attr = false }
287
child.block { attr = true }
288
}`
289
eval := vm.New(parseBlock(t, input))
290
291
var actual block
292
require.NoError(t, eval.Evaluate(nil, &actual))
293
require.Equal(t, 15, actual.Value)
294
require.Equal(t, true, actual.Children[0].Attr)
295
require.Equal(t, false, actual.Children[1].Attr)
296
require.Equal(t, true, actual.Children[2].Attr)
297
})
298
299
t.Run("Fails if block used as an attribute", func(t *testing.T) {
300
type block struct {
301
Child childBlock `river:"child,block"`
302
}
303
304
input := `some_block {
305
child = 15
306
}`
307
eval := vm.New(parseBlock(t, input))
308
309
err := eval.Evaluate(nil, &block{})
310
require.EqualError(t, err, `2:4: "child" must be a block, but is used as an attribute`)
311
})
312
313
t.Run("Fails if required children blocks are not present", func(t *testing.T) {
314
type block struct {
315
Value int `river:"value,attr"`
316
Child childBlock `river:"child.block,block"`
317
}
318
319
input := `some_block {
320
value = 15
321
}`
322
eval := vm.New(parseBlock(t, input))
323
324
err := eval.Evaluate(nil, &block{})
325
require.EqualError(t, err, `missing required block "child.block"`)
326
})
327
328
t.Run("Succeeds if optional children blocks are not present", func(t *testing.T) {
329
type block struct {
330
Value int `river:"value,attr"`
331
Child childBlock `river:"child.block,block,optional"`
332
}
333
334
input := `some_block {
335
value = 15
336
}`
337
eval := vm.New(parseBlock(t, input))
338
339
var actual block
340
require.NoError(t, eval.Evaluate(nil, &actual))
341
require.Equal(t, 15, actual.Value)
342
})
343
344
t.Run("Fails if child block is not defined in struct", func(t *testing.T) {
345
type block struct {
346
Value int `river:"value,attr"`
347
}
348
349
input := `some_block {
350
value = 15
351
352
child.block { attr = true }
353
}`
354
eval := vm.New(parseBlock(t, input))
355
356
err := eval.Evaluate(nil, &block{})
357
require.EqualError(t, err, `4:4: unrecognized block name "child.block"`)
358
})
359
360
t.Run("Supports arbitrarily nested struct pointer fields", func(t *testing.T) {
361
type block struct {
362
BlockA childBlock `river:"block_a,block"`
363
BlockB *childBlock `river:"block_b,block"`
364
BlockC **childBlock `river:"block_c,block"`
365
BlockD ***childBlock `river:"block_d,block"`
366
}
367
368
input := `some_block {
369
block_a { attr = true }
370
block_b { attr = false }
371
block_c { attr = true }
372
block_d { attr = false }
373
}`
374
eval := vm.New(parseBlock(t, input))
375
376
var actual block
377
require.NoError(t, eval.Evaluate(nil, &actual))
378
require.Equal(t, true, (actual.BlockA).Attr)
379
require.Equal(t, false, (*actual.BlockB).Attr)
380
require.Equal(t, true, (**actual.BlockC).Attr)
381
require.Equal(t, false, (***actual.BlockD).Attr)
382
})
383
384
t.Run("Supports squashed blocks", func(t *testing.T) {
385
type InnerStruct struct {
386
Inner1 childBlock `river:"inner_block_1,block"`
387
Inner2 childBlock `river:"inner_block_2,block"`
388
}
389
390
type OuterStruct struct {
391
Outer1 childBlock `river:"outer_block_1,block"`
392
Inner InnerStruct `river:",squash"`
393
Outer2 childBlock `river:"outer_block_2,block"`
394
}
395
396
var (
397
input = `some_block {
398
outer_block_1 { attr = true }
399
outer_block_2 { attr = false }
400
inner_block_1 { attr = true }
401
inner_block_2 { attr = false }
402
}`
403
404
expect = OuterStruct{
405
Outer1: childBlock{Attr: true},
406
Outer2: childBlock{Attr: false},
407
Inner: InnerStruct{
408
Inner1: childBlock{Attr: true},
409
Inner2: childBlock{Attr: false},
410
},
411
}
412
)
413
eval := vm.New(parseBlock(t, input))
414
415
var actual OuterStruct
416
require.NoError(t, eval.Evaluate(nil, &actual))
417
require.Equal(t, expect, actual)
418
})
419
420
t.Run("Supports squashed blocks in pointers", func(t *testing.T) {
421
type InnerStruct struct {
422
Inner1 *childBlock `river:"inner_block_1,block"`
423
Inner2 *childBlock `river:"inner_block_2,block"`
424
}
425
426
type OuterStruct struct {
427
Outer1 childBlock `river:"outer_block_1,block"`
428
Inner *InnerStruct `river:",squash"`
429
Outer2 childBlock `river:"outer_block_2,block"`
430
}
431
432
var (
433
input = `some_block {
434
outer_block_1 { attr = true }
435
outer_block_2 { attr = false }
436
inner_block_1 { attr = true }
437
inner_block_2 { attr = false }
438
}`
439
440
expect = OuterStruct{
441
Outer1: childBlock{Attr: true},
442
Outer2: childBlock{Attr: false},
443
Inner: &InnerStruct{
444
Inner1: &childBlock{Attr: true},
445
Inner2: &childBlock{Attr: false},
446
},
447
}
448
)
449
eval := vm.New(parseBlock(t, input))
450
451
var actual OuterStruct
452
require.NoError(t, eval.Evaluate(nil, &actual))
453
require.Equal(t, expect, actual)
454
})
455
456
// TODO(rfratto): decode all blocks into a []*ast.BlockStmt field.
457
}
458
459
func TestVM_Block_Enum_Block(t *testing.T) {
460
type childBlock struct {
461
Attr int `river:"attr,attr"`
462
}
463
464
type enumBlock struct {
465
BlockA *childBlock `river:"a,block,optional"`
466
BlockB *childBlock `river:"b,block,optional"`
467
BlockC *childBlock `river:"c,block,optional"`
468
BlockD *childBlock `river:"d,block,optional"`
469
}
470
471
t.Run("Decodes enum blocks", func(t *testing.T) {
472
type block struct {
473
Value int `river:"value,attr"`
474
Blocks []*enumBlock `river:"child,enum,optional"`
475
}
476
477
input := `some_block {
478
value = 15
479
480
child.a { attr = 1 }
481
}`
482
eval := vm.New(parseBlock(t, input))
483
484
expect := block{
485
Value: 15,
486
Blocks: []*enumBlock{
487
{BlockA: &childBlock{Attr: 1}},
488
},
489
}
490
491
var actual block
492
require.NoError(t, eval.Evaluate(nil, &actual))
493
require.Equal(t, expect, actual)
494
})
495
496
t.Run("Decodes multiple enum blocks", func(t *testing.T) {
497
type block struct {
498
Value int `river:"value,attr"`
499
Blocks []*enumBlock `river:"child,enum,optional"`
500
}
501
502
input := `some_block {
503
value = 15
504
505
child.b { attr = 1 }
506
child.a { attr = 2 }
507
child.c { attr = 3 }
508
}`
509
eval := vm.New(parseBlock(t, input))
510
511
expect := block{
512
Value: 15,
513
Blocks: []*enumBlock{
514
{BlockB: &childBlock{Attr: 1}},
515
{BlockA: &childBlock{Attr: 2}},
516
{BlockC: &childBlock{Attr: 3}},
517
},
518
}
519
520
var actual block
521
require.NoError(t, eval.Evaluate(nil, &actual))
522
require.Equal(t, expect, actual)
523
})
524
525
t.Run("Decodes multiple enum blocks with repeating blocks", func(t *testing.T) {
526
type block struct {
527
Value int `river:"value,attr"`
528
Blocks []*enumBlock `river:"child,enum,optional"`
529
}
530
531
input := `some_block {
532
value = 15
533
534
child.a { attr = 1 }
535
child.b { attr = 2 }
536
child.c { attr = 3 }
537
child.a { attr = 4 }
538
}`
539
eval := vm.New(parseBlock(t, input))
540
541
expect := block{
542
Value: 15,
543
Blocks: []*enumBlock{
544
{BlockA: &childBlock{Attr: 1}},
545
{BlockB: &childBlock{Attr: 2}},
546
{BlockC: &childBlock{Attr: 3}},
547
{BlockA: &childBlock{Attr: 4}},
548
},
549
}
550
551
var actual block
552
require.NoError(t, eval.Evaluate(nil, &actual))
553
require.Equal(t, expect, actual)
554
})
555
}
556
557
func TestVM_Block_Label(t *testing.T) {
558
t.Run("Decodes label into string field", func(t *testing.T) {
559
type block struct {
560
Label string `river:",label"`
561
}
562
563
input := `some_block "label_value_1" {}`
564
eval := vm.New(parseBlock(t, input))
565
566
var actual block
567
require.NoError(t, eval.Evaluate(nil, &actual))
568
require.Equal(t, "label_value_1", actual.Label)
569
})
570
571
t.Run("Struct must have label field if block is labeled", func(t *testing.T) {
572
type block struct{}
573
574
input := `some_block "label_value_2" {}`
575
eval := vm.New(parseBlock(t, input))
576
577
err := eval.Evaluate(nil, &block{})
578
require.EqualError(t, err, `1:1: block "some_block" does not support specifying labels`)
579
})
580
581
t.Run("Block must have label if struct accepts label", func(t *testing.T) {
582
type block struct {
583
Label string `river:",label"`
584
}
585
586
input := `some_block {}`
587
eval := vm.New(parseBlock(t, input))
588
589
err := eval.Evaluate(nil, &block{})
590
require.EqualError(t, err, `1:1: block "some_block" requires non-empty label`)
591
})
592
593
t.Run("Block must have non-empty label if struct accepts label", func(t *testing.T) {
594
type block struct {
595
Label string `river:",label"`
596
}
597
598
input := `some_block "" {}`
599
eval := vm.New(parseBlock(t, input))
600
601
err := eval.Evaluate(nil, &block{})
602
require.EqualError(t, err, `1:1: block "some_block" requires non-empty label`)
603
})
604
}
605
606
func TestVM_Block_Unmarshaler(t *testing.T) {
607
type OuterBlock struct {
608
FieldA string `river:"field_a,attr"`
609
Settings Setting `river:"some.settings,block"`
610
}
611
612
input := `
613
field_a = "foobar"
614
some.settings {
615
field_a = "fizzbuzz"
616
field_b = "helloworld"
617
}
618
`
619
620
file, err := parser.ParseFile(t.Name(), []byte(input))
621
require.NoError(t, err)
622
623
eval := vm.New(file)
624
625
var actual OuterBlock
626
require.NoError(t, eval.Evaluate(nil, &actual))
627
require.True(t, actual.Settings.Called, "UnmarshalRiver did not get invoked")
628
}
629
630
func TestVM_Block_UnmarshalToMap(t *testing.T) {
631
type OuterBlock struct {
632
Settings map[string]interface{} `river:"some.settings,block"`
633
}
634
635
tt := []struct {
636
name string
637
input string
638
expect OuterBlock
639
expectError string
640
}{
641
{
642
name: "decodes successfully",
643
input: `
644
some.settings {
645
field_a = 12345
646
field_b = "helloworld"
647
}
648
`,
649
expect: OuterBlock{
650
Settings: map[string]interface{}{
651
"field_a": float64(12345),
652
"field_b": "helloworld",
653
},
654
},
655
},
656
{
657
name: "rejects labeled blocks",
658
input: `
659
some.settings "foo" {
660
field_a = 12345
661
}
662
`,
663
expectError: `block "some.settings" requires non-empty label`,
664
},
665
666
{
667
name: "rejects nested maps",
668
input: `
669
some.settings {
670
inner_map {
671
field_a = 12345
672
}
673
}
674
`,
675
expectError: "nested blocks not supported here",
676
},
677
}
678
679
for _, tc := range tt {
680
t.Run(tc.name, func(t *testing.T) {
681
file, err := parser.ParseFile(t.Name(), []byte(tc.input))
682
require.NoError(t, err)
683
684
eval := vm.New(file)
685
686
var actual OuterBlock
687
err = eval.Evaluate(nil, &actual)
688
689
if tc.expectError == "" {
690
require.NoError(t, err)
691
require.Equal(t, tc.expect, actual)
692
} else {
693
require.ErrorContains(t, err, tc.expectError)
694
}
695
})
696
}
697
}
698
699
func TestVM_Block_UnmarshalToAny(t *testing.T) {
700
type OuterBlock struct {
701
Settings any `river:"some.settings,block"`
702
}
703
704
input := `
705
some.settings {
706
field_a = 12345
707
field_b = "helloworld"
708
}
709
`
710
711
file, err := parser.ParseFile(t.Name(), []byte(input))
712
require.NoError(t, err)
713
714
eval := vm.New(file)
715
716
var actual OuterBlock
717
require.NoError(t, eval.Evaluate(nil, &actual))
718
719
expect := map[string]interface{}{
720
"field_a": float64(12345),
721
"field_b": "helloworld",
722
}
723
require.Equal(t, expect, actual.Settings)
724
}
725
726
type Setting struct {
727
FieldA string `river:"field_a,attr"`
728
FieldB string `river:"field_b,attr"`
729
730
Called bool
731
}
732
733
func (s *Setting) UnmarshalRiver(f func(interface{}) error) error {
734
s.Called = true
735
type setting Setting
736
return f((*setting)(s))
737
}
738
739
func parseBlock(t *testing.T, input string) *ast.BlockStmt {
740
t.Helper()
741
742
res, err := parser.ParseFile("", []byte(input))
743
require.NoError(t, err)
744
require.Len(t, res.Body, 1)
745
746
stmt, ok := res.Body[0].(*ast.BlockStmt)
747
require.True(t, ok, "Expected stmt to be a ast.BlockStmt, got %T", res.Body[0])
748
return stmt
749
}
750
751