Files
streamsql/functions/expr_bridge_test.go
T
2025-08-08 17:52:43 +08:00

372 lines
10 KiB
Go

package functions
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestExprBridge(t *testing.T) {
bridge := NewExprBridge()
t.Run("StreamSQL Functions Available", func(t *testing.T) {
// 测试StreamSQL函数是否可用
data := map[string]interface{}{
"temperature": 25.5,
"humidity": 60,
}
// 测试数学函数
result, err := bridge.EvaluateExpression("abs(-5)", data)
assert.NoError(t, err)
assert.Equal(t, float64(5), result)
// 测试字符串函数
result, err = bridge.EvaluateExpression("length(\"hello\")", data)
assert.NoError(t, err)
assert.Equal(t, int(5), result)
})
t.Run("Expr-Lang Functions Available", func(t *testing.T) {
data := map[string]interface{}{
"numbers": []int{1, 2, 3, 4, 5},
"text": "Hello World",
}
// 测试expr-lang数组函数
result, err := bridge.EvaluateExpression("len(numbers)", data)
assert.NoError(t, err)
assert.Equal(t, 5, result)
// 测试expr-lang字符串函数
result, err = bridge.EvaluateExpression("trim(\" hello \")", data)
assert.NoError(t, err)
assert.Equal(t, "hello", result)
})
t.Run("Mixed Functions", func(t *testing.T) {
data := map[string]interface{}{
"values": []float64{-3.5, 2.1, -1.8, 4.2},
}
// 使用StreamSQL的abs函数和expr-lang的filter函数
// 注意:这个测试可能需要根据实际实现调整
env := bridge.CreateEnhancedExprEnvironment(data)
// 验证环境中包含所有预期的函数
assert.Contains(t, env, "abs")
assert.Contains(t, env, "length")
assert.Contains(t, env, "values")
})
t.Run("Function Resolution", func(t *testing.T) {
// 测试函数解析优先级
_, exists, source := bridge.ResolveFunction("abs")
assert.True(t, exists)
assert.Equal(t, "streamsql", source) // StreamSQL优先
_, exists, source = bridge.ResolveFunction("encode")
assert.True(t, exists)
assert.Equal(t, "streamsql", source) // StreamSQL独有
_, exists, _ = bridge.ResolveFunction("nonexistent")
assert.False(t, exists)
})
t.Run("Function Information", func(t *testing.T) {
info := bridge.GetFunctionInfo()
// 验证包含StreamSQL函数信息
streamSQLFuncs, ok := info["streamsql"].(map[string]interface{})
assert.True(t, ok)
assert.Contains(t, streamSQLFuncs, "abs")
assert.Contains(t, streamSQLFuncs, "encode")
// 验证包含expr-lang函数信息
exprLangFuncs, ok := info["expr-lang"].(map[string]interface{})
assert.True(t, ok)
assert.Contains(t, exprLangFuncs, "trim")
assert.Contains(t, exprLangFuncs, "filter")
})
}
func TestEvaluateWithBridge(t *testing.T) {
data := map[string]interface{}{
"x": 3.5,
"y": -2.1,
}
// 测试简单表达式
result, err := EvaluateWithBridge("abs(y)", data)
assert.NoError(t, err)
assert.Equal(t, 2.1, result)
// 测试复合表达式
result, err = EvaluateWithBridge("x + abs(y)", data)
assert.NoError(t, err)
assert.Equal(t, 5.6, result)
}
func TestGetAllAvailableFunctions(t *testing.T) {
info := GetAllAvailableFunctions()
// 验证返回的信息结构
assert.Contains(t, info, "streamsql")
assert.Contains(t, info, "expr-lang")
// 验证函数数量合理
streamSQLFuncs := info["streamsql"].(map[string]interface{})
t.Logf("StreamSQL functions count: %d", len(streamSQLFuncs))
// for name := range streamSQLFuncs {
// t.Logf("StreamSQL function: %s", name)
// }
assert.GreaterOrEqual(t, len(streamSQLFuncs), 1) // 至少应该有一个函数
exprLangFuncs := info["expr-lang"].(map[string]interface{})
t.Logf("Expr-lang functions count: %d", len(exprLangFuncs))
assert.GreaterOrEqual(t, len(exprLangFuncs), 1) // 至少应该有一个函数
}
func TestFunctionConflictResolution(t *testing.T) {
bridge := NewExprBridge()
data := map[string]interface{}{
"value": -5.5,
}
// 测试冲突函数的解析(abs函数在两个系统中都存在)
// 应该优先使用expr-lang的版本
env := bridge.CreateEnhancedExprEnvironment(data)
// 验证StreamSQL函数可以通过别名访问
assert.Contains(t, env, "streamsql_abs")
assert.Contains(t, env, "abs")
// 测试两个版本都能正常工作
result, err := bridge.EvaluateExpression("abs(value)", data)
assert.NoError(t, err)
assert.Equal(t, 5.5, result)
}
func TestExprBridgeAdvancedFunctions(t *testing.T) {
bridge := NewExprBridge()
t.Run("String Concatenation Detection", func(t *testing.T) {
data := map[string]interface{}{
"name": "John",
"age": 25,
}
// 测试字符串连接表达式检测
isConcat := bridge.isStringConcatenationExpression("name + ' is ' + age", data)
assert.True(t, isConcat)
isConcat = bridge.isStringConcatenationExpression("abs(-5)", data)
assert.False(t, isConcat)
})
t.Run("Fallback to Custom Expression", func(t *testing.T) {
data := map[string]interface{}{
"text": "hello",
}
// 测试回退到自定义表达式处理
result, err := bridge.fallbackToCustomExpr("text + ' world'", data)
assert.NoError(t, err)
assert.Equal(t, "hello world", result)
})
t.Run("String Concatenation Evaluation", func(t *testing.T) {
data := map[string]interface{}{
"first": "Hello",
"second": "World",
}
// 测试字符串连接求值
result, err := bridge.evaluateStringConcatenation("first + ' ' + second", data)
assert.NoError(t, err)
assert.Equal(t, "Hello World", result)
})
t.Run("Simple Numeric Expression", func(t *testing.T) {
data := map[string]interface{}{
"x": 10,
"y": 5,
}
// 测试简单数值表达式
result, err := bridge.evaluateSimpleNumericExpression("x + y", data)
assert.NoError(t, err)
assert.Equal(t, float64(15), result)
})
t.Run("Function Call Detection", func(t *testing.T) {
// 测试函数调用检测
assert.True(t, bridge.isFunctionCall("abs(-5)"))
assert.True(t, bridge.isFunctionCall("length('hello')"))
assert.False(t, bridge.isFunctionCall("x + y"))
assert.False(t, bridge.isFunctionCall("simple_variable"))
})
t.Run("Like Expression Preprocessing", func(t *testing.T) {
// 测试LIKE表达式预处理
processed, err := bridge.PreprocessLikeExpression("name LIKE '%john%'")
assert.NoError(t, err)
assert.Contains(t, processed, "contains")
})
t.Run("IsNull Expression Preprocessing", func(t *testing.T) {
// 测试IS NULL表达式预处理
processed, err := bridge.PreprocessIsNullExpression("field IS NULL")
assert.NoError(t, err)
assert.Contains(t, processed, "== nil")
})
t.Run("Backtick Identifiers", func(t *testing.T) {
// 测试反引号标识符检测
assert.True(t, bridge.ContainsBacktickIdentifiers("`field_name` = 1"))
assert.False(t, bridge.ContainsBacktickIdentifiers("field_name = 1"))
// 测试反引号标识符预处理
processed, err := bridge.PreprocessBacktickIdentifiers("`field_name` = 1")
assert.NoError(t, err)
assert.Contains(t, processed, "field_name")
})
t.Run("Like Pattern Matching", func(t *testing.T) {
// 测试LIKE模式匹配
assert.True(t, bridge.matchesLikePattern("hello", "h%"))
assert.True(t, bridge.matchesLikePattern("world", "%d"))
assert.False(t, bridge.matchesLikePattern("hello", "x%"))
// 测试递归LIKE匹配
assert.True(t, bridge.likeMatch("hello", "h%o", 0, 0))
assert.False(t, bridge.likeMatch("hello", "x%", 0, 0))
})
t.Run("Type Conversion", func(t *testing.T) {
// 测试类型转换
result, err := bridge.toFloat64(10)
assert.NoError(t, err)
assert.Equal(t, float64(10), result)
result, err = bridge.toFloat64("10.5")
assert.NoError(t, err)
assert.Equal(t, float64(10.5), result)
_, err = bridge.toFloat64("invalid")
assert.Error(t, err)
})
t.Run("Expr Lang Function Detection", func(t *testing.T) {
// 测试expr-lang函数检测
assert.True(t, bridge.IsExprLangFunction("trim"))
assert.True(t, bridge.IsExprLangFunction("len"))
assert.True(t, bridge.IsExprLangFunction("abs"))
assert.False(t, bridge.IsExprLangFunction("nonexistent"))
})
t.Run("Like to Function Conversion", func(t *testing.T) {
// 测试LIKE转换为函数调用
result := bridge.convertLikeToFunction("name", "%john%")
assert.Contains(t, result, "contains")
assert.Contains(t, result, "name")
assert.Contains(t, result, "john")
})
}
// TestExprBridgeComplexExpressions 测试复杂表达式处理
func TestExprBridgeComplexExpressions(t *testing.T) {
bridge := NewExprBridge()
tests := []struct {
name string
expression string
data map[string]interface{}
expected interface{}
wantErr bool
}{
{
name: "math_and_string",
expression: "length('test')",
data: map[string]interface{}{},
expected: 4,
wantErr: false,
},
{
name: "nested_function_calls",
expression: "abs(sqrt(16) - 5)",
data: map[string]interface{}{},
expected: float64(1),
wantErr: false,
},
{
name: "array_operations",
expression: "array_length([1, 2, 3, 4])",
data: map[string]interface{}{},
expected: 4,
wantErr: false,
},
{
name: "string_with_variables",
expression: "upper(name)",
data: map[string]interface{}{"name": "john"},
expected: "JOHN",
wantErr: false,
},
{
name: "conditional_expression",
expression: "age > 18 ? 'adult' : 'minor'",
data: map[string]interface{}{"age": 25},
expected: "adult",
wantErr: false,
},
{
name: "complex_math",
expression: "power(2, 3) + mod(10, 3)",
data: map[string]interface{}{},
expected: float64(9),
wantErr: false,
},
{
name: "array_contains_check",
expression: "array_contains([1, 2, 3], 2)",
data: map[string]interface{}{},
expected: true,
wantErr: false,
},
{
name: "string_concatenation",
expression: "concat(first_name, ' ', last_name)",
data: map[string]interface{}{"first_name": "John", "last_name": "Doe"},
expected: "John Doe",
wantErr: false,
},
{
name: "invalid_function",
expression: "nonexistent_function(1)",
data: map[string]interface{}{},
expected: nil,
wantErr: true,
},
{
name: "invalid_syntax",
expression: "length(",
data: map[string]interface{}{},
expected: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := bridge.EvaluateExpression(tt.expression, tt.data)
if tt.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.expected, result)
})
}
}