mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 15:36:48 +00:00
282 lines
8.0 KiB
Go
282 lines
8.0 KiB
Go
package querybuilder
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/SigNoz/signoz/pkg/types/metrictypes"
|
|
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
|
)
|
|
|
|
func TestHavingExpressionRewriter_RewriteForTraces(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
expression string
|
|
aggregations []qbtypes.TraceAggregation
|
|
expected string
|
|
}{
|
|
{
|
|
name: "single aggregation with __result",
|
|
expression: "__result > 100",
|
|
aggregations: []qbtypes.TraceAggregation{
|
|
{Expression: "count()", Alias: ""},
|
|
},
|
|
expected: "__result_0 > 100",
|
|
},
|
|
{
|
|
name: "single aggregation with alias",
|
|
expression: "total_count > 100 AND total_count < 1000",
|
|
aggregations: []qbtypes.TraceAggregation{
|
|
{Expression: "count()", Alias: "total_count"},
|
|
},
|
|
expected: "__result_0 > 100 AND __result_0 < 1000",
|
|
},
|
|
{
|
|
name: "multiple aggregations with aliases",
|
|
expression: "error_count > 10 OR success_count > 100",
|
|
aggregations: []qbtypes.TraceAggregation{
|
|
{Expression: "countIf(status = 'error')", Alias: "error_count"},
|
|
{Expression: "countIf(status = 'success')", Alias: "success_count"},
|
|
},
|
|
expected: "__result_0 > 10 OR __result_1 > 100",
|
|
},
|
|
{
|
|
name: "expression reference",
|
|
expression: "count() > 50",
|
|
aggregations: []qbtypes.TraceAggregation{
|
|
{Expression: "count()", Alias: ""},
|
|
},
|
|
expected: "__result_0 > 50",
|
|
},
|
|
{
|
|
name: "__result{number} format",
|
|
expression: "__result0 > 10 AND __result1 < 100",
|
|
aggregations: []qbtypes.TraceAggregation{
|
|
{Expression: "count()", Alias: ""},
|
|
{Expression: "sum(duration)", Alias: ""},
|
|
},
|
|
expected: "__result_0 > 10 AND __result_1 < 100",
|
|
},
|
|
{
|
|
name: "complex expression with parentheses",
|
|
expression: "(total > 100 AND errors < 10) OR (total < 50 AND errors = 0)",
|
|
aggregations: []qbtypes.TraceAggregation{
|
|
{Expression: "count()", Alias: "total"},
|
|
{Expression: "countIf(error = true)", Alias: "errors"},
|
|
},
|
|
expected: "(__result_0 > 100 AND __result_1 < 10) OR (__result_0 < 50 AND __result_1 = 0)",
|
|
},
|
|
{
|
|
name: "with quoted strings",
|
|
expression: "status = 'active' AND count > 100",
|
|
aggregations: []qbtypes.TraceAggregation{
|
|
{Expression: "status", Alias: "status"},
|
|
{Expression: "count()", Alias: "count"},
|
|
},
|
|
expected: "__result_0 = 'active' AND __result_1 > 100",
|
|
},
|
|
{
|
|
name: "avoid partial replacements",
|
|
expression: "count_distinct > 10 AND count > 100",
|
|
aggregations: []qbtypes.TraceAggregation{
|
|
{Expression: "count_distinct(user_id)", Alias: "count_distinct"},
|
|
{Expression: "count()", Alias: "count"},
|
|
},
|
|
expected: "__result_0 > 10 AND __result_1 > 100",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rewriter := NewHavingExpressionRewriter()
|
|
result := rewriter.RewriteForTraces(tt.expression, tt.aggregations)
|
|
if result != tt.expected {
|
|
t.Errorf("Expected: %s, Got: %s", tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHavingExpressionRewriter_RewriteForLogs(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
expression string
|
|
aggregations []qbtypes.LogAggregation
|
|
expected string
|
|
}{
|
|
{
|
|
name: "single aggregation with __result",
|
|
expression: "__result > 1000",
|
|
aggregations: []qbtypes.LogAggregation{
|
|
{Expression: "count()", Alias: ""},
|
|
},
|
|
expected: "__result_0 > 1000",
|
|
},
|
|
{
|
|
name: "multiple aggregations with aliases and expressions",
|
|
expression: "total_logs > 1000 AND avg(size) < 1024",
|
|
aggregations: []qbtypes.LogAggregation{
|
|
{Expression: "count()", Alias: "total_logs"},
|
|
{Expression: "avg(size)", Alias: ""},
|
|
},
|
|
expected: "__result_0 > 1000 AND __result_1 < 1024",
|
|
},
|
|
{
|
|
name: "complex boolean expression",
|
|
expression: "(error_logs > 100 AND error_logs < 1000) OR warning_logs > 5000",
|
|
aggregations: []qbtypes.LogAggregation{
|
|
{Expression: "countIf(level = 'error')", Alias: "error_logs"},
|
|
{Expression: "countIf(level = 'warning')", Alias: "warning_logs"},
|
|
},
|
|
expected: "(__result_0 > 100 AND __result_0 < 1000) OR __result_1 > 5000",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rewriter := NewHavingExpressionRewriter()
|
|
result := rewriter.RewriteForLogs(tt.expression, tt.aggregations)
|
|
if result != tt.expected {
|
|
t.Errorf("Expected: %s, Got: %s", tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHavingExpressionRewriter_RewriteForMetrics(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
expression string
|
|
aggregations []qbtypes.MetricAggregation
|
|
expected string
|
|
}{
|
|
{
|
|
name: "metric with space aggregation",
|
|
expression: "avg(cpu_usage) > 80",
|
|
aggregations: []qbtypes.MetricAggregation{
|
|
{
|
|
MetricName: "cpu_usage",
|
|
SpaceAggregation: metrictypes.SpaceAggregationAvg,
|
|
},
|
|
},
|
|
expected: "value > 80",
|
|
},
|
|
{
|
|
name: "metric with time aggregation",
|
|
expression: "rate(requests) > 1000",
|
|
aggregations: []qbtypes.MetricAggregation{
|
|
{
|
|
MetricName: "requests",
|
|
TimeAggregation: metrictypes.TimeAggregationRate,
|
|
},
|
|
},
|
|
expected: "value > 1000",
|
|
},
|
|
{
|
|
name: "metric with both aggregations",
|
|
expression: "sum(rate(requests)) > 5000",
|
|
aggregations: []qbtypes.MetricAggregation{
|
|
{
|
|
MetricName: "requests",
|
|
TimeAggregation: metrictypes.TimeAggregationRate,
|
|
SpaceAggregation: metrictypes.SpaceAggregationSum,
|
|
},
|
|
},
|
|
expected: "value > 5000",
|
|
},
|
|
{
|
|
name: "metric with __result",
|
|
expression: "__result < 100",
|
|
aggregations: []qbtypes.MetricAggregation{
|
|
{
|
|
MetricName: "memory_usage",
|
|
SpaceAggregation: metrictypes.SpaceAggregationMax,
|
|
},
|
|
},
|
|
expected: "value < 100",
|
|
},
|
|
{
|
|
name: "metric name without aggregation",
|
|
expression: "temperature > 30",
|
|
aggregations: []qbtypes.MetricAggregation{
|
|
{
|
|
MetricName: "temperature",
|
|
},
|
|
},
|
|
expected: "value > 30",
|
|
},
|
|
{
|
|
name: "complex expression with parentheses",
|
|
expression: "(avg(cpu_usage) > 80 AND avg(cpu_usage) < 95) OR __result > 99",
|
|
aggregations: []qbtypes.MetricAggregation{
|
|
{
|
|
MetricName: "cpu_usage",
|
|
SpaceAggregation: metrictypes.SpaceAggregationAvg,
|
|
},
|
|
},
|
|
expected: "(value > 80 AND value < 95) OR value > 99",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rewriter := NewHavingExpressionRewriter()
|
|
result := rewriter.RewriteForMetrics(tt.expression, tt.aggregations)
|
|
if result != tt.expected {
|
|
t.Errorf("Expected: %s, Got: %s", tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHavingExpressionRewriter_EdgeCases(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
expression string
|
|
aggregations []qbtypes.TraceAggregation
|
|
expected string
|
|
}{
|
|
{
|
|
name: "empty expression",
|
|
expression: "",
|
|
aggregations: []qbtypes.TraceAggregation{},
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "no matching columns",
|
|
expression: "unknown_column > 100",
|
|
aggregations: []qbtypes.TraceAggregation{
|
|
{Expression: "count()", Alias: "total"},
|
|
},
|
|
expected: "unknown_column > 100",
|
|
},
|
|
{
|
|
name: "expression within quoted string",
|
|
expression: "status = 'count() > 100' AND total > 100",
|
|
aggregations: []qbtypes.TraceAggregation{
|
|
{Expression: "status", Alias: "status"},
|
|
{Expression: "count()", Alias: "total"},
|
|
},
|
|
expected: "__result_0 = 'count() > 100' AND __result_1 > 100",
|
|
},
|
|
{
|
|
name: "double quotes",
|
|
expression: `name = "test" AND count > 10`,
|
|
aggregations: []qbtypes.TraceAggregation{
|
|
{Expression: "name", Alias: "name"},
|
|
{Expression: "count()", Alias: "count"},
|
|
},
|
|
expected: `__result_0 = "test" AND __result_1 > 10`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rewriter := NewHavingExpressionRewriter()
|
|
result := rewriter.RewriteForTraces(tt.expression, tt.aggregations)
|
|
if result != tt.expected {
|
|
t.Errorf("Expected: %s, Got: %s", tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|