Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/river/token/builder/builder_test.go
4096 views
1
package builder_test
2
3
import (
4
"bytes"
5
"fmt"
6
"testing"
7
"time"
8
9
"github.com/grafana/agent/pkg/river/parser"
10
"github.com/grafana/agent/pkg/river/printer"
11
"github.com/grafana/agent/pkg/river/token"
12
"github.com/grafana/agent/pkg/river/token/builder"
13
"github.com/stretchr/testify/require"
14
)
15
16
func TestBuilder_File(t *testing.T) {
17
f := builder.NewFile()
18
19
f.Body().SetAttributeTokens("attr_1", []builder.Token{{Tok: token.NUMBER, Lit: "15"}})
20
f.Body().SetAttributeTokens("attr_2", []builder.Token{{Tok: token.BOOL, Lit: "true"}})
21
22
b1 := builder.NewBlock([]string{"test", "block"}, "")
23
b1.Body().SetAttributeTokens("inner_attr", []builder.Token{{Tok: token.STRING, Lit: `"block 1"`}})
24
f.Body().AppendBlock(b1)
25
26
b2 := builder.NewBlock([]string{"test", "block"}, "labeled")
27
b2.Body().SetAttributeTokens("inner_attr", []builder.Token{{Tok: token.STRING, Lit: `"block 2"`}})
28
f.Body().AppendBlock(b2)
29
30
expect := format(t, `
31
attr_1 = 15
32
attr_2 = true
33
34
test.block {
35
inner_attr = "block 1"
36
}
37
38
test.block "labeled" {
39
inner_attr = "block 2"
40
}
41
`)
42
43
require.Equal(t, expect, string(f.Bytes()))
44
}
45
46
func TestBuilder_GoEncode(t *testing.T) {
47
f := builder.NewFile()
48
49
f.Body().AppendTokens([]builder.Token{{token.COMMENT, "// Hello, world!"}})
50
f.Body().SetAttributeValue("null_value", nil)
51
f.Body().AppendTokens([]builder.Token{{token.LITERAL, "\n"}})
52
53
f.Body().SetAttributeValue("num", 15)
54
f.Body().SetAttributeValue("string", "Hello, world!")
55
f.Body().SetAttributeValue("bool", true)
56
f.Body().SetAttributeValue("list", []int{0, 1, 2})
57
f.Body().SetAttributeValue("func", func(int, int) int { return 0 })
58
f.Body().SetAttributeValue("capsule", make(chan int))
59
f.Body().AppendTokens([]builder.Token{{token.LITERAL, "\n"}})
60
61
f.Body().SetAttributeValue("map", map[string]interface{}{"foo": "bar"})
62
f.Body().SetAttributeValue("map_2", map[string]interface{}{"non ident": "bar"})
63
f.Body().AppendTokens([]builder.Token{{token.LITERAL, "\n"}})
64
65
f.Body().SetAttributeValue("mixed_list", []interface{}{
66
0,
67
true,
68
map[string]interface{}{"key": true},
69
"Hello!",
70
})
71
72
expect := format(t, `
73
// Hello, world!
74
null_value = null
75
76
num = 15
77
string = "Hello, world!"
78
bool = true
79
list = [0, 1, 2]
80
func = function
81
capsule = capsule("chan int")
82
83
map = {
84
foo = "bar",
85
}
86
map_2 = {
87
"non ident" = "bar",
88
}
89
90
mixed_list = [0, true, {
91
key = true,
92
}, "Hello!"]
93
`)
94
95
require.Equal(t, expect, string(f.Bytes()))
96
}
97
98
// TestBuilder_GoEncode_SortMapKeys ensures that object literals from unordered
99
// values (i.e., Go maps) are printed in a deterministic order by sorting the
100
// keys lexicographically. Other object literals should be printed in the order
101
// the keys are reported in (i.e., in the order presented by the Go structs).
102
func TestBuilder_GoEncode_SortMapKeys(t *testing.T) {
103
f := builder.NewFile()
104
105
type Ordered struct {
106
SomeKey string `river:"some_key,attr"`
107
OtherKey string `river:"other_key,attr"`
108
}
109
110
// Maps are unordered because you can't iterate over their keys in a
111
// consistent order.
112
var unordered = map[string]interface{}{
113
"key_a": 1,
114
"key_c": 3,
115
"key_b": 2,
116
}
117
118
f.Body().SetAttributeValue("ordered", Ordered{SomeKey: "foo", OtherKey: "bar"})
119
f.Body().SetAttributeValue("unordered", unordered)
120
121
expect := format(t, `
122
ordered = {
123
some_key = "foo",
124
other_key = "bar",
125
}
126
unordered = {
127
key_a = 1,
128
key_b = 2,
129
key_c = 3,
130
}
131
`)
132
133
require.Equal(t, expect, string(f.Bytes()))
134
}
135
136
func TestBuilder_AppendFrom(t *testing.T) {
137
type InnerBlock struct {
138
Number int `river:"number,attr"`
139
}
140
141
type Structure struct {
142
Field string `river:"field,attr"`
143
144
Block InnerBlock `river:"block,block"`
145
OtherBlocks []InnerBlock `river:"other_block,block"`
146
}
147
148
f := builder.NewFile()
149
f.Body().AppendFrom(Structure{
150
Field: "some_value",
151
152
Block: InnerBlock{Number: 1},
153
OtherBlocks: []InnerBlock{
154
{Number: 2},
155
{Number: 3},
156
},
157
})
158
159
expect := format(t, `
160
field = "some_value"
161
162
block {
163
number = 1
164
}
165
166
other_block {
167
number = 2
168
}
169
170
other_block {
171
number = 3
172
}
173
`)
174
175
require.Equal(t, expect, string(f.Bytes()))
176
}
177
178
func TestBuilder_AppendFrom_EnumSlice(t *testing.T) {
179
type InnerBlock struct {
180
Number int `river:"number,attr"`
181
}
182
183
type EnumBlock struct {
184
BlockA InnerBlock `river:"a,block,optional"`
185
BlockB InnerBlock `river:"b,block,optional"`
186
BlockC InnerBlock `river:"c,block,optional"`
187
}
188
189
type Structure struct {
190
Field string `river:"field,attr"`
191
192
OtherBlocks []EnumBlock `river:"block,enum"`
193
}
194
195
f := builder.NewFile()
196
f.Body().AppendFrom(Structure{
197
Field: "some_value",
198
OtherBlocks: []EnumBlock{
199
{BlockC: InnerBlock{Number: 1}},
200
{BlockB: InnerBlock{Number: 2}},
201
{BlockC: InnerBlock{Number: 3}},
202
},
203
})
204
205
expect := format(t, `
206
field = "some_value"
207
208
block.c {
209
number = 1
210
}
211
212
block.b {
213
number = 2
214
}
215
216
block.c {
217
number = 3
218
}
219
`)
220
221
require.Equal(t, expect, string(f.Bytes()))
222
}
223
224
func TestBuilder_AppendFrom_EnumSlice_Pointer(t *testing.T) {
225
type InnerBlock struct {
226
Number int `river:"number,attr"`
227
}
228
229
type EnumBlock struct {
230
BlockA *InnerBlock `river:"a,block,optional"`
231
BlockB *InnerBlock `river:"b,block,optional"`
232
BlockC *InnerBlock `river:"c,block,optional"`
233
}
234
235
type Structure struct {
236
Field string `river:"field,attr"`
237
238
OtherBlocks []EnumBlock `river:"block,enum"`
239
}
240
241
f := builder.NewFile()
242
f.Body().AppendFrom(Structure{
243
Field: "some_value",
244
OtherBlocks: []EnumBlock{
245
{BlockC: &InnerBlock{Number: 1}},
246
{BlockB: &InnerBlock{Number: 2}},
247
{BlockC: &InnerBlock{Number: 3}},
248
},
249
})
250
251
expect := format(t, `
252
field = "some_value"
253
254
block.c {
255
number = 1
256
}
257
258
block.b {
259
number = 2
260
}
261
262
block.c {
263
number = 3
264
}
265
`)
266
267
require.Equal(t, expect, string(f.Bytes()))
268
}
269
270
func TestBuilder_SkipOptional(t *testing.T) {
271
type Structure struct {
272
OptFieldA string `river:"opt_field_a,attr,optional"`
273
OptFieldB string `river:"opt_field_b,attr,optional"`
274
ReqFieldA string `river:"req_field_a,attr"`
275
ReqFieldB string `river:"req_field_b,attr"`
276
}
277
278
f := builder.NewFile()
279
f.Body().AppendFrom(Structure{
280
OptFieldA: "some_value",
281
OptFieldB: "", // Zero value
282
ReqFieldA: "some_value",
283
ReqFieldB: "", // Zero value
284
})
285
286
expect := format(t, `
287
opt_field_a = "some_value"
288
req_field_a = "some_value"
289
req_field_b = ""
290
`)
291
292
require.Equal(t, expect, string(f.Bytes()))
293
}
294
295
func format(t *testing.T, in string) string {
296
t.Helper()
297
298
f, err := parser.ParseFile(t.Name(), []byte(in))
299
require.NoError(t, err)
300
301
var buf bytes.Buffer
302
require.NoError(t, printer.Fprint(&buf, f))
303
304
return buf.String()
305
}
306
307
type CustomTokenizer bool
308
309
var _ builder.Tokenizer = (CustomTokenizer)(false)
310
311
func (ct CustomTokenizer) RiverTokenize() []builder.Token {
312
return []builder.Token{{Tok: token.LITERAL, Lit: "CUSTOM_TOKENS"}}
313
}
314
315
func TestBuilder_GoEncode_Tokenizer(t *testing.T) {
316
t.Run("Tokenizer", func(t *testing.T) {
317
f := builder.NewFile()
318
f.Body().SetAttributeValue("value", CustomTokenizer(true))
319
320
expect := format(t, `value = CUSTOM_TOKENS`)
321
require.Equal(t, expect, string(f.Bytes()))
322
})
323
324
t.Run("TextMarshaler", func(t *testing.T) {
325
now := time.Now()
326
expectBytes, err := now.MarshalText()
327
require.NoError(t, err)
328
329
f := builder.NewFile()
330
f.Body().SetAttributeValue("value", now)
331
332
expect := format(t, fmt.Sprintf(`value = %q`, string(expectBytes)))
333
require.Equal(t, expect, string(f.Bytes()))
334
})
335
336
t.Run("Duration", func(t *testing.T) {
337
dur := 15 * time.Second
338
339
f := builder.NewFile()
340
f.Body().SetAttributeValue("value", dur)
341
342
expect := format(t, fmt.Sprintf(`value = %q`, dur.String()))
343
require.Equal(t, expect, string(f.Bytes()))
344
})
345
}
346
347