mirror of
https://gitee.com/rulego/streamsql.git
synced 2026-03-14 14:27:27 +00:00
test:add test cases
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,297 @@
|
||||
package functions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestNewAnalyticalAggregatorAdapter 测试创建分析聚合器适配器
|
||||
func TestNewAnalyticalAggregatorAdapter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
funcName string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "valid analytical function",
|
||||
funcName: "lag",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "invalid function name",
|
||||
funcName: "nonexistent_function",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
adapter, err := NewAnalyticalAggregatorAdapter(tt.funcName)
|
||||
if tt.expectError {
|
||||
if err == nil {
|
||||
t.Error("Expected error but got none")
|
||||
}
|
||||
if adapter != nil {
|
||||
t.Error("Expected nil adapter but got one")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if adapter == nil {
|
||||
t.Error("Expected adapter but got nil")
|
||||
}
|
||||
if adapter != nil {
|
||||
if adapter.analFunc == nil {
|
||||
t.Error("Expected analytical function but got nil")
|
||||
}
|
||||
if adapter.ctx == nil {
|
||||
t.Error("Expected context but got nil")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAnalyticalAggregatorAdapterNew 测试New方法
|
||||
func TestAnalyticalAggregatorAdapterNew(t *testing.T) {
|
||||
// 创建一个测试适配器
|
||||
adapter, err := NewAnalyticalAggregatorAdapter("lag")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create adapter: %v", err)
|
||||
}
|
||||
|
||||
// 测试New方法
|
||||
newAdapter := adapter.New()
|
||||
if newAdapter == nil {
|
||||
t.Error("New() should return a new adapter")
|
||||
}
|
||||
|
||||
// 验证返回的是正确的类型
|
||||
if _, ok := newAdapter.(*AnalyticalAggregatorAdapter); !ok {
|
||||
t.Error("New() should return AnalyticalAggregatorAdapter")
|
||||
}
|
||||
|
||||
// 验证新适配器有独立的上下文
|
||||
newAdapterTyped := newAdapter.(*AnalyticalAggregatorAdapter)
|
||||
if newAdapterTyped.ctx == adapter.ctx {
|
||||
t.Error("New adapter should have independent context")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAnalyticalAggregatorAdapterAdd 测试Add方法
|
||||
func TestAnalyticalAggregatorAdapterAdd(t *testing.T) {
|
||||
adapter, err := NewAnalyticalAggregatorAdapter("lag")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create adapter: %v", err)
|
||||
}
|
||||
|
||||
// 测试Add方法
|
||||
adapter.Add(10)
|
||||
adapter.Add(20)
|
||||
adapter.Add(30)
|
||||
|
||||
// 验证没有panic
|
||||
t.Log("Add method executed successfully")
|
||||
}
|
||||
|
||||
// TestAnalyticalAggregatorAdapterResult 测试Result方法
|
||||
func TestAnalyticalAggregatorAdapterResult(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
funcName string
|
||||
}{
|
||||
{"lag function", "lag"},
|
||||
{"latest function", "latest"},
|
||||
{"had_changed function", "had_changed"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
adapter, err := NewAnalyticalAggregatorAdapter(tt.funcName)
|
||||
if err != nil {
|
||||
t.Skipf("Function %s not available: %v", tt.funcName, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 添加一些数据
|
||||
adapter.Add(10)
|
||||
adapter.Add(20)
|
||||
|
||||
// 获取结果
|
||||
result := adapter.Result()
|
||||
t.Logf("Result for %s: %v", tt.funcName, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateAnalyticalAggregatorFromFunctions 测试从函数模块创建分析聚合器
|
||||
func TestCreateAnalyticalAggregatorFromFunctions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
funcType string
|
||||
expected bool // 是否期望创建成功
|
||||
}{
|
||||
{
|
||||
name: "valid analytical function",
|
||||
funcType: "lag",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "another valid function",
|
||||
funcType: "latest",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "invalid function",
|
||||
funcType: "nonexistent",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := CreateAnalyticalAggregatorFromFunctions(tt.funcType)
|
||||
if tt.expected {
|
||||
if result == nil {
|
||||
t.Errorf("Expected to create aggregator for %s but got nil", tt.funcType)
|
||||
} else {
|
||||
// 验证返回的是正确的类型
|
||||
if _, ok := result.(*AnalyticalAggregatorAdapter); !ok {
|
||||
t.Errorf("Expected AnalyticalAggregatorAdapter but got %T", result)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if result != nil {
|
||||
t.Errorf("Expected nil for %s but got %v", tt.funcType, result)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAnalyticalAggregatorAdapterWithMockFunction 测试使用模拟函数的适配器
|
||||
func TestAnalyticalAggregatorAdapterWithMockFunction(t *testing.T) {
|
||||
// 创建模拟分析函数
|
||||
mockFunc := &MockAnalyticalFunction{
|
||||
name: "mock_analytical",
|
||||
values: []interface{}{},
|
||||
}
|
||||
|
||||
// 创建适配器
|
||||
adapter := &AnalyticalAggregatorAdapter{
|
||||
analFunc: mockFunc,
|
||||
ctx: &FunctionContext{
|
||||
Data: make(map[string]interface{}),
|
||||
},
|
||||
}
|
||||
|
||||
// 测试Add方法
|
||||
adapter.Add("test1")
|
||||
adapter.Add("test2")
|
||||
|
||||
// 验证值被添加
|
||||
if len(mockFunc.values) != 2 {
|
||||
t.Errorf("Expected 2 values, got %d", len(mockFunc.values))
|
||||
}
|
||||
|
||||
// 测试Result方法
|
||||
result := adapter.Result()
|
||||
if result != "mock_result" {
|
||||
t.Errorf("Expected 'mock_result', got %v", result)
|
||||
}
|
||||
|
||||
// 测试New方法
|
||||
newAdapter := adapter.New()
|
||||
if newAdapter == nil {
|
||||
t.Error("New() should return a new adapter")
|
||||
}
|
||||
}
|
||||
|
||||
// MockAnalyticalFunction 模拟分析函数用于测试
|
||||
type MockAnalyticalFunction struct {
|
||||
name string
|
||||
values []interface{}
|
||||
}
|
||||
|
||||
// GetName 返回函数名称
|
||||
func (m *MockAnalyticalFunction) GetName() string {
|
||||
return m.name
|
||||
}
|
||||
|
||||
// GetType 返回函数类型
|
||||
func (m *MockAnalyticalFunction) GetType() FunctionType {
|
||||
return TypeAnalytical
|
||||
}
|
||||
|
||||
// GetCategory 返回函数分类
|
||||
func (m *MockAnalyticalFunction) GetCategory() string {
|
||||
return "mock"
|
||||
}
|
||||
|
||||
// GetDescription 返回函数描述
|
||||
func (m *MockAnalyticalFunction) GetDescription() string {
|
||||
return "Mock analytical function for testing"
|
||||
}
|
||||
|
||||
// GetAliases 返回函数别名
|
||||
func (m *MockAnalyticalFunction) GetAliases() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// GetMinArgs 返回最小参数数量
|
||||
func (m *MockAnalyticalFunction) GetMinArgs() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// GetMaxArgs 返回最大参数数量
|
||||
func (m *MockAnalyticalFunction) GetMaxArgs() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Validate 验证参数
|
||||
func (m *MockAnalyticalFunction) Validate(args []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Execute 执行函数
|
||||
func (m *MockAnalyticalFunction) Execute(ctx *FunctionContext, args []interface{}) (interface{}, error) {
|
||||
if len(args) > 0 {
|
||||
m.values = append(m.values, args[0])
|
||||
}
|
||||
return "mock_result", nil
|
||||
}
|
||||
|
||||
// Add 添加值到聚合器
|
||||
func (m *MockAnalyticalFunction) Add(value interface{}) {
|
||||
m.values = append(m.values, value)
|
||||
}
|
||||
|
||||
// Result 返回聚合结果
|
||||
func (m *MockAnalyticalFunction) Result() interface{} {
|
||||
return len(m.values)
|
||||
}
|
||||
|
||||
// Reset 重置聚合器
|
||||
func (m *MockAnalyticalFunction) Reset() {
|
||||
m.values = make([]interface{}, 0)
|
||||
}
|
||||
|
||||
// New 创建新的聚合器实例
|
||||
func (m *MockAnalyticalFunction) New() AggregatorFunction {
|
||||
newMock := &MockAnalyticalFunction{
|
||||
name: m.name,
|
||||
values: make([]interface{}, 0),
|
||||
}
|
||||
return newMock
|
||||
}
|
||||
|
||||
// Clone 克隆函数 - 返回AggregatorFunction以满足接口要求
|
||||
func (m *MockAnalyticalFunction) Clone() AggregatorFunction {
|
||||
newMock := &MockAnalyticalFunction{
|
||||
name: m.name,
|
||||
values: make([]interface{}, len(m.values)),
|
||||
}
|
||||
copy(newMock.values, m.values)
|
||||
return newMock
|
||||
}
|
||||
@@ -53,4 +53,155 @@ func TestExprFunctionEdgeCases(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Error("ExprFunction.Execute should fail for empty args")
|
||||
}
|
||||
|
||||
// 测试非字符串参数(现在应该成功)
|
||||
ctx := &FunctionContext{Data: map[string]interface{}{}}
|
||||
_, err = fn.Execute(ctx, []interface{}{123})
|
||||
if err != nil {
|
||||
t.Errorf("ExprFunction.Execute should accept non-string argument: %v", err)
|
||||
}
|
||||
|
||||
// 测试无效表达式
|
||||
_, err = fn.Execute(ctx, []interface{}{"invalid expression +++"})
|
||||
if err == nil {
|
||||
t.Error("ExprFunction.Execute should fail for invalid expression")
|
||||
}
|
||||
}
|
||||
|
||||
// TestExprFunctionCreation 测试ExprFunction的创建和属性
|
||||
func TestExprFunctionCreation(t *testing.T) {
|
||||
fn := NewExprFunction()
|
||||
if fn == nil {
|
||||
t.Error("NewExprFunction should not return nil")
|
||||
}
|
||||
|
||||
if fn.GetName() != "expr" {
|
||||
t.Errorf("Expected name 'expr', got %s", fn.GetName())
|
||||
}
|
||||
|
||||
if fn.GetType() != TypeString {
|
||||
t.Errorf("Expected type %s, got %s", TypeString, fn.GetType())
|
||||
}
|
||||
|
||||
// BaseFunction doesn't expose GetMinArgs/GetMaxArgs methods
|
||||
// We can only test through Validate method
|
||||
err := fn.Validate([]interface{}{"test"})
|
||||
if err != nil {
|
||||
t.Errorf("Validate should accept 1 argument: %v", err)
|
||||
}
|
||||
|
||||
err = fn.Validate([]interface{}{})
|
||||
if err == nil {
|
||||
t.Error("Validate should reject 0 arguments")
|
||||
}
|
||||
|
||||
err = fn.Validate([]interface{}{"arg1", "arg2"})
|
||||
if err == nil {
|
||||
t.Error("Validate should reject 2 arguments")
|
||||
}
|
||||
|
||||
if fn.GetCategory() == "" {
|
||||
t.Error("Function category should not be empty")
|
||||
}
|
||||
|
||||
if fn.GetDescription() == "" {
|
||||
t.Error("Function description should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
// TestExprFunctionWithDifferentExpressions 测试不同类型的表达式
|
||||
func TestExprFunctionWithDifferentExpressions(t *testing.T) {
|
||||
fn := NewExprFunction()
|
||||
ctx := &FunctionContext{
|
||||
Data: map[string]interface{}{
|
||||
"x": 10,
|
||||
"y": 5,
|
||||
"name": "John",
|
||||
"active": true,
|
||||
},
|
||||
}
|
||||
|
||||
// 测试数学表达式
|
||||
result, err := fn.Execute(ctx, []interface{}{"x + y"})
|
||||
if err != nil {
|
||||
t.Errorf("Math expression failed: %v", err)
|
||||
}
|
||||
if result != 15 {
|
||||
t.Errorf("Expected 15, got %v", result)
|
||||
}
|
||||
|
||||
// 测试比较表达式
|
||||
result, err = fn.Execute(ctx, []interface{}{"x > y"})
|
||||
if err != nil {
|
||||
t.Errorf("Comparison expression failed: %v", err)
|
||||
}
|
||||
if result != true {
|
||||
t.Errorf("Expected true, got %v", result)
|
||||
}
|
||||
|
||||
// 测试字符串表达式
|
||||
result, err = fn.Execute(ctx, []interface{}{"name + ' Doe'"})
|
||||
if err != nil {
|
||||
t.Errorf("String expression failed: %v", err)
|
||||
}
|
||||
if result != "John Doe" {
|
||||
t.Errorf("Expected 'John Doe', got %v", result)
|
||||
}
|
||||
|
||||
// 测试布尔表达式
|
||||
result, err = fn.Execute(ctx, []interface{}{"active && true"})
|
||||
if err != nil {
|
||||
t.Errorf("Boolean expression failed: %v", err)
|
||||
}
|
||||
if result != true {
|
||||
t.Errorf("Expected true, got %v", result)
|
||||
}
|
||||
|
||||
// 测试复杂表达式
|
||||
result, err = fn.Execute(ctx, []interface{}{"(x + y) * 2"})
|
||||
if err != nil {
|
||||
t.Errorf("Complex expression failed: %v", err)
|
||||
}
|
||||
if result != 30 {
|
||||
t.Errorf("Expected 30, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestExprFunctionWithFunctionCalls 测试函数调用表达式
|
||||
func TestExprFunctionWithFunctionCalls(t *testing.T) {
|
||||
fn := NewExprFunction()
|
||||
ctx := &FunctionContext{
|
||||
Data: map[string]interface{}{
|
||||
"text": "Hello World",
|
||||
"num": -42,
|
||||
},
|
||||
}
|
||||
|
||||
// 测试abs函数调用
|
||||
result, err := fn.Execute(ctx, []interface{}{"abs(-10)"})
|
||||
if err != nil {
|
||||
t.Errorf("Function call expression failed: %v", err)
|
||||
}
|
||||
if result != float64(10) {
|
||||
t.Errorf("Expected 10, got %v", result)
|
||||
}
|
||||
|
||||
// 测试length函数调用
|
||||
result, err = fn.Execute(ctx, []interface{}{"length(text)"})
|
||||
if err != nil {
|
||||
t.Errorf("Length function call failed: %v", err)
|
||||
}
|
||||
if result != 11 {
|
||||
t.Errorf("Expected 11, got %v", result)
|
||||
}
|
||||
|
||||
// 测试组合函数调用
|
||||
result, err = fn.Execute(ctx, []interface{}{"abs(num) + length(text)"})
|
||||
if err != nil {
|
||||
t.Errorf("Combined function calls failed: %v", err)
|
||||
}
|
||||
expected := float64(53) // 42 + 11
|
||||
if result != expected {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,132 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnnestFunction(t *testing.T) {
|
||||
fn := NewUnnestFunction()
|
||||
ctx := &FunctionContext{}
|
||||
|
||||
// 测试基本unnest功能
|
||||
args := []interface{}{[]interface{}{"a", "b", "c"}}
|
||||
result, err := fn.Execute(ctx, args)
|
||||
if err != nil {
|
||||
t.Errorf("UnnestFunction should not return error: %v", err)
|
||||
}
|
||||
expected := []interface{}{"a", "b", "c"}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("UnnestFunction = %v, want %v", result, expected)
|
||||
}
|
||||
|
||||
// 测试对象数组unnest
|
||||
args = []interface{}{
|
||||
[]interface{}{
|
||||
map[string]interface{}{"name": "Alice", "age": 25},
|
||||
map[string]interface{}{"name": "Bob", "age": 30},
|
||||
},
|
||||
}
|
||||
result, err = fn.Execute(ctx, args)
|
||||
if err != nil {
|
||||
t.Errorf("UnnestFunction should not return error: %v", err)
|
||||
}
|
||||
expected = []interface{}{
|
||||
map[string]interface{}{
|
||||
"__unnest_object__": true,
|
||||
"__data__": map[string]interface{}{"name": "Alice", "age": 25},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"__unnest_object__": true,
|
||||
"__data__": map[string]interface{}{"name": "Bob", "age": 30},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("UnnestFunction = %v, want %v", result, expected)
|
||||
}
|
||||
|
||||
// 测试空数组
|
||||
args = []interface{}{[]interface{}{}}
|
||||
result, err = fn.Execute(ctx, args)
|
||||
if err != nil {
|
||||
t.Errorf("UnnestFunction should not return error for empty array: %v", err)
|
||||
}
|
||||
if len(result.([]interface{})) != 0 {
|
||||
t.Errorf("UnnestFunction should return empty array for empty input")
|
||||
}
|
||||
|
||||
// 测试nil参数
|
||||
args = []interface{}{nil}
|
||||
result, err = fn.Execute(ctx, args)
|
||||
if err != nil {
|
||||
t.Errorf("UnnestFunction should not return error for nil: %v", err)
|
||||
}
|
||||
if len(result.([]interface{})) != 0 {
|
||||
t.Errorf("UnnestFunction should return empty array for nil input")
|
||||
}
|
||||
|
||||
// 测试错误参数数量
|
||||
args = []interface{}{}
|
||||
err = fn.Validate(args)
|
||||
if err == nil {
|
||||
t.Errorf("UnnestFunction should return error for no arguments")
|
||||
}
|
||||
|
||||
// 测试非数组参数
|
||||
args = []interface{}{"not an array"}
|
||||
_, err = fn.Execute(ctx, args)
|
||||
if err == nil {
|
||||
t.Errorf("UnnestFunction should return error for non-array argument")
|
||||
}
|
||||
|
||||
// 测试数组类型
|
||||
args = []interface{}{[3]string{"x", "y", "z"}}
|
||||
result, err = fn.Execute(ctx, args)
|
||||
if err != nil {
|
||||
t.Errorf("UnnestFunction should handle arrays: %v", err)
|
||||
}
|
||||
expected = []interface{}{"x", "y", "z"}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("UnnestFunction array = %v, want %v", result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUnnestFunctionCreation 测试UnnestFunction创建
|
||||
func TestUnnestFunctionCreation(t *testing.T) {
|
||||
fn := NewUnnestFunction()
|
||||
if fn == nil {
|
||||
t.Error("NewUnnestFunction should not return nil")
|
||||
}
|
||||
|
||||
if fn.GetName() != "unnest" {
|
||||
t.Errorf("Expected name 'unnest', got %s", fn.GetName())
|
||||
}
|
||||
|
||||
// Test argument validation through Validate method
|
||||
err := fn.Validate([]interface{}{"test"})
|
||||
if err != nil {
|
||||
t.Errorf("Validate should accept 1 argument: %v", err)
|
||||
}
|
||||
|
||||
err = fn.Validate([]interface{}{})
|
||||
if err == nil {
|
||||
t.Error("Validate should reject 0 arguments")
|
||||
}
|
||||
|
||||
err = fn.Validate([]interface{}{"arg1", "arg2"})
|
||||
if err == nil {
|
||||
t.Error("Validate should reject 2 arguments")
|
||||
}
|
||||
|
||||
if fn.GetType() == "" {
|
||||
t.Error("Function type should not be empty")
|
||||
}
|
||||
|
||||
if fn.GetCategory() == "" {
|
||||
t.Error("Function category should not be empty")
|
||||
}
|
||||
|
||||
if fn.GetDescription() == "" {
|
||||
t.Error("Function description should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsUnnestResult(t *testing.T) {
|
||||
// 测试非unnest结果
|
||||
normalSlice := []interface{}{"a", "b", "c"}
|
||||
|
||||
Reference in New Issue
Block a user