master
go 241 lines 6.42 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package charttpl
4
5 import (
6 "encoding/json"
7 "os"
8 "path/filepath"
9 "testing"
10
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13 )
14
15 func TestDecodeYAMLScenarios(t *testing.T) {
16 tests := map[string]struct {
17 input string
18 wantErr bool
19 assert func(t *testing.T, spec *Spec)
20 }{
21 "valid grouped spec and default chart type": {
22 input: `
23 version: v1
24 context_namespace: mysql
25 engine:
26 selector:
27 allow:
28 - mysql_queries_total{db="main"}
29 autogen:
30 enabled: true
31 max_type_id_len: 512
32 expire_after_success_cycles: 9
33 groups:
34 - family: Database
35 metrics:
36 - mysql_queries_total
37 charts:
38 - title: Queries
39 context: queries_total
40 units: queries/s
41 algorithm: incremental
42 dimensions:
43 - selector: mysql_queries_total
44 name: total
45 options:
46 multiplier: -8
47 divisor: 1000
48 hidden: true
49 float: true
50 `,
51 assert: func(t *testing.T, spec *Spec) {
52 t.Helper()
53 require.Len(t, spec.Groups, 1)
54 require.Len(t, spec.Groups[0].Charts, 1)
55 assert.Equal(t, "line", spec.Groups[0].Charts[0].Type)
56 require.Len(t, spec.Groups[0].Charts[0].Dimensions, 1)
57 require.NotNil(t, spec.Groups[0].Charts[0].Dimensions[0].Options)
58 assert.Equal(t, -8, spec.Groups[0].Charts[0].Dimensions[0].Options.Multiplier)
59 assert.Equal(t, 1000, spec.Groups[0].Charts[0].Dimensions[0].Options.Divisor)
60 assert.True(t, spec.Groups[0].Charts[0].Dimensions[0].Options.Hidden)
61 assert.True(t, spec.Groups[0].Charts[0].Dimensions[0].Options.Float)
62 require.NotNil(t, spec.Engine)
63 require.NotNil(t, spec.Engine.Selector)
64 assert.Equal(t, []string{`mysql_queries_total{db="main"}`}, spec.Engine.Selector.Allow)
65 require.NotNil(t, spec.Engine.Autogen)
66 assert.True(t, spec.Engine.Autogen.Enabled)
67 assert.Equal(t, 512, spec.Engine.Autogen.MaxTypeIDLen)
68 assert.Equal(t, uint64(9), spec.Engine.Autogen.ExpireAfterSuccessCycles)
69 },
70 },
71 "group chart defaults apply recursively with nearest-scope replace semantics": {
72 input: `
73 version: v1
74 groups:
75 - family: Root
76 metrics:
77 - mysql_queries_total
78 chart_defaults:
79 label_promotion: [resource_name, region]
80 instances:
81 by_labels: [resource_uid]
82 groups:
83 - family: Child
84 chart_defaults:
85 instances:
86 by_labels: [resource_uid, region]
87 charts:
88 - title: Queries
89 context: queries
90 units: queries/s
91 dimensions:
92 - selector: mysql_queries_total
93 name: total
94 groups:
95 - family: Leaf
96 charts:
97 - title: Overrides
98 context: overrides
99 units: queries/s
100 label_promotion: []
101 instances:
102 by_labels: [region]
103 dimensions:
104 - selector: mysql_queries_total
105 name: total
106 `,
107 assert: func(t *testing.T, spec *Spec) {
108 t.Helper()
109 root := spec.Groups[0]
110 child := root.Groups[0]
111 require.Len(t, child.Charts, 1)
112 assert.Equal(t, "line", child.Charts[0].Type)
113 assert.Equal(t, []string{"resource_name", "region"}, child.Charts[0].LabelPromoted)
114 require.NotNil(t, child.Charts[0].Instances)
115 assert.Equal(t, []string{"resource_uid", "region"}, child.Charts[0].Instances.ByLabels)
116
117 leaf := child.Groups[0]
118 require.Len(t, leaf.Charts, 1)
119 assert.Equal(t, "line", leaf.Charts[0].Type)
120 assert.Empty(t, leaf.Charts[0].LabelPromoted)
121 require.NotNil(t, leaf.Charts[0].Instances)
122 assert.Equal(t, []string{"region"}, leaf.Charts[0].Instances.ByLabels)
123 },
124 },
125 "rejects unknown yaml field via strict unmarshal": {
126 input: `
127 version: v1
128 groups:
129 - family: Database
130 metrics:
131 - mysql_queries_total
132 charts:
133 - title: Queries
134 context: queries_total
135 units: queries/s
136 algorithm: incremental
137 unknown_field: true
138 dimensions:
139 - selector: mysql_queries_total
140 name: total
141 `,
142 wantErr: true,
143 },
144 }
145
146 for name, tc := range tests {
147 t.Run(name, func(t *testing.T) {
148 spec, err := DecodeYAML([]byte(tc.input))
149 if tc.wantErr {
150 require.Error(t, err)
151 return
152 }
153 require.NoError(t, err)
154 require.NotNil(t, spec)
155 if tc.assert != nil {
156 tc.assert(t, spec)
157 }
158 })
159 }
160 }
161
162 func TestConfigSchemaJSON(t *testing.T) {
163 schema := ConfigSchemaJSON
164 require.NotEmpty(t, schema)
165
166 var doc map[string]any
167 require.NoError(t, json.Unmarshal([]byte(schema), &doc))
168
169 defs, ok := doc["$defs"].(map[string]any)
170 require.True(t, ok)
171
172 chart, ok := defs["chart"].(map[string]any)
173 require.True(t, ok)
174 chartProps, ok := chart["properties"].(map[string]any)
175 require.True(t, ok)
176 chartLabelPromotion, ok := chartProps["label_promotion"].(map[string]any)
177 require.True(t, ok)
178 chartLabelPromotionItems, ok := chartLabelPromotion["items"].(map[string]any)
179 require.True(t, ok)
180 assert.Equal(t, `\S`, chartLabelPromotionItems["pattern"])
181
182 chartDefaults, ok := defs["chart_defaults"].(map[string]any)
183 require.True(t, ok)
184 defaultProps, ok := chartDefaults["properties"].(map[string]any)
185 require.True(t, ok)
186 defaultLabelPromotion, ok := defaultProps["label_promotion"].(map[string]any)
187 require.True(t, ok)
188 defaultLabelPromotionItems, ok := defaultLabelPromotion["items"].(map[string]any)
189 require.True(t, ok)
190 assert.Equal(t, `\S`, defaultLabelPromotionItems["pattern"])
191 }
192
193 func TestDecodeYAMLFileScenarios(t *testing.T) {
194 tests := map[string]struct {
195 prepare func(t *testing.T) string
196 wantErr bool
197 }{
198 "success": {
199 prepare: func(t *testing.T) string {
200 t.Helper()
201 dir := t.TempDir()
202 path := filepath.Join(dir, "charts.yaml")
203 data := []byte(`
204 version: v1
205 groups:
206 - family: Root
207 metrics: [metric_a]
208 charts:
209 - title: A
210 context: a
211 units: "1"
212 dimensions:
213 - selector: metric_a
214 name: x
215 `)
216 require.NoError(t, os.WriteFile(path, data, 0o644))
217 return path
218 },
219 },
220 "read error": {
221 prepare: func(t *testing.T) string {
222 t.Helper()
223 return filepath.Join(t.TempDir(), "missing.yaml")
224 },
225 wantErr: true,
226 },
227 }
228
229 for name, tc := range tests {
230 t.Run(name, func(t *testing.T) {
231 path := tc.prepare(t)
232 spec, err := DecodeYAMLFile(path)
233 if tc.wantErr {
234 require.Error(t, err)
235 return
236 }
237 require.NoError(t, err)
238 require.NotNil(t, spec)
239 })
240 }
241 }