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

357 lines
9.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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)
}