signoz/pkg/querybuilder/variable_resolver_test.go

318 lines
6.6 KiB
Go
Raw Normal View History

2025-06-18 15:30:29 +05:30
package querybuilder
import (
"testing"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/stretchr/testify/assert"
)
func TestVariableResolver_IsVariableReference(t *testing.T) {
r := NewVariableResolver(nil)
tests := []struct {
name string
value string
isVar bool
varName string
}{
{
name: "double curly with dot",
value: "{{.myVar}}",
isVar: true,
varName: "myVar",
},
{
name: "double curly without dot",
value: "{{myVar}}",
isVar: true,
varName: "myVar",
},
{
name: "double square with dot",
value: "[[.myVar]]",
isVar: true,
varName: "myVar",
},
{
name: "double square without dot",
value: "[[myVar]]",
isVar: true,
varName: "myVar",
},
{
name: "dollar sign",
value: "$myVar",
isVar: true,
varName: "myVar",
},
{
name: "not a variable",
value: "myVar",
isVar: false,
varName: "",
},
{
name: "partial match",
value: "prefix{{myVar}}suffix",
isVar: false,
varName: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isVar, varName := r.IsVariableReference(tt.value)
assert.Equal(t, tt.isVar, isVar)
assert.Equal(t, tt.varName, varName)
})
}
}
func TestVariableResolver_ResolveVariable(t *testing.T) {
variables := map[string]qbtypes.VariableItem{
"service": {
Type: qbtypes.QueryVariableType,
Value: "payment-service",
},
"status": {
Type: qbtypes.CustomVariableType,
Value: []string{"200", "201"},
},
"env": {
Type: qbtypes.TextBoxVariableType,
Value: "production",
},
"all_services": {
Type: qbtypes.DynamicVariableType,
Value: "__all__",
},
"all_array": {
Type: qbtypes.DynamicVariableType,
Value: []string{"__all__"},
},
}
r := NewVariableResolver(variables)
tests := []struct {
name string
varName string
wantValue any
wantSkip bool
wantErr bool
}{
{
name: "query variable",
varName: "service",
wantValue: "payment-service",
wantSkip: false,
wantErr: false,
},
{
name: "custom variable array",
varName: "status",
wantValue: []string{"200", "201"},
wantSkip: false,
wantErr: false,
},
{
name: "textbox variable",
varName: "env",
wantValue: "production",
wantSkip: false,
wantErr: false,
},
{
name: "dynamic variable with __all__",
varName: "all_services",
wantValue: nil,
wantSkip: true,
wantErr: false,
},
{
name: "dynamic variable with __all__ in array",
varName: "all_array",
wantValue: nil,
wantSkip: true,
wantErr: false,
},
{
name: "non-existent variable",
varName: "unknown",
wantValue: nil,
wantSkip: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
value, skipFilter, err := r.ResolveVariable(tt.varName)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.wantValue, value)
assert.Equal(t, tt.wantSkip, skipFilter)
}
})
}
}
func TestVariableResolver_ResolveFilterExpression(t *testing.T) {
variables := map[string]qbtypes.VariableItem{
"service": {
Type: qbtypes.QueryVariableType,
Value: "payment-service",
},
"status": {
Type: qbtypes.CustomVariableType,
Value: []string{"200", "201"},
},
"env": {
Type: qbtypes.TextBoxVariableType,
Value: "production",
},
"all": {
Type: qbtypes.DynamicVariableType,
Value: "__all__",
},
}
r := NewVariableResolver(variables)
tests := []struct {
name string
expression string
wantExpression string
wantSkip bool
wantErr bool
}{
{
name: "simple variable reference",
expression: "{{service}}",
wantExpression: "'payment-service'",
wantSkip: false,
wantErr: false,
},
{
name: "expression with variable",
expression: `service.name = "{{service}}"`,
wantExpression: `service.name = "'payment-service'"`,
wantSkip: false,
wantErr: false,
},
{
name: "expression with array variable",
expression: "status_code IN {{status}}",
wantExpression: `status_code IN ['200', '201']`,
wantSkip: false,
wantErr: false,
},
{
name: "multiple variables",
expression: `service.name = "{{service}}" AND environment = "{{env}}"`,
wantExpression: `service.name = "'payment-service'" AND environment = "'production'"`,
wantSkip: false,
wantErr: false,
},
{
name: "dollar variable syntax",
expression: `service.name = "$service"`,
wantExpression: `service.name = "'payment-service'"`,
wantSkip: false,
wantErr: false,
},
{
name: "double square brackets",
expression: `service.name = "[[service]]"`,
wantExpression: `service.name = "'payment-service'"`,
wantSkip: false,
wantErr: false,
},
{
name: "__all__ variable should skip filter",
expression: "service.name = {{all}}",
wantExpression: "",
wantSkip: true,
wantErr: false,
},
{
name: "expression with unknown variable",
expression: "service.name = {{unknown}}",
wantExpression: "service.name = {{unknown}}", // unchanged
wantSkip: false,
wantErr: false,
},
{
name: "no variables",
expression: "service.name = 'static-value'",
wantExpression: "service.name = 'static-value'",
wantSkip: false,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resolved, skipFilter, err := r.ResolveFilterExpression(tt.expression)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.wantExpression, resolved)
assert.Equal(t, tt.wantSkip, skipFilter)
}
})
}
}
func TestFormatValue(t *testing.T) {
tests := []struct {
name string
value any
want string
}{
{
name: "string value",
value: "test",
want: "'test'",
},
{
name: "string with quotes",
value: "test's value",
want: "'test''s value'",
},
{
name: "string array",
value: []string{"a", "b", "c"},
want: "['a', 'b', 'c']",
},
{
name: "interface array",
value: []any{"a", 123, "c"},
want: "['a', 123, 'c']",
},
{
name: "number",
value: 123,
want: "123",
},
{
name: "boolean",
value: true,
want: "true",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := formatValue(tt.value)
assert.Equal(t, tt.want, got)
})
}
}