Files
streamsql/expr/validator_test.go
2025-08-07 19:18:40 +08:00

886 lines
25 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.
package expr
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
// TestValidateExpressionNode 测试表达式节点验证功能
func TestValidateExpressionNode(t *testing.T) {
tests := []struct {
name string
node *ExprNode
wantErr bool
}{
{
"有效数字节点",
&ExprNode{Type: TypeNumber, Value: "123"},
false,
},
{
"有效字段节点",
&ExprNode{Type: TypeField, Value: "field1"},
false,
},
{
"有效字符串节点",
&ExprNode{Type: TypeString, Value: "'hello'"},
false,
},
{
"有效运算符节点",
&ExprNode{
Type: TypeOperator,
Value: "+",
Left: &ExprNode{Type: TypeNumber, Value: "1"},
Right: &ExprNode{Type: TypeNumber, Value: "2"},
},
false,
},
{
"有效函数节点",
&ExprNode{
Type: TypeFunction,
Value: "abs",
Args: []*ExprNode{{Type: TypeNumber, Value: "1"}},
},
false,
},
{
"有效CASE节点",
&ExprNode{
Type: TypeCase,
CaseExpr: &CaseExpression{
WhenClauses: []WhenClause{
{
Condition: &ExprNode{
Type: TypeOperator,
Value: ">",
Left: &ExprNode{Type: TypeField, Value: "a"},
Right: &ExprNode{Type: TypeNumber, Value: "0"},
},
Result: &ExprNode{Type: TypeNumber, Value: "1"},
},
},
ElseResult: &ExprNode{Type: TypeNumber, Value: "0"},
},
},
false,
},
// 错误情况
{"空节点", nil, true},
{"无效数字", &ExprNode{Type: TypeNumber, Value: "abc"}, true},
{"无效字段名", &ExprNode{Type: TypeField, Value: "123field"}, true},
{"无效字符串", &ExprNode{Type: TypeString, Value: "hello"}, true},
{"运算符缺少左操作数", &ExprNode{
Type: TypeOperator,
Value: "+",
Right: &ExprNode{Type: TypeNumber, Value: "1"},
}, true},
{"运算符缺少右操作数", &ExprNode{
Type: TypeOperator,
Value: "+",
Left: &ExprNode{Type: TypeNumber, Value: "1"},
}, true},
{"无效运算符", &ExprNode{
Type: TypeOperator,
Value: "@",
Left: &ExprNode{Type: TypeNumber, Value: "1"},
Right: &ExprNode{Type: TypeNumber, Value: "2"},
}, true},
{"无效函数", &ExprNode{
Type: TypeFunction,
Value: "unknown",
Args: []*ExprNode{{Type: TypeNumber, Value: "1"}},
}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateExpression(tt.node)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "验证不应该失败")
}
})
}
}
// TestValidateExpression 测试公共表达式验证接口
func TestValidateExpression(t *testing.T) {
tests := []struct {
name string
expr string
wantErr bool
}{
// 有效表达式
{"简单数字", "123", false},
{"简单字段", "field1", false},
{"算术表达式", "1 + 2", false},
{"比较表达式", "field1 > 10", false},
{"函数调用", "abs(-5)", false},
{"复杂表达式", "(field1 + field2) * 2", false},
{"字符串比较", "name = 'test'", false},
{"CASE表达式", "CASE WHEN field1 > 0 THEN 1 ELSE 0 END", false},
{"逻辑表达式", "field1 > 0 AND field2 < 100", false},
{"嵌套函数", "max(abs(field1), abs(field2))", false},
// 无效表达式
{"空表达式", "", true},
{"只有空格", " ", true},
{"括号不匹配1", "(1 + 2", true},
{"括号不匹配2", "1 + 2)", true},
{"连续运算符", "1 + + 2", true},
{"运算符开头", "+ 1 + 2", true},
{"运算符结尾", "1 + 2 +", true},
{"空括号", "()", true},
{"无效数字", "12.34.56", true},
{"无效字符串", "'unclosed string", true},
{"无效函数", "unknown_func(1)", true},
{"无效字段名", "123field", true},
{"tokenize错误", "field with invalid 'quote", true},
{"解析错误", "(((", true},
{"AST验证错误", "123abc", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateExpression(tt.expr)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "验证不应该失败")
}
})
}
}
// TestValidateNumberNode 测试数字节点验证
func TestValidateNumberNode(t *testing.T) {
tests := []struct {
name string
value string
wantErr bool
}{
{"正整数", "123", false},
{"负整数", "-123", false},
{"零", "0", false},
{"正小数", "3.14", false},
{"负小数", "-3.14", false},
{"科学计数法", "1.5e10", false},
{"负科学计数法", "-1.5e-3", false},
{"小数点开头", ".5", false},
{"小数点结尾", "5.", false},
// 错误情况
{"空字符串", "", true},
{"字母", "abc", true},
{"多个小数点", "3.14.15", true},
{"多个负号", "--5", true},
{"负号在中间", "3-5", true},
{"只有小数点", ".", true},
{"只有负号", "-", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
node := &ExprNode{Type: TypeNumber, Value: tt.value}
err := validateNumberNode(node)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "数字验证不应该失败")
}
})
}
}
// TestValidateStringNode 测试字符串节点验证
func TestValidateStringNode(t *testing.T) {
tests := []struct {
name string
value string
wantErr bool
}{
{"单引号字符串", "'hello'", false},
{"双引号字符串", "\"world\"", false},
{"空字符串", "''", false},
{"空双引号字符串", "\"\"", false},
{"包含转义的字符串", "'hello\\world'", false},
{"包含单引号的双引号字符串", "\"hello'world\"", false},
{"包含双引号的单引号字符串", "'hello\"world'", false},
// 错误情况
{"未闭合单引号", "'hello", true},
{"未闭合双引号", "\"hello", true},
{"没有引号", "hello", true},
{"空值", "", true},
{"只有单引号", "'", true},
{"只有双引号", "\"", true},
{"引号不匹配", "'hello\"", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
node := &ExprNode{Type: TypeString, Value: tt.value}
err := validateStringNode(node)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "字符串验证不应该失败")
}
})
}
}
// TestValidateFieldNode 测试字段节点验证
func TestValidateFieldNode(t *testing.T) {
tests := []struct {
name string
value string
wantErr bool
}{
{"简单字段名", "field1", false},
{"下划线开头", "_field", false},
{"包含数字", "field123", false},
{"驼峰命名", "fieldName", false},
{"蛇形命名", "field_name", false},
{"大写字段", "FIELD", false},
{"反引号字段", "`field name`", false},
{"反引号包含特殊字符", "`user.name`", false},
{"反引号包含空格", "`user name`", false},
// 错误情况
{"空字段名", "", true},
{"数字开头", "123field", true},
{"包含特殊字符", "field-name", true},
{"包含空格", "field name", true},
{"包含点号", "field.name", true},
{"未闭合反引号", "`field", true},
{"只有反引号", "`", true},
{"空反引号", "``", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
node := &ExprNode{Type: TypeField, Value: tt.value}
err := validateFieldNode(node)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "字段验证不应该失败")
}
})
}
}
// TestValidateOperatorNode 测试运算符节点验证
func TestValidateOperatorNode(t *testing.T) {
tests := []struct {
name string
operator string
left *ExprNode
right *ExprNode
wantErr bool
}{
{
"有效加法",
"+",
&ExprNode{Type: TypeNumber, Value: "1"},
&ExprNode{Type: TypeNumber, Value: "2"},
false,
},
{
"有效比较",
">",
&ExprNode{Type: TypeField, Value: "a"},
&ExprNode{Type: TypeNumber, Value: "0"},
false,
},
{
"有效逻辑运算",
"AND",
&ExprNode{
Type: TypeOperator,
Value: ">",
Left: &ExprNode{Type: TypeField, Value: "a"},
Right: &ExprNode{Type: TypeNumber, Value: "0"},
},
&ExprNode{
Type: TypeOperator,
Value: "<",
Left: &ExprNode{Type: TypeField, Value: "b"},
Right: &ExprNode{Type: TypeNumber, Value: "10"},
},
false,
},
{
"有效NOT运算单操作数",
"NOT",
&ExprNode{
Type: TypeOperator,
Value: ">",
Left: &ExprNode{Type: TypeField, Value: "a"},
Right: &ExprNode{Type: TypeNumber, Value: "0"},
},
nil,
false,
},
// 错误情况
{"无效运算符", "@", &ExprNode{Type: TypeNumber, Value: "1"}, &ExprNode{Type: TypeNumber, Value: "2"}, true},
{"缺少左操作数", "+", nil, &ExprNode{Type: TypeNumber, Value: "2"}, true},
{"缺少右操作数(双操作数运算符)", "+", &ExprNode{Type: TypeNumber, Value: "1"}, nil, true},
{"NOT运算符有右操作数", "NOT", &ExprNode{Type: TypeNumber, Value: "1"}, &ExprNode{Type: TypeNumber, Value: "2"}, true},
{"左操作数验证失败", "+", &ExprNode{Type: TypeNumber, Value: "abc"}, &ExprNode{Type: TypeNumber, Value: "2"}, true},
{"右操作数验证失败", "+", &ExprNode{Type: TypeNumber, Value: "1"}, &ExprNode{Type: TypeNumber, Value: "abc"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
node := &ExprNode{
Type: TypeOperator,
Value: tt.operator,
Left: tt.left,
Right: tt.right,
}
err := validateOperatorNode(node)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "运算符验证不应该失败")
}
})
}
}
// TestValidateFunctionNode 测试函数节点验证
func TestValidateFunctionNode(t *testing.T) {
// 测试函数Value为空的情况
t.Run("函数名为空", func(t *testing.T) {
node := &ExprNode{
Type: TypeFunction,
Value: "",
Args: []*ExprNode{{Type: TypeNumber, Value: "1"}},
}
err := validateFunctionNode(node)
assert.Error(t, err, "函数名为空时应该返回错误")
assert.Contains(t, err.Error(), "function node has empty value")
})
tests := []struct {
name string
funcName string
args []*ExprNode
wantErr bool
}{
{
"ABS函数",
"abs",
[]*ExprNode{{Type: TypeNumber, Value: "1"}},
false,
},
{
"MAX函数",
"max",
[]*ExprNode{
{Type: TypeNumber, Value: "1"},
{Type: TypeNumber, Value: "2"},
{Type: TypeNumber, Value: "3"},
},
false,
},
{
"POW函数",
"pow",
[]*ExprNode{
{Type: TypeNumber, Value: "2"},
{Type: TypeNumber, Value: "3"},
},
false,
},
{
"COUNT函数无参数",
"count",
[]*ExprNode{},
false,
},
// 错误情况
{"未知函数", "unknown", []*ExprNode{{Type: TypeNumber, Value: "1"}}, true},
{"ABS参数数量错误", "abs", []*ExprNode{}, true},
{"POW参数数量错误", "pow", []*ExprNode{{Type: TypeNumber, Value: "2"}}, true},
{"参数验证失败", "abs", []*ExprNode{{Type: TypeNumber, Value: "abc"}}, true},
{"参数表达式验证失败", "abs", []*ExprNode{{Type: TypeField, Value: "invalid field name!"}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
node := &ExprNode{
Type: TypeFunction,
Value: tt.funcName,
Args: tt.args,
}
err := validateFunctionNode(node)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "函数验证不应该失败")
}
})
}
}
// TestValidateFunctionArgs 测试函数参数验证
func TestValidateFunctionArgs(t *testing.T) {
tests := []struct {
name string
funcName string
args []*ExprNode
wantErr bool
}{
// 单参数函数
{"ABS正确参数", "abs", []*ExprNode{{Type: TypeNumber, Value: "1"}}, false},
{"ABS参数过少", "abs", []*ExprNode{}, true},
{"ABS参数过多", "abs", []*ExprNode{{Type: TypeNumber, Value: "1"}, {Type: TypeNumber, Value: "2"}}, true},
// 双参数函数
{"POW正确参数", "pow", []*ExprNode{{Type: TypeNumber, Value: "2"}, {Type: TypeNumber, Value: "3"}}, false},
{"POW参数过少", "pow", []*ExprNode{{Type: TypeNumber, Value: "2"}}, true},
{"POW参数过多", "pow", []*ExprNode{{Type: TypeNumber, Value: "2"}, {Type: TypeNumber, Value: "3"}, {Type: TypeNumber, Value: "4"}}, true},
// 可变参数函数
{"MAX单参数", "max", []*ExprNode{{Type: TypeNumber, Value: "1"}}, false},
{"MAX多参数", "max", []*ExprNode{{Type: TypeNumber, Value: "1"}, {Type: TypeNumber, Value: "2"}, {Type: TypeNumber, Value: "3"}}, false},
{"MAX无参数", "max", []*ExprNode{}, true},
// 无参数函数(如果有的话)
{"COUNT无参数", "count", []*ExprNode{}, false},
{"COUNT有参数", "count", []*ExprNode{{Type: TypeNumber, Value: "1"}}, false}, // COUNT可以有参数
// 未知函数
{"未知函数", "unknown", []*ExprNode{{Type: TypeNumber, Value: "1"}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 创建一个函数节点来测试
node := &ExprNode{
Type: TypeFunction,
Value: tt.funcName,
Args: tt.args,
}
// 使用validateFunctionNode来验证函数名和参数数量
err := validateFunctionNode(node)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "函数参数验证不应该失败")
}
})
}
}
// TestValidateCaseNode 测试CASE节点验证
func TestValidateCaseNode(t *testing.T) {
tests := []struct {
name string
caseExpr *CaseExpression
wantErr bool
}{
{
"有效CASE表达式",
&CaseExpression{
WhenClauses: []WhenClause{
{
Condition: &ExprNode{
Type: TypeOperator,
Value: ">",
Left: &ExprNode{Type: TypeField, Value: "a"},
Right: &ExprNode{Type: TypeNumber, Value: "0"},
},
Result: &ExprNode{Type: TypeNumber, Value: "1"},
},
},
ElseResult: &ExprNode{Type: TypeNumber, Value: "0"},
},
false,
},
{
"简单CASE表达式带Value",
&CaseExpression{
Value: &ExprNode{Type: TypeField, Value: "status"},
WhenClauses: []WhenClause{
{
Condition: &ExprNode{Type: TypeString, Value: "'active'"},
Result: &ExprNode{Type: TypeNumber, Value: "1"},
},
{
Condition: &ExprNode{Type: TypeString, Value: "'inactive'"},
Result: &ExprNode{Type: TypeNumber, Value: "0"},
},
},
ElseResult: &ExprNode{Type: TypeNumber, Value: "-1"},
},
false,
},
{
"多个WHEN子句",
&CaseExpression{
WhenClauses: []WhenClause{
{
Condition: &ExprNode{
Type: TypeOperator,
Value: ">",
Left: &ExprNode{Type: TypeField, Value: "a"},
Right: &ExprNode{Type: TypeNumber, Value: "0"},
},
Result: &ExprNode{Type: TypeNumber, Value: "1"},
},
{
Condition: &ExprNode{
Type: TypeOperator,
Value: "<",
Left: &ExprNode{Type: TypeField, Value: "a"},
Right: &ExprNode{Type: TypeNumber, Value: "0"},
},
Result: &ExprNode{Type: TypeNumber, Value: "-1"},
},
},
ElseResult: &ExprNode{Type: TypeNumber, Value: "0"},
},
false,
},
{
"没有ELSE子句",
&CaseExpression{
WhenClauses: []WhenClause{
{
Condition: &ExprNode{
Type: TypeOperator,
Value: ">",
Left: &ExprNode{Type: TypeField, Value: "a"},
Right: &ExprNode{Type: TypeNumber, Value: "0"},
},
Result: &ExprNode{Type: TypeNumber, Value: "1"},
},
},
ElseResult: nil,
},
false,
},
// 错误情况
{"没有WHEN子句", &CaseExpression{WhenClauses: []WhenClause{}, ElseResult: &ExprNode{Type: TypeNumber, Value: "0"}}, true},
{"WHEN条件为空", &CaseExpression{
WhenClauses: []WhenClause{
{Condition: nil, Result: &ExprNode{Type: TypeNumber, Value: "1"}},
},
}, true},
{"WHEN结果为空", &CaseExpression{
WhenClauses: []WhenClause{
{Condition: &ExprNode{Type: TypeField, Value: "a"}, Result: nil},
},
}, true},
{"WHEN条件验证失败", &CaseExpression{
WhenClauses: []WhenClause{
{Condition: &ExprNode{Type: TypeNumber, Value: "abc"}, Result: &ExprNode{Type: TypeNumber, Value: "1"}},
},
}, true},
{"WHEN结果验证失败", &CaseExpression{
WhenClauses: []WhenClause{
{Condition: &ExprNode{Type: TypeField, Value: "a"}, Result: &ExprNode{Type: TypeNumber, Value: "abc"}},
},
}, true},
{"ELSE结果验证失败", &CaseExpression{
WhenClauses: []WhenClause{
{Condition: &ExprNode{Type: TypeField, Value: "a"}, Result: &ExprNode{Type: TypeNumber, Value: "1"}},
},
ElseResult: &ExprNode{Type: TypeNumber, Value: "abc"},
}, true},
{"简单CASE的Value验证失败", &CaseExpression{
Value: &ExprNode{Type: TypeNumber, Value: "invalid_number"},
WhenClauses: []WhenClause{
{Condition: &ExprNode{Type: TypeString, Value: "'test'"}, Result: &ExprNode{Type: TypeNumber, Value: "1"}},
},
}, true},
}
// 添加CaseExpr为nil的测试用例
t.Run("CaseExpr为nil", func(t *testing.T) {
node := &ExprNode{Type: TypeCase, CaseExpr: nil}
err := validateCaseNode(node)
assert.Error(t, err, "CaseExpr为nil时应该返回错误")
assert.Contains(t, err.Error(), "CASE expression is missing")
})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
node := &ExprNode{Type: TypeCase, CaseExpr: tt.caseExpr}
err := validateCaseNode(node)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "CASE节点验证不应该失败")
}
})
}
}
// TestIsValidFieldName 测试字段名验证
func TestIsValidFieldName(t *testing.T) {
tests := []struct {
name string
fieldName string
expected bool
}{
// 有效情况
{"简单字段名", "field", true},
{"下划线开头", "_field", true},
{"包含数字", "field123", true},
{"驼峰命名", "fieldName", true},
{"蛇形命名", "field_name", true},
{"大写字段", "FIELD", true},
{"单字符字段", "a", true},
{"单下划线", "_", true},
{"反引号字段", "`field name`", true},
{"反引号包含特殊字符", "`user.name`", true},
{"反引号包含数字开头", "`123field`", true},
{"反引号包含连字符", "`field-name`", true},
{"反引号包含各种符号", "`field@#$%^&*()`", true},
// 无效情况
{"空字段名", "", false},
{"数字开头", "123field", false},
{"包含连字符", "field-name", false},
{"包含空格(无反引号)", "field name", false},
{"包含点号(无反引号)", "field.name", false},
{"包含特殊字符@", "field@name", false},
{"包含特殊字符#", "field#name", false},
{"包含特殊字符$", "field$name", false},
{"包含特殊字符%", "field%name", false},
{"未闭合反引号", "`field", false},
{"空反引号", "``", false},
{"反引号内包含反引号", "`field`name`", false},
{"只有反引号开头", "`", false},
{"非ASCII字符", "字段名", false},
{"包含非ASCII字符", "field字段", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := isValidFieldName(tt.fieldName)
assert.Equal(t, tt.expected, result, "字段名验证结果应该正确")
})
}
}
// TestValidateTokens 测试标记列表验证
func TestValidateTokens(t *testing.T) {
tests := []struct {
name string
tokens []string
wantErr bool
}{
{"有效标记列表", []string{"a", "+", "b"}, false},
{"有效函数调用", []string{"abs", "(", "x", ")"}, false},
{"有效CASE表达式", []string{"CASE", "WHEN", "a", ">", "0", "THEN", "1", "ELSE", "0", "END"}, false},
// 错误情况
{"空标记列表", []string{}, true},
{"括号不匹配", []string{"(", "a", "+", "b"}, true},
{"连续运算符", []string{"a", "+", "+", "b"}, true},
{"运算符开头", []string{"+", "a"}, true},
{"运算符结尾", []string{"a", "+"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateTokens(tt.tokens)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "标记验证不应该失败")
}
})
}
}
// TestValidateParentheses 测试括号验证
func TestValidateParentheses(t *testing.T) {
tests := []struct {
name string
tokens []string
wantErr bool
}{
{"匹配的括号", []string{"(", "a", "+", "b", ")"}, false},
{"嵌套括号", []string{"(", "(", "a", "+", "b", ")", "*", "c", ")"}, false},
{"函数括号", []string{"abs", "(", "x", ")"}, false},
{"无括号", []string{"a", "+", "b"}, false},
// 错误情况
{"缺少右括号", []string{"(", "a", "+", "b"}, true},
{"缺少左括号", []string{"a", "+", "b", ")"}, true},
{"括号顺序错误", []string{")", "a", "+", "b", "("}, true},
{"嵌套不匹配", []string{"(", "(", "a", "+", "b", ")"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateParentheses(tt.tokens)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "括号验证不应该失败")
}
})
}
}
// TestValidateTokenOrder 测试标记顺序验证
func TestValidateTokenOrder(t *testing.T) {
tests := []struct {
name string
tokens []string
wantErr bool
}{
{"正确顺序", []string{"a", "+", "b"}, false},
{"函数调用", []string{"abs", "(", "x", ")"}, false},
{"复杂表达式", []string{"a", "+", "b", "*", "c"}, false},
// 错误情况
{"连续运算符", []string{"a", "+", "+", "b"}, true},
{"运算符开头", []string{"+", "a"}, true},
{"运算符结尾", []string{"a", "+"}, true},
{"连续操作数", []string{"a", "b", "+", "c"}, true},
{"CASE关键字组合", []string{"CASE", "WHEN", "field", "THEN", "value", "END"}, false},
{"操作符后跟一元操作符", []string{"a", "AND", "NOT", "b"}, false},
{"连续二元操作符", []string{"a", "+", "*", "b"}, true},
{"以一元操作符开始", []string{"NOT", "a"}, false},
{"以二元操作符开始", []string{"-", "a"}, true},
{"逗号分隔的参数", []string{"func", "(", "a", ",", "b", ")"}, false},
{"括号和操作数混合", []string{"(", "a", ")", "+", "b"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateTokenOrder(tt.tokens)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "标记顺序验证不应该失败")
}
})
}
}
// TestValidateSyntax 测试语法验证
func TestValidateParenthesisNode(t *testing.T) {
tests := []struct {
name string
node *ExprNode
wantErr bool
errMsg string
}{
{
name: "有效的括号表达式",
node: &ExprNode{
Type: TypeParenthesis,
Left: &ExprNode{
Type: TypeField,
Value: "field1",
},
},
wantErr: false,
},
{
name: "括号内为空",
node: &ExprNode{
Type: TypeParenthesis,
Left: nil,
},
wantErr: true,
errMsg: "parenthesis node missing inner expression",
},
{
name: "括号内表达式无效",
node: &ExprNode{
Type: TypeParenthesis,
Left: &ExprNode{
Type: TypeField,
Value: "", // 空字段名
},
},
wantErr: true,
errMsg: "field node has empty value",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateParenthesisNode(tt.node)
if tt.wantErr {
if err == nil {
t.Errorf("validateParenthesisNode() expected error, got nil")
} else if !strings.Contains(err.Error(), tt.errMsg) {
t.Errorf("validateParenthesisNode() error = %v, want error containing %v", err, tt.errMsg)
}
} else {
if err != nil {
t.Errorf("validateParenthesisNode() error = %v, want nil", err)
}
}
})
}
}
func TestValidateSyntax(t *testing.T) {
tests := []struct {
name string
expr string
wantErr bool
}{
// 有效表达式
{"有效算术表达式", "a + b", false},
{"有效函数调用", "abs(x)", false},
{"有效CASE表达式", "CASE WHEN a > 0 THEN 1 END", false},
{"有效比较表达式", "field >= 10", false},
{"有效逻辑表达式", "a != b", false},
{"有效不等于表达式", "a <> b", false},
{"有效小于等于表达式", "a <= b", false},
{"复杂表达式", "a + b * c", false},
// 错误情况
{"空表达式", "", true},
{"只有空格", " ", true},
{"空括号", "()", true},
{"括号不匹配1", "(a + b", true},
{"括号不匹配2", "a + b)", true},
{"连续运算符(空格分隔)", "a + + b", true},
{"连续运算符(直接相邻)", "a+-b", true},
{"运算符开头", "+ a + b", true},
{"运算符结尾", "a + b +", true},
{"乘法运算符开头", "* a + b", true},
{"除法运算符结尾", "a + b /", true},
{"模运算符开头", "% a + b", true},
{"幂运算符结尾", "a + b ^", true},
{"等号运算符开头", "= a + b", true},
{"不等号运算符结尾", "a + b !=", true},
{"大于号运算符开头", "> a + b", true},
{"小于号运算符结尾", "a + b <", true},
{"大于等于运算符开头", ">= a + b", true},
{"小于等于运算符结尾", "a + b <=", true},
{"不等于运算符开头", "<> a + b", true},
{"多个连续运算符组合1", "a + * b", true},
{"多个连续运算符组合2", "a / % b", true},
{"多个连续运算符组合3", "a ^ + b", true},
{"多个连续运算符组合4", "a = > b", true},
{"多个连续运算符组合5", "a < = b", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateSyntax(tt.expr)
if tt.wantErr {
assert.Error(t, err, "应该返回错误")
} else {
assert.NoError(t, err, "语法验证不应该失败")
}
})
}
}