mirror of
https://gitee.com/rulego/streamsql.git
synced 2026-03-14 06:17:29 +00:00
372 lines
10 KiB
Go
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)
|
||
})
|
||
}
|
||
}
|