Files
streamsql/stream/processor_field_test.go
T
2025-08-07 19:23:48 +08:00

357 lines
9.7 KiB
Go

/*
* Copyright 2025 The RuleGo Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package stream
import (
"fmt"
"testing"
"github.com/rulego/streamsql/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestStream_CompileFieldProcessInfo 测试字段处理信息编译
func TestStream_CompileFieldProcessInfo(t *testing.T) {
config := types.Config{
SimpleFields: []string{"name", "age:user_age", "`device_id`", "*"},
FieldExpressions: map[string]types.FieldExpression{
"full_name": {
Expression: "first_name + ' ' + last_name",
Fields: []string{"first_name", "last_name"},
},
},
}
stream, err := NewStream(config)
require.NoError(t, err)
defer func() {
if stream != nil {
close(stream.done)
}
}()
stream.compileFieldProcessInfo()
// 验证编译后的字段信息
assert.NotNil(t, stream.compiledFieldInfo)
assert.NotNil(t, stream.compiledExprInfo)
// 验证简单字段编译
assert.Contains(t, stream.compiledFieldInfo, "name")
assert.Contains(t, stream.compiledFieldInfo, "age:user_age")
assert.Contains(t, stream.compiledFieldInfo, "`device_id`")
assert.Contains(t, stream.compiledFieldInfo, "*")
}
// TestStream_CompileSimpleFieldInfo 测试简单字段信息编译
func TestStream_CompileSimpleFieldInfo(t *testing.T) {
config := types.Config{}
stream, err := NewStream(config)
require.NoError(t, err)
defer func() {
if stream != nil {
close(stream.done)
}
}()
tests := []struct {
name string
fieldSpec string
expectedFieldName string
expectedOutput string
expectedSelectAll bool
expectedNested bool
expectedFunction bool
expectedLiteral bool
expectedString string
}{
{
name: "Select all",
fieldSpec: "*",
expectedFieldName: "",
expectedOutput: "*",
expectedSelectAll: true,
expectedNested: false,
expectedFunction: false,
expectedLiteral: false,
},
{
name: "Simple field",
fieldSpec: "name",
expectedFieldName: "name",
expectedOutput: "name",
expectedSelectAll: false,
expectedNested: false,
expectedFunction: false,
expectedLiteral: false,
},
{
name: "Field with alias",
fieldSpec: "age:user_age",
expectedFieldName: "age",
expectedOutput: "user_age",
expectedSelectAll: false,
expectedNested: false,
expectedFunction: false,
expectedLiteral: false,
},
{
name: "Field with backticks",
fieldSpec: "`device_id`",
expectedFieldName: "device_id",
expectedOutput: "device_id",
expectedSelectAll: false,
expectedNested: false,
expectedFunction: false,
expectedLiteral: false,
},
{
name: "Field with backticks and alias",
fieldSpec: "`device_id`:`id`",
expectedFieldName: "device_id",
expectedOutput: "id",
expectedSelectAll: false,
expectedNested: false,
expectedFunction: false,
expectedLiteral: false,
},
{
name: "Nested field",
fieldSpec: "device.id",
expectedFieldName: "device.id",
expectedOutput: "device.id",
expectedSelectAll: false,
expectedNested: true,
expectedFunction: false,
expectedLiteral: false,
},
{
name: "Function call",
fieldSpec: "UPPER(name)",
expectedFieldName: "UPPER(name)",
expectedOutput: "UPPER(name)",
expectedSelectAll: false,
expectedNested: false,
expectedFunction: true,
expectedLiteral: false,
},
{
name: "String literal with single quotes",
fieldSpec: "'constant_value'",
expectedFieldName: "'constant_value'",
expectedOutput: "'constant_value'",
expectedSelectAll: false,
expectedNested: false,
expectedFunction: false,
expectedLiteral: true,
expectedString: "constant_value",
},
{
name: "String literal with double quotes",
fieldSpec: "\"test_string\"",
expectedFieldName: "\"test_string\"",
expectedOutput: "\"test_string\"",
expectedSelectAll: false,
expectedNested: false,
expectedFunction: false,
expectedLiteral: true,
expectedString: "test_string",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
info := stream.compileSimpleFieldInfo(tt.fieldSpec)
assert.NotNil(t, info)
assert.Equal(t, tt.expectedFieldName, info.fieldName)
assert.Equal(t, tt.expectedOutput, info.outputName)
assert.Equal(t, tt.expectedSelectAll, info.isSelectAll)
assert.Equal(t, tt.expectedNested, info.hasNestedField)
assert.Equal(t, tt.expectedFunction, info.isFunctionCall)
assert.Equal(t, tt.expectedLiteral, info.isStringLiteral)
if tt.expectedLiteral {
assert.Equal(t, tt.expectedString, info.stringValue)
}
assert.Equal(t, tt.expectedOutput, info.alias)
})
}
}
// TestStream_CompileExpressionInfo 测试表达式信息编译
func TestStream_CompileExpressionInfo(t *testing.T) {
config := types.Config{
SimpleFields: []string{"name", "age"},
FieldExpressions: map[string]types.FieldExpression{
"simple_expr": {
Expression: "value + 10",
Fields: []string{"value"},
},
"nested_expr": {
Expression: "device.temperature * 1.8 + 32",
Fields: []string{"device.temperature"},
},
"function_expr": {
Expression: "UPPER(name)",
Fields: []string{"name"},
},
"backtick_expr": {
Expression: "`field_name` + 5",
Fields: []string{"field_name"},
},
},
}
stream, err := NewStream(config)
require.NoError(t, err)
defer func() {
if stream != nil {
close(stream.done)
}
}()
stream.compileExpressionInfo()
// 验证表达式信息已编译
assert.NotNil(t, stream.compiledExprInfo)
assert.Len(t, stream.compiledExprInfo, 4)
// 验证每个表达式的编译信息
for exprName := range config.FieldExpressions {
assert.Contains(t, stream.compiledExprInfo, exprName)
info := stream.compiledExprInfo[exprName]
assert.NotNil(t, info)
assert.NotEmpty(t, info.originalExpr)
}
}
// TestFieldProcessInfo_EdgeCases 测试字段处理信息边界情况
func TestFieldProcessInfo_EdgeCases(t *testing.T) {
config := types.Config{
SimpleFields: []string{"name", "age"},
}
stream, err := NewStream(config)
require.NoError(t, err)
defer func() {
if stream != nil {
close(stream.done)
}
}()
tests := []struct {
name string
fieldSpec string
}{
{"Empty string", ""},
{"Only backticks", "``"},
{"Only quotes", "''"},
{"Only double quotes", "\"\""},
{"Malformed alias", "field::alias"},
{"Complex nested", "a.b.c.d.e"},
{"Function with nested", "FUNC(a.b.c)"},
{"Mixed quotes", "'test\""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 应该不会panic,即使输入不规范
assert.NotPanics(t, func() {
info := stream.compileSimpleFieldInfo(tt.fieldSpec)
assert.NotNil(t, info)
})
})
}
}
// TestExpressionProcessInfo_Structure 测试表达式处理信息结构
func TestExpressionProcessInfo_Structure(t *testing.T) {
// 测试expressionProcessInfo结构的基本功能
info := &expressionProcessInfo{
originalExpr: "value + 10",
processedExpr: "value + 10",
isFunctionCall: false,
hasNestedFields: false,
needsBacktickPreprocess: false,
}
assert.Equal(t, "value + 10", info.originalExpr)
assert.Equal(t, "value + 10", info.processedExpr)
assert.False(t, info.isFunctionCall)
assert.False(t, info.hasNestedFields)
assert.False(t, info.needsBacktickPreprocess)
assert.Nil(t, info.compiledExpr)
}
// TestFieldProcessInfo_Structure 测试字段处理信息结构
func TestFieldProcessInfo_Structure(t *testing.T) {
// 测试fieldProcessInfo结构的基本功能
info := &fieldProcessInfo{
fieldName: "test_field",
outputName: "output_field",
isFunctionCall: false,
hasNestedField: false,
isSelectAll: false,
isStringLiteral: true,
stringValue: "literal_value",
alias: "field_alias",
}
assert.Equal(t, "test_field", info.fieldName)
assert.Equal(t, "output_field", info.outputName)
assert.False(t, info.isFunctionCall)
assert.False(t, info.hasNestedField)
assert.False(t, info.isSelectAll)
assert.True(t, info.isStringLiteral)
assert.Equal(t, "literal_value", info.stringValue)
assert.Equal(t, "field_alias", info.alias)
}
// TestStream_CompileFieldProcessInfo_Performance 测试字段处理信息编译性能
func TestStream_CompileFieldProcessInfo_Performance(t *testing.T) {
// 创建大量字段的配置
fields := make([]string, 100)
expressions := make(map[string]types.FieldExpression)
for i := 0; i < 100; i++ {
fields[i] = fmt.Sprintf("field_%d", i)
expressions[fmt.Sprintf("expr_%d", i)] = types.FieldExpression{
Expression: fmt.Sprintf("field_%d + %d", i, i),
Fields: []string{fmt.Sprintf("field_%d", i)},
}
}
config := types.Config{
SimpleFields: fields,
FieldExpressions: expressions,
}
stream, err := NewStream(config)
require.NoError(t, err)
defer func() {
if stream != nil {
close(stream.done)
}
}()
// 编译应该快速完成,不会超时
assert.NotPanics(t, func() {
stream.compileFieldProcessInfo()
})
// 验证编译结果
assert.Len(t, stream.compiledFieldInfo, 100)
assert.Len(t, stream.compiledExprInfo, 100)
}