mirror of
https://gitee.com/rulego/streamsql.git
synced 2026-03-14 06:17:29 +00:00
@@ -41,6 +41,7 @@ jobs:
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Run tests
|
||||
if: matrix.go-version != '1.21'
|
||||
run: go test -v -race -timeout 300s ./...
|
||||
|
||||
- name: Run tests with coverage
|
||||
|
||||
@@ -273,3 +273,99 @@ func TestExprBridgeAdvancedFunctions(t *testing.T) {
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ func (f *ArrayRemoveFunction) Execute(ctx *FunctionContext, args []interface{})
|
||||
return nil, fmt.Errorf("array_remove requires array input")
|
||||
}
|
||||
|
||||
var result []interface{}
|
||||
result := make([]interface{}, 0) // 初始化为空切片而不是nil切片
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
elem := v.Index(i).Interface()
|
||||
if !reflect.DeepEqual(elem, value) {
|
||||
@@ -151,7 +151,7 @@ func (f *ArrayDistinctFunction) Execute(ctx *FunctionContext, args []interface{}
|
||||
}
|
||||
|
||||
seen := make(map[interface{}]bool)
|
||||
var result []interface{}
|
||||
result := make([]interface{}, 0) // 初始化为空切片而不是nil切片
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
elem := v.Index(i).Interface()
|
||||
@@ -200,7 +200,7 @@ func (f *ArrayIntersectFunction) Execute(ctx *FunctionContext, args []interface{
|
||||
|
||||
// 找交集
|
||||
seen := make(map[interface{}]bool)
|
||||
var result []interface{}
|
||||
result := make([]interface{}, 0) // 初始化为空切片而不是nil切片
|
||||
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
elem := v1.Index(i).Interface()
|
||||
@@ -242,7 +242,7 @@ func (f *ArrayUnionFunction) Execute(ctx *FunctionContext, args []interface{}) (
|
||||
}
|
||||
|
||||
seen := make(map[interface{}]bool)
|
||||
var result []interface{}
|
||||
result := make([]interface{}, 0) // 初始化为空切片而不是nil切片
|
||||
|
||||
// 添加第一个数组的元素
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
@@ -301,7 +301,7 @@ func (f *ArrayExceptFunction) Execute(ctx *FunctionContext, args []interface{})
|
||||
|
||||
// 找差集
|
||||
seen := make(map[interface{}]bool)
|
||||
var result []interface{}
|
||||
result := make([]interface{}, 0) // 初始化为空切片而不是nil切片
|
||||
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
elem := v1.Index(i).Interface()
|
||||
|
||||
@@ -12,66 +12,175 @@ func TestArrayFunctions(t *testing.T) {
|
||||
funcName string
|
||||
args []interface{}
|
||||
expected interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "array_length basic",
|
||||
funcName: "array_length",
|
||||
args: []interface{}{[]interface{}{1, 2, 3}},
|
||||
expected: 3,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_length empty",
|
||||
funcName: "array_length",
|
||||
args: []interface{}{[]interface{}{}},
|
||||
expected: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_contains true",
|
||||
funcName: "array_contains",
|
||||
args: []interface{}{[]interface{}{1, 2, 3}, 2},
|
||||
expected: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_contains false",
|
||||
funcName: "array_contains",
|
||||
args: []interface{}{[]interface{}{1, 2, 3}, 4},
|
||||
expected: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_contains empty array",
|
||||
funcName: "array_contains",
|
||||
args: []interface{}{[]interface{}{}, 1},
|
||||
expected: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_position found",
|
||||
funcName: "array_position",
|
||||
args: []interface{}{[]interface{}{1, 2, 3}, 2},
|
||||
expected: 2,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_position not found",
|
||||
funcName: "array_position",
|
||||
args: []interface{}{[]interface{}{1, 2, 3}, 4},
|
||||
expected: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_position empty array",
|
||||
funcName: "array_position",
|
||||
args: []interface{}{[]interface{}{}, 1},
|
||||
expected: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_remove basic",
|
||||
funcName: "array_remove",
|
||||
args: []interface{}{[]interface{}{1, 2, 3, 2}, 2},
|
||||
expected: []interface{}{1, 3},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_remove not found",
|
||||
funcName: "array_remove",
|
||||
args: []interface{}{[]interface{}{1, 2, 3}, 4},
|
||||
expected: []interface{}{1, 2, 3},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_remove empty array",
|
||||
funcName: "array_remove",
|
||||
args: []interface{}{[]interface{}{}, 1},
|
||||
expected: []interface{}{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_distinct basic",
|
||||
funcName: "array_distinct",
|
||||
args: []interface{}{[]interface{}{1, 2, 2, 3, 1}},
|
||||
expected: []interface{}{1, 2, 3},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_distinct empty",
|
||||
funcName: "array_distinct",
|
||||
args: []interface{}{[]interface{}{}},
|
||||
expected: []interface{}{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_intersect basic",
|
||||
funcName: "array_intersect",
|
||||
args: []interface{}{[]interface{}{1, 2, 3}, []interface{}{2, 3, 4}},
|
||||
expected: []interface{}{2, 3},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_intersect no intersection",
|
||||
funcName: "array_intersect",
|
||||
args: []interface{}{[]interface{}{1, 2}, []interface{}{3, 4}},
|
||||
expected: []interface{}{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_intersect first empty",
|
||||
funcName: "array_intersect",
|
||||
args: []interface{}{[]interface{}{}, []interface{}{1, 2}},
|
||||
expected: []interface{}{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_intersect second empty",
|
||||
funcName: "array_intersect",
|
||||
args: []interface{}{[]interface{}{1, 2}, []interface{}{}},
|
||||
expected: []interface{}{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_union basic",
|
||||
funcName: "array_union",
|
||||
args: []interface{}{[]interface{}{1, 2}, []interface{}{2, 3}},
|
||||
expected: []interface{}{1, 2, 3},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_union first empty",
|
||||
funcName: "array_union",
|
||||
args: []interface{}{[]interface{}{}, []interface{}{1, 2}},
|
||||
expected: []interface{}{1, 2},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_union second empty",
|
||||
funcName: "array_union",
|
||||
args: []interface{}{[]interface{}{1, 2}, []interface{}{}},
|
||||
expected: []interface{}{1, 2},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_except basic",
|
||||
funcName: "array_except",
|
||||
args: []interface{}{[]interface{}{1, 2, 3}, []interface{}{2}},
|
||||
expected: []interface{}{1, 3},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_except no overlap",
|
||||
funcName: "array_except",
|
||||
args: []interface{}{[]interface{}{1, 2}, []interface{}{3, 4}},
|
||||
expected: []interface{}{1, 2},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_except first empty",
|
||||
funcName: "array_except",
|
||||
args: []interface{}{[]interface{}{}, []interface{}{1, 2}},
|
||||
expected: []interface{}{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "array_except second empty",
|
||||
funcName: "array_except",
|
||||
args: []interface{}{[]interface{}{1, 2}, []interface{}{}},
|
||||
expected: []interface{}{1, 2},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -83,13 +192,75 @@ func TestArrayFunctions(t *testing.T) {
|
||||
}
|
||||
|
||||
result, err := fn.Execute(&FunctionContext{}, tt.args)
|
||||
if err != nil {
|
||||
t.Errorf("Execute() error = %v", err)
|
||||
return
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if !tt.wantErr && !reflect.DeepEqual(result, tt.expected) {
|
||||
t.Errorf("Execute() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestArrayFunctionErrors 测试数组函数的错误处理
|
||||
func TestArrayFunctionErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
funcName string
|
||||
args []interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
// array_length 错误测试
|
||||
{"array_length nil", "array_length", []interface{}{nil}, true},
|
||||
{"array_length invalid type", "array_length", []interface{}{"not an array"}, true},
|
||||
|
||||
// array_contains 错误测试
|
||||
{"array_contains nil array", "array_contains", []interface{}{nil, 1}, true},
|
||||
{"array_contains invalid type", "array_contains", []interface{}{"not an array", 1}, true},
|
||||
|
||||
// array_position 错误测试
|
||||
{"array_position nil array", "array_position", []interface{}{nil, 1}, true},
|
||||
{"array_position invalid type", "array_position", []interface{}{"not an array", 1}, true},
|
||||
|
||||
// array_remove 错误测试
|
||||
{"array_remove nil array", "array_remove", []interface{}{nil, 1}, true},
|
||||
{"array_remove invalid type", "array_remove", []interface{}{"not an array", 1}, true},
|
||||
|
||||
// array_distinct 错误测试
|
||||
{"array_distinct nil", "array_distinct", []interface{}{nil}, true},
|
||||
{"array_distinct invalid type", "array_distinct", []interface{}{"not an array"}, true},
|
||||
|
||||
// array_intersect 错误测试
|
||||
{"array_intersect first nil", "array_intersect", []interface{}{nil, []interface{}{1, 2}}, true},
|
||||
{"array_intersect second nil", "array_intersect", []interface{}{[]interface{}{1, 2}, nil}, true},
|
||||
{"array_intersect first invalid type", "array_intersect", []interface{}{"not an array", []interface{}{1, 2}}, true},
|
||||
{"array_intersect second invalid type", "array_intersect", []interface{}{[]interface{}{1, 2}, "not an array"}, true},
|
||||
|
||||
// array_union 错误测试
|
||||
{"array_union first nil", "array_union", []interface{}{nil, []interface{}{1, 2}}, true},
|
||||
{"array_union second nil", "array_union", []interface{}{[]interface{}{1, 2}, nil}, true},
|
||||
{"array_union first invalid type", "array_union", []interface{}{"not an array", []interface{}{1, 2}}, true},
|
||||
{"array_union second invalid type", "array_union", []interface{}{[]interface{}{1, 2}, "not an array"}, true},
|
||||
|
||||
// array_except 错误测试
|
||||
{"array_except first nil", "array_except", []interface{}{nil, []interface{}{1, 2}}, true},
|
||||
{"array_except second nil", "array_except", []interface{}{[]interface{}{1, 2}, nil}, true},
|
||||
{"array_except first invalid type", "array_except", []interface{}{"not an array", []interface{}{1, 2}}, true},
|
||||
{"array_except second invalid type", "array_except", []interface{}{[]interface{}{1, 2}, "not an array"}, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fn, exists := Get(tt.funcName)
|
||||
if !exists {
|
||||
t.Fatalf("Function %s not found", tt.funcName)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(result, tt.expected) {
|
||||
t.Errorf("Execute() = %v, want %v", result, tt.expected)
|
||||
_, err := fn.Execute(&FunctionContext{}, tt.args)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 测试条件函数
|
||||
// TestConditionalFunctions 测试条件函数的基本功能
|
||||
func TestConditionalFunctions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -103,3 +103,202 @@ func TestConditionalFunctions(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestConditionalFunctionValidation 测试条件函数的参数验证
|
||||
func TestConditionalFunctionValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
function Function
|
||||
args []interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "if_null no args",
|
||||
function: NewIfNullFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "if_null one arg",
|
||||
function: NewIfNullFunction(),
|
||||
args: []interface{}{"test"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "if_null valid args",
|
||||
function: NewIfNullFunction(),
|
||||
args: []interface{}{nil, "default"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "coalesce no args",
|
||||
function: NewCoalesceFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "coalesce valid args",
|
||||
function: NewCoalesceFunction(),
|
||||
args: []interface{}{nil, "default"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "null_if no args",
|
||||
function: NewNullIfFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "null_if one arg",
|
||||
function: NewNullIfFunction(),
|
||||
args: []interface{}{"test"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "null_if valid args",
|
||||
function: NewNullIfFunction(),
|
||||
args: []interface{}{"test", "test"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "greatest no args",
|
||||
function: NewGreatestFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "greatest valid args",
|
||||
function: NewGreatestFunction(),
|
||||
args: []interface{}{1, 2, 3},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "least no args",
|
||||
function: NewLeastFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "least valid args",
|
||||
function: NewLeastFunction(),
|
||||
args: []interface{}{1, 2, 3},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "case_when no args",
|
||||
function: NewCaseWhenFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "case_when one arg",
|
||||
function: NewCaseWhenFunction(),
|
||||
args: []interface{}{true},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "case_when valid args",
|
||||
function: NewCaseWhenFunction(),
|
||||
args: []interface{}{true, "result"},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.function.Validate(tt.args)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestConditionalFunctionEdgeCases 测试条件函数的边界情况
|
||||
func TestConditionalFunctionEdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
function Function
|
||||
args []interface{}
|
||||
expected interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "coalesce all null",
|
||||
function: NewCoalesceFunction(),
|
||||
args: []interface{}{nil, nil, nil},
|
||||
expected: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "coalesce first non-null",
|
||||
function: NewCoalesceFunction(),
|
||||
args: []interface{}{"first", nil, "third"},
|
||||
expected: "first",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "coalesce middle non-null",
|
||||
function: NewCoalesceFunction(),
|
||||
args: []interface{}{nil, "second", "third"},
|
||||
expected: "second",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "greatest with mixed types",
|
||||
function: NewGreatestFunction(),
|
||||
args: []interface{}{1, 3.14, 2},
|
||||
expected: 3.14,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "least with mixed types",
|
||||
function: NewLeastFunction(),
|
||||
args: []interface{}{1, 3.14, 2},
|
||||
expected: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "greatest with strings",
|
||||
function: NewGreatestFunction(),
|
||||
args: []interface{}{"apple", "banana", "cherry"},
|
||||
expected: "cherry",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "least with strings",
|
||||
function: NewLeastFunction(),
|
||||
args: []interface{}{"apple", "banana", "cherry"},
|
||||
expected: "apple",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "case_when with complex conditions",
|
||||
function: NewCaseWhenFunction(),
|
||||
args: []interface{}{false, "first", false, "second", true, "third", "default"},
|
||||
expected: "third",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "null_if with different types",
|
||||
function: NewNullIfFunction(),
|
||||
args: []interface{}{"123", 123},
|
||||
expected: "123",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := tt.function.Execute(&FunctionContext{}, tt.args)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if !tt.wantErr && result != tt.expected {
|
||||
t.Errorf("Execute() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +179,261 @@ func TestDateTimeFunctions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestDateTimeFunctionValidation 测试日期时间函数的参数验证
|
||||
func TestDateTimeFunctionValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
function Function
|
||||
args []interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "now no args",
|
||||
function: NewNowFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "now too many args",
|
||||
function: NewNowFunction(),
|
||||
args: []interface{}{"extra"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "current_time no args",
|
||||
function: NewCurrentTimeFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "current_date no args",
|
||||
function: NewCurrentDateFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "date_format no args",
|
||||
function: NewDateFormatFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "date_format one arg",
|
||||
function: NewDateFormatFunction(),
|
||||
args: []interface{}{"2023-12-25"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "date_format valid args",
|
||||
function: NewDateFormatFunction(),
|
||||
args: []interface{}{"2023-12-25", "YYYY-MM-DD"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "date_add no args",
|
||||
function: NewDateAddFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "date_add two args",
|
||||
function: NewDateAddFunction(),
|
||||
args: []interface{}{"2023-12-25", 7},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "date_add valid args",
|
||||
function: NewDateAddFunction(),
|
||||
args: []interface{}{"2023-12-25", 7, "days"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "year no args",
|
||||
function: NewYearFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "year valid args",
|
||||
function: NewYearFunction(),
|
||||
args: []interface{}{"2023-12-25"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "extract no args",
|
||||
function: NewExtractFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "extract one arg",
|
||||
function: NewExtractFunction(),
|
||||
args: []interface{}{"year"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "extract valid args",
|
||||
function: NewExtractFunction(),
|
||||
args: []interface{}{"year", "2023-12-25"},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.function.Validate(tt.args)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDateTimeFunctionErrors 测试日期时间函数的错误处理
|
||||
func TestDateTimeFunctionErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
function Function
|
||||
args []interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "date_format invalid date",
|
||||
function: NewDateFormatFunction(),
|
||||
args: []interface{}{"invalid-date", "YYYY-MM-DD"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "date_add invalid date",
|
||||
function: NewDateAddFunction(),
|
||||
args: []interface{}{"invalid-date", 7, "days"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "date_add invalid unit",
|
||||
function: NewDateAddFunction(),
|
||||
args: []interface{}{"2023-12-25", 7, "invalid-unit"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "year invalid date",
|
||||
function: NewYearFunction(),
|
||||
args: []interface{}{"invalid-date"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "extract invalid unit",
|
||||
function: NewExtractFunction(),
|
||||
args: []interface{}{"invalid-unit", "2023-12-25"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "extract invalid date",
|
||||
function: NewExtractFunction(),
|
||||
args: []interface{}{"year", "invalid-date"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := tt.function.Execute(&FunctionContext{}, tt.args)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDateTimeFunctionEdgeCases 测试日期时间函数的边界情况
|
||||
func TestDateTimeFunctionEdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
function Function
|
||||
args []interface{}
|
||||
expected interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "now function",
|
||||
function: NewNowFunction(),
|
||||
args: []interface{}{},
|
||||
expected: nil, // 不检查具体值,只检查不出错
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "current_time function",
|
||||
function: NewCurrentTimeFunction(),
|
||||
args: []interface{}{},
|
||||
expected: nil, // 不检查具体值,只检查不出错
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "current_date function",
|
||||
function: NewCurrentDateFunction(),
|
||||
args: []interface{}{},
|
||||
expected: nil, // 不检查具体值,只检查不出错
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "unix_timestamp with valid date",
|
||||
function: NewUnixTimestampFunction(),
|
||||
args: []interface{}{"2023-01-01 00:00:00"},
|
||||
expected: nil, // 不检查具体值,只检查不出错
|
||||
wantErr: false,
|
||||
},
|
||||
// 新增边界情况测试
|
||||
{
|
||||
name: "date_format empty string",
|
||||
function: NewDateFormatFunction(),
|
||||
args: []interface{}{"", "YYYY-MM-DD"},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "date_add zero days",
|
||||
function: NewDateAddFunction(),
|
||||
args: []interface{}{"2023-12-25", 0, "days"},
|
||||
expected: "2023-12-25 00:00:00",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "date_diff same date",
|
||||
function: NewDateDiffFunction(),
|
||||
args: []interface{}{"2023-12-25", "2023-12-25", "days"},
|
||||
expected: int64(0),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dayofyear function",
|
||||
function: NewDayOfYearFunction(),
|
||||
args: []interface{}{"2023-12-25"},
|
||||
expected: 359,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "weekofyear function",
|
||||
function: NewWeekOfYearFunction(),
|
||||
args: []interface{}{"2023-12-25"},
|
||||
expected: 52,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := tt.function.Execute(&FunctionContext{}, tt.args)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !tt.wantErr && tt.expected != nil && result != tt.expected {
|
||||
t.Errorf("Execute() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateTimeRegistration(t *testing.T) {
|
||||
// 测试函数是否正确注册
|
||||
dateTimeFunctions := []string{
|
||||
@@ -199,6 +454,9 @@ func TestDateTimeRegistration(t *testing.T) {
|
||||
"dayofweek",
|
||||
"dayofyear",
|
||||
"weekofyear",
|
||||
"now",
|
||||
"current_time",
|
||||
"current_date",
|
||||
}
|
||||
|
||||
for _, funcName := range dateTimeFunctions {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 测试哈希函数
|
||||
// TestHashFunctions 测试哈希函数的基本功能
|
||||
func TestHashFunctions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -30,6 +30,24 @@ func TestHashFunctions(t *testing.T) {
|
||||
args: []interface{}{"hello"},
|
||||
expected: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
|
||||
},
|
||||
{
|
||||
name: "sha512 basic",
|
||||
funcName: "sha512",
|
||||
args: []interface{}{"hello"},
|
||||
expected: "9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043",
|
||||
},
|
||||
{
|
||||
name: "md5 empty string",
|
||||
funcName: "md5",
|
||||
args: []interface{}{""},
|
||||
expected: "d41d8cd98f00b204e9800998ecf8427e",
|
||||
},
|
||||
{
|
||||
name: "sha1 empty string",
|
||||
funcName: "sha1",
|
||||
args: []interface{}{""},
|
||||
expected: "da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -51,3 +69,103 @@ func TestHashFunctions(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestHashFunctionValidation 测试哈希函数的参数验证
|
||||
func TestHashFunctionValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
function Function
|
||||
args []interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "md5 no args",
|
||||
function: NewMd5Function(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "md5 too many args",
|
||||
function: NewMd5Function(),
|
||||
args: []interface{}{"hello", "world"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "md5 valid args",
|
||||
function: NewMd5Function(),
|
||||
args: []interface{}{"hello"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "sha1 no args",
|
||||
function: NewSha1Function(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "sha256 valid args",
|
||||
function: NewSha256Function(),
|
||||
args: []interface{}{"test"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "sha512 valid args",
|
||||
function: NewSha512Function(),
|
||||
args: []interface{}{"test"},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.function.Validate(tt.args)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestHashFunctionErrors 测试哈希函数的错误处理
|
||||
func TestHashFunctionErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
function Function
|
||||
args []interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "md5 non-string input",
|
||||
function: NewMd5Function(),
|
||||
args: []interface{}{123},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "sha1 non-string input",
|
||||
function: NewSha1Function(),
|
||||
args: []interface{}{123},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "sha256 non-string input",
|
||||
function: NewSha256Function(),
|
||||
args: []interface{}{123},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "sha512 non-string input",
|
||||
function: NewSha512Function(),
|
||||
args: []interface{}{123},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := tt.function.Execute(&FunctionContext{}, tt.args)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 测试类型检查函数
|
||||
// TestTypeFunctions 测试类型检查函数的基本功能
|
||||
func TestTypeFunctions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -93,3 +93,180 @@ func TestTypeFunctions(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestTypeFunctionValidation 测试类型函数的参数验证
|
||||
func TestTypeFunctionValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
function Function
|
||||
args []interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "is_null no args",
|
||||
function: NewIsNullFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "is_null too many args",
|
||||
function: NewIsNullFunction(),
|
||||
args: []interface{}{"test", "extra"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "is_null valid args",
|
||||
function: NewIsNullFunction(),
|
||||
args: []interface{}{"test"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "is_not_null no args",
|
||||
function: NewIsNotNullFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "is_not_null valid args",
|
||||
function: NewIsNotNullFunction(),
|
||||
args: []interface{}{nil},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "is_numeric no args",
|
||||
function: NewIsNumericFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "is_numeric valid args",
|
||||
function: NewIsNumericFunction(),
|
||||
args: []interface{}{123},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "is_string no args",
|
||||
function: NewIsStringFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "is_string valid args",
|
||||
function: NewIsStringFunction(),
|
||||
args: []interface{}{"test"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "is_bool no args",
|
||||
function: NewIsBoolFunction(),
|
||||
args: []interface{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "is_bool valid args",
|
||||
function: NewIsBoolFunction(),
|
||||
args: []interface{}{true},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.function.Validate(tt.args)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestTypeFunctionEdgeCases 测试类型函数的边界情况
|
||||
func TestTypeFunctionEdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
function Function
|
||||
args []interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
name: "is_numeric with float",
|
||||
function: NewIsNumericFunction(),
|
||||
args: []interface{}{3.14},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "is_numeric with int64",
|
||||
function: NewIsNumericFunction(),
|
||||
args: []interface{}{int64(123)},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "is_numeric with float32",
|
||||
function: NewIsNumericFunction(),
|
||||
args: []interface{}{float32(3.14)},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "is_numeric with float64",
|
||||
function: NewIsNumericFunction(),
|
||||
args: []interface{}{float64(3.14)},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "is_numeric with int32",
|
||||
function: NewIsNumericFunction(),
|
||||
args: []interface{}{int32(123)},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "is_numeric with uint",
|
||||
function: NewIsNumericFunction(),
|
||||
args: []interface{}{uint(123)},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "is_numeric with uint64",
|
||||
function: NewIsNumericFunction(),
|
||||
args: []interface{}{uint64(123)},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "is_numeric with uint32",
|
||||
function: NewIsNumericFunction(),
|
||||
args: []interface{}{uint32(123)},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "is_numeric with bool",
|
||||
function: NewIsNumericFunction(),
|
||||
args: []interface{}{true},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "is_string with empty string",
|
||||
function: NewIsStringFunction(),
|
||||
args: []interface{}{""},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "is_bool with false",
|
||||
function: NewIsBoolFunction(),
|
||||
args: []interface{}{false},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := tt.function.Execute(&FunctionContext{}, tt.args)
|
||||
if err != nil {
|
||||
t.Errorf("Execute() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if result != tt.expected {
|
||||
t.Errorf("Execute() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+309
-176
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user