signoz/pkg/telemetrymetrics/condition_builder_test.go

296 lines
8.7 KiB
Go

package telemetrymetrics
import (
"context"
"testing"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/huandu/go-sqlbuilder"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestConditionFor(t *testing.T) {
ctx := context.Background()
testCases := []struct {
name string
key telemetrytypes.TelemetryFieldKey
operator qbtypes.FilterOperator
value any
expectedSQL string
expectedArgs []any
expectedError error
}{
{
name: "Equal operator - string",
key: telemetrytypes.TelemetryFieldKey{
Name: "metric_name",
FieldContext: telemetrytypes.FieldContextMetric,
},
operator: qbtypes.FilterOperatorEqual,
value: "http.server.duration",
expectedSQL: "metric_name = ?",
expectedArgs: []any{"http.server.duration"},
expectedError: nil,
},
{
name: "Not Equal operator - metric_name",
key: telemetrytypes.TelemetryFieldKey{
Name: "metric_name",
FieldContext: telemetrytypes.FieldContextMetric,
},
operator: qbtypes.FilterOperatorNotEqual,
value: "http.server.duration",
expectedSQL: "metric_name <> ?",
expectedArgs: []any{"http.server.duration"},
expectedError: nil,
},
{
name: "Like operator - metric_name",
key: telemetrytypes.TelemetryFieldKey{
Name: "metric_name",
FieldContext: telemetrytypes.FieldContextMetric,
},
operator: qbtypes.FilterOperatorLike,
value: "%error%",
expectedSQL: "metric_name LIKE ?",
expectedArgs: []any{"%error%"},
expectedError: nil,
},
{
name: "Not Like operator - metric_name",
key: telemetrytypes.TelemetryFieldKey{
Name: "metric_name",
FieldContext: telemetrytypes.FieldContextMetric,
},
operator: qbtypes.FilterOperatorNotLike,
value: "%error%",
expectedSQL: "metric_name NOT LIKE ?",
expectedArgs: []any{"%error%"},
expectedError: nil,
},
{
name: "ILike operator - string label",
key: telemetrytypes.TelemetryFieldKey{
Name: "user.id",
FieldContext: telemetrytypes.FieldContextResource,
FieldDataType: telemetrytypes.FieldDataTypeString,
},
operator: qbtypes.FilterOperatorILike,
value: "%admin%",
expectedSQL: "LOWER(JSONExtractString(labels, 'user.id')) LIKE LOWER(?)",
expectedArgs: []any{"%admin%"},
expectedError: nil,
},
{
name: "Not ILike operator - string label",
key: telemetrytypes.TelemetryFieldKey{
Name: "user.id",
FieldContext: telemetrytypes.FieldContextResource,
FieldDataType: telemetrytypes.FieldDataTypeString,
},
operator: qbtypes.FilterOperatorNotILike,
value: "%admin%",
expectedSQL: "LOWER(JSONExtractString(labels, 'user.id')) NOT LIKE LOWER(?)",
expectedArgs: []any{"%admin%"},
expectedError: nil,
},
{
name: "Contains operator - string label",
key: telemetrytypes.TelemetryFieldKey{
Name: "user.id",
FieldContext: telemetrytypes.FieldContextResource,
FieldDataType: telemetrytypes.FieldDataTypeString,
},
operator: qbtypes.FilterOperatorContains,
value: "admin",
expectedSQL: "LOWER(JSONExtractString(labels, 'user.id')) LIKE LOWER(?)",
expectedArgs: []any{"%admin%"},
expectedError: nil,
},
{
name: "In operator - metric_name",
key: telemetrytypes.TelemetryFieldKey{
Name: "metric_name",
FieldContext: telemetrytypes.FieldContextMetric,
},
operator: qbtypes.FilterOperatorIn,
value: []any{"http.server.duration", "http.server.request.duration", "http.server.response.duration"},
expectedSQL: "(metric_name = ? OR metric_name = ? OR metric_name = ?)",
expectedArgs: []any{"http.server.duration", "http.server.request.duration", "http.server.response.duration"},
expectedError: nil,
},
{
name: "In operator - invalid value",
key: telemetrytypes.TelemetryFieldKey{
Name: "metric_name",
FieldContext: telemetrytypes.FieldContextMetric,
},
operator: qbtypes.FilterOperatorIn,
value: "error",
expectedSQL: "",
expectedError: qbtypes.ErrInValues,
},
{
name: "Not In operator - metric_name",
key: telemetrytypes.TelemetryFieldKey{
Name: "metric_name",
FieldContext: telemetrytypes.FieldContextMetric,
},
operator: qbtypes.FilterOperatorNotIn,
value: []any{"debug", "info", "trace"},
expectedSQL: "(metric_name <> ? AND metric_name <> ? AND metric_name <> ?)",
expectedArgs: []any{"debug", "info", "trace"},
expectedError: nil,
},
{
name: "Exists operator - string field",
key: telemetrytypes.TelemetryFieldKey{
Name: "metric_name",
FieldContext: telemetrytypes.FieldContextMetric,
},
operator: qbtypes.FilterOperatorExists,
value: nil,
expectedSQL: "true",
expectedError: nil,
},
{
name: "Not Exists operator - string field",
key: telemetrytypes.TelemetryFieldKey{
Name: "metric_name",
FieldContext: telemetrytypes.FieldContextMetric,
},
operator: qbtypes.FilterOperatorNotExists,
value: nil,
expectedSQL: "true",
expectedError: nil,
},
{
name: "Exists operator - type",
key: telemetrytypes.TelemetryFieldKey{
Name: "type",
FieldContext: telemetrytypes.FieldContextMetric,
},
operator: qbtypes.FilterOperatorExists,
value: nil,
expectedSQL: "true",
expectedError: nil,
},
{
name: "Exists operator - string label",
key: telemetrytypes.TelemetryFieldKey{
Name: "user.id",
FieldContext: telemetrytypes.FieldContextResource,
FieldDataType: telemetrytypes.FieldDataTypeString,
},
operator: qbtypes.FilterOperatorExists,
value: nil,
expectedSQL: "has(JSONExtractKeys(labels), 'user.id')",
expectedError: nil,
},
{
name: "Not Exists operator - string label",
key: telemetrytypes.TelemetryFieldKey{
Name: "user.id",
FieldContext: telemetrytypes.FieldContextResource,
FieldDataType: telemetrytypes.FieldDataTypeString,
},
operator: qbtypes.FilterOperatorNotExists,
value: nil,
expectedSQL: "not has(JSONExtractKeys(labels), 'user.id')",
expectedError: nil,
},
{
name: "Non-existent column",
key: telemetrytypes.TelemetryFieldKey{
Name: "nonexistent_field",
FieldContext: telemetrytypes.FieldContextMetric,
},
operator: qbtypes.FilterOperatorEqual,
value: "value",
expectedSQL: "",
expectedError: qbtypes.ErrColumnNotFound,
},
}
fm := NewFieldMapper()
conditionBuilder := NewConditionBuilder(fm)
for _, tc := range testCases {
sb := sqlbuilder.NewSelectBuilder()
t.Run(tc.name, func(t *testing.T) {
cond, err := conditionBuilder.ConditionFor(ctx, &tc.key, tc.operator, tc.value, sb)
sb.Where(cond)
if tc.expectedError != nil {
assert.Equal(t, tc.expectedError, err)
} else {
require.NoError(t, err)
sql, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
assert.Contains(t, sql, tc.expectedSQL)
assert.Equal(t, tc.expectedArgs, args)
}
})
}
}
func TestConditionForMultipleKeys(t *testing.T) {
ctx := context.Background()
testCases := []struct {
name string
keys []telemetrytypes.TelemetryFieldKey
operator qbtypes.FilterOperator
value any
expectedSQL string
expectedArgs []any
expectedError error
}{
{
name: "Equal operator - string",
keys: []telemetrytypes.TelemetryFieldKey{
{
Name: "metric_name",
FieldContext: telemetrytypes.FieldContextMetric,
},
{
Name: "type",
FieldContext: telemetrytypes.FieldContextMetric,
},
},
operator: qbtypes.FilterOperatorEqual,
value: "error message",
expectedSQL: "metric_name = ? AND type = ?",
expectedArgs: []any{"error message", "error message"},
expectedError: nil,
},
}
fm := NewFieldMapper()
conditionBuilder := NewConditionBuilder(fm)
for _, tc := range testCases {
sb := sqlbuilder.NewSelectBuilder()
t.Run(tc.name, func(t *testing.T) {
var err error
for _, key := range tc.keys {
cond, err := conditionBuilder.ConditionFor(ctx, &key, tc.operator, tc.value, sb)
sb.Where(cond)
if err != nil {
t.Fatalf("Error getting condition for key %s: %v", key.Name, err)
}
}
if tc.expectedError != nil {
assert.Equal(t, tc.expectedError, err)
} else {
require.NoError(t, err)
sql, _ := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
assert.Contains(t, sql, tc.expectedSQL)
}
})
}
}