master
go 317 lines 8.91 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package funcapi
4
5 import (
6 "testing"
7
8 "github.com/stretchr/testify/assert"
9 "github.com/stretchr/testify/require"
10 )
11
12 // testColumn is a test column type that embeds ColumnMeta.
13 type testColumn struct {
14 ColumnMeta
15 Extra string // Simulates collector-specific field
16 }
17
18 func testColumnSet(cols ...testColumn) ColumnSet[testColumn] {
19 return Columns(cols, func(c testColumn) ColumnMeta { return c.ColumnMeta })
20 }
21
22 func TestColumnSet_Len(t *testing.T) {
23 cs := testColumnSet(
24 testColumn{ColumnMeta: ColumnMeta{Name: "a"}},
25 testColumn{ColumnMeta: ColumnMeta{Name: "b"}},
26 )
27 assert.Equal(t, 2, cs.Len())
28 }
29
30 func TestColumnSet_Names(t *testing.T) {
31 cs := testColumnSet(
32 testColumn{ColumnMeta: ColumnMeta{Name: "col1"}},
33 testColumn{ColumnMeta: ColumnMeta{Name: "col2"}},
34 testColumn{ColumnMeta: ColumnMeta{Name: "col3"}},
35 )
36 assert.Equal(t, []string{"col1", "col2", "col3"}, cs.Names())
37 }
38
39 func TestColumnSet_ContainsColumn(t *testing.T) {
40 cs := testColumnSet(
41 testColumn{ColumnMeta: ColumnMeta{Name: "exists"}},
42 )
43 assert.True(t, cs.ContainsColumn("exists"))
44 assert.False(t, cs.ContainsColumn("not_exists"))
45 }
46
47 func TestColumnSet_BuildColumns(t *testing.T) {
48 cs := testColumnSet(
49 testColumn{ColumnMeta: ColumnMeta{
50 Name: "query",
51 Tooltip: "Query",
52 Type: FieldTypeString,
53 Visible: true,
54 Sortable: true,
55 Filter: FieldFilterMultiselect,
56 }},
57 testColumn{ColumnMeta: ColumnMeta{
58 Name: "duration",
59 Tooltip: "Duration",
60 Type: FieldTypeDuration,
61 Units: "ms",
62 Visible: true,
63 Sortable: true,
64 Transform: FieldTransformDuration,
65 DecimalPoints: 2,
66 }},
67 )
68
69 result := cs.BuildColumns()
70
71 require.Len(t, result, 2)
72 require.Contains(t, result, "query")
73 require.Contains(t, result, "duration")
74
75 // Check query column
76 queryCol := result["query"].(map[string]any)
77 assert.Equal(t, 0, queryCol["index"])
78 assert.Equal(t, "Query", queryCol["name"])
79 assert.Equal(t, "string", queryCol["type"])
80 assert.Equal(t, true, queryCol["visible"])
81 assert.Equal(t, true, queryCol["sortable"])
82
83 // Check duration column - should auto-switch to bar visualization
84 durationCol := result["duration"].(map[string]any)
85 assert.Equal(t, 1, durationCol["index"])
86 assert.Equal(t, "Duration", durationCol["name"])
87 assert.Equal(t, "duration", durationCol["type"])
88 assert.Equal(t, "bar", durationCol["visualization"]) // Auto-switched from value to bar
89 assert.Equal(t, "ms", durationCol["units"])
90 }
91
92 func TestColumnSet_BuildColumns_ExplicitVisualization(t *testing.T) {
93 // When visualization is explicitly set, it should NOT be overridden
94 cs := testColumnSet(
95 testColumn{ColumnMeta: ColumnMeta{
96 Name: "duration",
97 Tooltip: "Duration",
98 Type: FieldTypeDuration,
99 Visualization: FieldVisualPill, // Explicitly set to pill
100 }},
101 )
102
103 result := cs.BuildColumns()
104 durationCol := result["duration"].(map[string]any)
105 assert.Equal(t, "pill", durationCol["visualization"]) // Should remain pill, not bar
106 }
107
108 func TestColumnSet_BuildCharts(t *testing.T) {
109 cs := testColumnSet(
110 testColumn{ColumnMeta: ColumnMeta{
111 Name: "query",
112 Tooltip: "Query",
113 Chart: nil, // Not a chart metric
114 }},
115 testColumn{ColumnMeta: ColumnMeta{
116 Name: "exec_time",
117 Tooltip: "Execution Time",
118 Chart: &ChartOptions{Group: "Time", Title: "Query Time"},
119 }},
120 testColumn{ColumnMeta: ColumnMeta{
121 Name: "cpu_time",
122 Tooltip: "CPU Time",
123 Chart: &ChartOptions{Group: "Time", Title: "Query Time"},
124 }},
125 testColumn{ColumnMeta: ColumnMeta{
126 Name: "rows",
127 Tooltip: "Rows",
128 Chart: &ChartOptions{Group: "Rows", Title: "Row Count"},
129 }},
130 )
131
132 result := cs.BuildCharts()
133
134 require.Len(t, result, 2)
135
136 // Time chart should have 2 columns
137 timeChart := result["Time"]
138 assert.Equal(t, "Query Time", timeChart.Name)
139 assert.Equal(t, "stacked-bar", timeChart.Type)
140 assert.Equal(t, []string{"exec_time", "cpu_time"}, timeChart.Columns)
141
142 // Rows chart should have 1 column
143 rowsChart := result["Rows"]
144 assert.Equal(t, "Row Count", rowsChart.Name)
145 assert.Equal(t, []string{"rows"}, rowsChart.Columns)
146 }
147
148 func TestColumnSet_BuildCharts_FallbackTitle(t *testing.T) {
149 cs := testColumnSet(
150 testColumn{ColumnMeta: ColumnMeta{
151 Name: "metric",
152 Chart: &ChartOptions{Group: "MyGroup", Title: ""}, // Empty title
153 }},
154 )
155
156 result := cs.BuildCharts()
157 assert.Equal(t, "MyGroup", result["MyGroup"].Name) // Falls back to Group
158 }
159
160 func TestColumnSet_BuildGroupBy(t *testing.T) {
161 cs := testColumnSet(
162 testColumn{ColumnMeta: ColumnMeta{
163 Name: "query",
164 Tooltip: "Query",
165 GroupBy: &GroupByOptions{},
166 }},
167 testColumn{ColumnMeta: ColumnMeta{
168 Name: "database",
169 Tooltip: "Database",
170 GroupBy: &GroupByOptions{},
171 }},
172 testColumn{ColumnMeta: ColumnMeta{
173 Name: "duration",
174 Tooltip: "Duration",
175 GroupBy: nil, // Not available for grouping
176 }},
177 )
178
179 result := cs.BuildGroupBy()
180
181 require.Len(t, result, 2)
182 assert.Equal(t, "Group by Query", result["query"].Name)
183 assert.Equal(t, []string{"query"}, result["query"].Columns)
184 assert.Equal(t, "Group by Database", result["database"].Name)
185 }
186
187 func TestColumnSet_FindDefaultGroupBy(t *testing.T) {
188 tests := []struct {
189 name string
190 cols []testColumn
191 expected string
192 }{
193 {
194 name: "returns default groupby if exists",
195 cols: []testColumn{
196 {ColumnMeta: ColumnMeta{Name: "a", GroupBy: &GroupByOptions{}}},
197 {ColumnMeta: ColumnMeta{Name: "b", GroupBy: &GroupByOptions{IsDefault: true}}},
198 {ColumnMeta: ColumnMeta{Name: "c", GroupBy: &GroupByOptions{}}},
199 },
200 expected: "b",
201 },
202 {
203 name: "falls back to first groupby if no default",
204 cols: []testColumn{
205 {ColumnMeta: ColumnMeta{Name: "a", GroupBy: nil}},
206 {ColumnMeta: ColumnMeta{Name: "b", GroupBy: &GroupByOptions{}}},
207 {ColumnMeta: ColumnMeta{Name: "c", GroupBy: &GroupByOptions{}}},
208 },
209 expected: "b",
210 },
211 {
212 name: "returns empty if no groupby columns",
213 cols: []testColumn{
214 {ColumnMeta: ColumnMeta{Name: "a"}},
215 {ColumnMeta: ColumnMeta{Name: "b"}},
216 },
217 expected: "",
218 },
219 }
220
221 for _, tt := range tests {
222 t.Run(tt.name, func(t *testing.T) {
223 cs := testColumnSet(tt.cols...)
224 assert.Equal(t, tt.expected, cs.FindDefaultGroupBy())
225 })
226 }
227 }
228
229 func TestColumnSet_FindDefaultChartGroups(t *testing.T) {
230 tests := []struct {
231 name string
232 cols []testColumn
233 expected []string
234 }{
235 {
236 name: "returns default chart groups",
237 cols: []testColumn{
238 {ColumnMeta: ColumnMeta{Name: "a", Chart: &ChartOptions{Group: "A", IsDefault: true}}},
239 {ColumnMeta: ColumnMeta{Name: "b", Chart: &ChartOptions{Group: "B", IsDefault: false}}},
240 {ColumnMeta: ColumnMeta{Name: "c", Chart: &ChartOptions{Group: "C", IsDefault: true}}},
241 },
242 expected: []string{"A", "C"},
243 },
244 {
245 name: "falls back to all groups if no defaults",
246 cols: []testColumn{
247 {ColumnMeta: ColumnMeta{Name: "a", Chart: &ChartOptions{Group: "A", IsDefault: false}}},
248 {ColumnMeta: ColumnMeta{Name: "b", Chart: &ChartOptions{Group: "B", IsDefault: false}}},
249 },
250 expected: []string{"A", "B"},
251 },
252 {
253 name: "deduplicates groups",
254 cols: []testColumn{
255 {ColumnMeta: ColumnMeta{Name: "a", Chart: &ChartOptions{Group: "Same", IsDefault: true}}},
256 {ColumnMeta: ColumnMeta{Name: "b", Chart: &ChartOptions{Group: "Same", IsDefault: true}}},
257 },
258 expected: []string{"Same"},
259 },
260 {
261 name: "returns nil for no chart columns",
262 cols: []testColumn{
263 {ColumnMeta: ColumnMeta{Name: "a", Chart: nil}},
264 },
265 expected: nil,
266 },
267 }
268
269 for _, tt := range tests {
270 t.Run(tt.name, func(t *testing.T) {
271 cs := testColumnSet(tt.cols...)
272 assert.Equal(t, tt.expected, cs.FindDefaultChartGroups())
273 })
274 }
275 }
276
277 func TestColumnSet_BuildDefaultCharts(t *testing.T) {
278 cs := testColumnSet(
279 testColumn{ColumnMeta: ColumnMeta{Name: "query", GroupBy: &GroupByOptions{IsDefault: true}}},
280 testColumn{ColumnMeta: ColumnMeta{Name: "time", Chart: &ChartOptions{Group: "Time", IsDefault: true}}},
281 testColumn{ColumnMeta: ColumnMeta{Name: "rows", Chart: &ChartOptions{Group: "Rows", IsDefault: true}}},
282 )
283
284 result := cs.BuildDefaultCharts()
285
286 expected := DefaultCharts{
287 {Chart: "Time", GroupBy: "query"},
288 {Chart: "Rows", GroupBy: "query"},
289 }
290 assert.Equal(t, expected, result)
291
292 // Test Build() method for JSON output
293 assert.Equal(t, [][]string{{"Time", "query"}, {"Rows", "query"}}, result.Build())
294 }
295
296 func TestColumnSet_BuildDefaultCharts_NoGroupBy(t *testing.T) {
297 cs := testColumnSet(
298 testColumn{ColumnMeta: ColumnMeta{Name: "metric", Chart: &ChartOptions{Group: "G"}}},
299 )
300
301 result := cs.BuildDefaultCharts()
302 assert.Nil(t, result)
303 }
304
305 func TestColumnSet_Empty(t *testing.T) {
306 cs := testColumnSet()
307
308 assert.Equal(t, 0, cs.Len())
309 assert.Empty(t, cs.Names())
310 assert.Empty(t, cs.BuildColumns())
311 assert.Empty(t, cs.BuildCharts())
312 assert.Empty(t, cs.BuildGroupBy())
313 assert.Nil(t, cs.BuildDefaultCharts())
314 assert.Equal(t, "", cs.FindDefaultGroupBy())
315 assert.Nil(t, cs.FindDefaultChartGroups())
316 assert.False(t, cs.ContainsColumn("any"))
317 }