1
0

feat: 增强模板条件渲染表达式支持

使用 simpleeval 库替换原有的简单正则匹配,支持复杂的条件表达式评估。新增 ConditionEvaluator 类处理条件逻辑,支持比较运算、逻辑运算、成员测试、数学计算和内置函数,同时保持向后兼容性。
This commit is contained in:
2026-03-03 17:28:23 +08:00
parent 01a93ce13b
commit 16ca9d77cd
16 changed files with 1437 additions and 31 deletions

View File

@@ -0,0 +1,299 @@
"""
条件评估器单元测试
测试 ConditionEvaluator 类的所有功能
"""
import pytest
from loaders.yaml_loader import YAMLError
from core.condition_evaluator import ConditionEvaluator
class TestConditionEvaluator:
"""ConditionEvaluator 测试类"""
def setup_method(self):
"""每个测试方法前执行"""
self.evaluator = ConditionEvaluator()
# ============= 比较运算符测试 =============
def test_greater_than(self):
"""测试大于运算符"""
assert self.evaluator.evaluate_condition("{count > 0}", {"count": 5}) is True
assert self.evaluator.evaluate_condition("{count > 0}", {"count": 0}) is False
def test_less_than(self):
"""测试小于运算符"""
assert self.evaluator.evaluate_condition("{count < 10}", {"count": 5}) is True
assert self.evaluator.evaluate_condition("{count < 10}", {"count": 10}) is False
def test_greater_equal(self):
"""测试大于等于运算符"""
assert self.evaluator.evaluate_condition("{count >= 5}", {"count": 5}) is True
assert self.evaluator.evaluate_condition("{count >= 5}", {"count": 4}) is False
def test_less_equal(self):
"""测试小于等于运算符"""
assert self.evaluator.evaluate_condition("{count <= 5}", {"count": 5}) is True
assert self.evaluator.evaluate_condition("{count <= 5}", {"count": 6}) is False
def test_equal(self):
"""测试相等运算符"""
assert self.evaluator.evaluate_condition("{status == 'active'}", {"status": "active"}) is True
assert self.evaluator.evaluate_condition("{status == 'active'}", {"status": "inactive"}) is False
def test_not_equal(self):
"""测试不等运算符"""
assert self.evaluator.evaluate_condition("{status != 'inactive'}", {"status": "active"}) is True
assert self.evaluator.evaluate_condition("{status != 'active'}", {"status": "active"}) is False
# ============= 逻辑运算符测试 =============
def test_logical_and(self):
"""测试逻辑与运算"""
assert self.evaluator.evaluate_condition(
"{count > 0 and status == 'active'}",
{"count": 5, "status": "active"}
) is True
assert self.evaluator.evaluate_condition(
"{count > 0 and status == 'active'}",
{"count": 0, "status": "active"}
) is False
def test_logical_or(self):
"""测试逻辑或运算"""
assert self.evaluator.evaluate_condition(
"{count > 0 or status == 'active'}",
{"count": 0, "status": "active"}
) is True
assert self.evaluator.evaluate_condition(
"{count > 0 or status == 'active'}",
{"count": 0, "status": "inactive"}
) is False
def test_logical_not(self):
"""测试逻辑非运算"""
assert self.evaluator.evaluate_condition("{not (count == 0)}", {"count": 5}) is True
assert self.evaluator.evaluate_condition("{not (count == 0)}", {"count": 0}) is False
# ============= 成员测试 =============
def test_in_list(self):
"""测试列表成员测试"""
assert self.evaluator.evaluate_condition(
"{status in ['draft', 'review', 'published']}",
{"status": "draft"}
) is True
assert self.evaluator.evaluate_condition(
"{status in ['draft', 'review']}",
{"status": "active"}
) is False
def test_not_in_list(self):
"""测试列表非成员测试"""
assert self.evaluator.evaluate_condition(
"{status not in ['draft', 'review']}",
{"status": "active"}
) is True
assert self.evaluator.evaluate_condition(
"{status not in ['draft', 'review']}",
{"status": "draft"}
) is False
def test_in_tuple(self):
"""测试元组成员测试"""
assert self.evaluator.evaluate_condition(
"{level in (1, 2, 3)}",
{"level": 2}
) is True
assert self.evaluator.evaluate_condition(
"{level in (1, 2, 3)}",
{"level": 4}
) is False
def test_string_contains(self):
"""测试字符串包含"""
assert self.evaluator.evaluate_condition(
"{'test' in status}",
{"status": "test-version"}
) is True
assert self.evaluator.evaluate_condition(
"{'test' in status}",
{"status": "production"}
) is False
# ============= 数学运算测试 =============
def test_addition(self):
"""测试加法运算"""
assert self.evaluator.evaluate_condition(
"{(price + tax) > 100}",
{"price": 90, "tax": 15}
) is True
def test_multiplication(self):
"""测试乘法运算"""
assert self.evaluator.evaluate_condition(
"{(price * discount) > 50}",
{"price": 100, "discount": 0.8}
) is True
def test_division(self):
"""测试除法运算"""
assert self.evaluator.evaluate_condition(
"{(total / count) >= 10}",
{"total": 100, "count": 5}
) is True
def test_modulo(self):
"""测试取模运算"""
assert self.evaluator.evaluate_condition(
"{(value % 2) == 0}",
{"value": 10}
) is True
def test_power(self):
"""测试幂运算"""
assert self.evaluator.evaluate_condition(
"{(base ** exp) > 100}",
{"base": 10, "exp": 2}
) is False
# ============= 内置函数测试 =============
def test_int_function(self):
"""测试 int() 函数"""
assert self.evaluator.evaluate_condition(
"{int(value) > 100}",
{"value": "150"}
) is True
def test_float_function(self):
"""测试 float() 函数"""
assert self.evaluator.evaluate_condition(
"{float(value) > 3.14}",
{"value": "3.5"}
) is True
def test_str_function(self):
"""测试 str() 函数"""
assert self.evaluator.evaluate_condition(
"{str(value) == '123'}",
{"value": 123}
) is True
def test_len_function(self):
"""测试 len() 函数"""
assert self.evaluator.evaluate_condition(
"{len(items) > 0}",
{"items": [1, 2, 3]}
) is True
def test_bool_function(self):
"""测试 bool() 函数"""
assert self.evaluator.evaluate_condition(
"{bool(value)}",
{"value": "text"}
) is True
assert self.evaluator.evaluate_condition(
"{bool(value)}",
{"value": ""}
) is False
def test_abs_function(self):
"""测试 abs() 函数"""
assert self.evaluator.evaluate_condition(
"{abs(value) > 10}",
{"value": -15}
) is True
def test_min_function(self):
"""测试 min() 函数"""
assert self.evaluator.evaluate_condition(
"{min(a, b) == 5}",
{"a": 5, "b": 10}
) is True
def test_max_function(self):
"""测试 max() 函数"""
assert self.evaluator.evaluate_condition(
"{max(a, b) == 10}",
{"a": 5, "b": 10}
) is True
# ============= 复杂逻辑组合测试 =============
def test_complex_logic_combination(self):
"""测试复杂逻辑组合"""
assert self.evaluator.evaluate_condition(
"{(score >= 60 and score <= 100) or is_admin}",
{"score": 75, "is_admin": False}
) is True
def test_nested_conditions(self):
"""测试嵌套条件"""
assert self.evaluator.evaluate_condition(
"{((count > 0) and (status == 'active')) or (is_admin and (level >= 3))}",
{"count": 5, "status": "active", "is_admin": False, "level": 1}
) is True
# ============= 错误处理测试 =============
def test_undefined_variable_error(self):
"""测试变量未定义错误"""
with pytest.raises(YAMLError, match="条件表达式中的变量未定义"):
self.evaluator.evaluate_condition("{undefined_var > 0}", {"count": 5})
def test_undefined_function_error(self):
"""测试函数未定义错误"""
with pytest.raises(YAMLError, match="条件表达式中使用了不支持的函数"):
self.evaluator.evaluate_condition("{custom_func(value)}", {"value": 5})
def test_syntax_error(self):
"""测试语法错误"""
with pytest.raises(YAMLError, match="条件表达式语法错误"):
self.evaluator.evaluate_condition("{count > }", {"count": 5})
# ============= 安全限制测试 =============
def test_expression_too_long(self):
"""测试表达式过长"""
long_expr = "{" + "x " * 300 + "}"
with pytest.raises(YAMLError, match="条件表达式过长"):
self.evaluator.evaluate_condition(long_expr, {"x": 1})
def test_attribute_access_forbidden(self):
"""测试禁止属性访问"""
with pytest.raises(YAMLError, match="条件表达式使用了不支持的语法特性"):
self.evaluator.evaluate_condition("{obj.attr}", {"obj": object()})
# ============= 表达式提取测试 =============
def test_extract_expression_with_braces(self):
"""测试提取带花括号的表达式"""
expr = self.evaluator._extract_expression("{count > 0}")
assert expr == "count > 0"
def test_extract_expression_with_whitespace(self):
"""测试提取带空白的表达式"""
expr = self.evaluator._extract_expression("{ count > 0 }")
assert expr == "count > 0"
def test_extract_expression_without_braces(self):
"""测试提取不带花括号的表达式"""
expr = self.evaluator._extract_expression("count > 0")
assert expr == "count > 0"
# ============= 向后兼容测试 =============
def test_backward_compatible_simple_expression(self):
"""测试向后兼容的简单表达式"""
assert self.evaluator.evaluate_condition(
"{subtitle != ''}",
{"subtitle": "Hello"}
) is True
assert self.evaluator.evaluate_condition(
"{subtitle != ''}",
{"subtitle": ""}
) is False