v0-json-schema

This commit is contained in:
gongwenxin
2025-05-16 15:18:02 +08:00
parent 32676a314f
commit 6d0b70fe11
260 changed files with 280278 additions and 0 deletions
@@ -0,0 +1,531 @@
"""规则执行引擎
该模块负责执行不同类型的规则,包括Python代码规则、断言规则等,
支持在API测试的不同生命周期阶段(请求准备、执行、响应验证等)执行规则。
"""
import logging
import importlib
import inspect
import time
import threading
from typing import Dict, List, Any, Optional, Union, Callable
from ..models.rule_models import (
AnyRule, BaseRule, RuleCategory, TargetType, RuleLifecycle, RuleScope,
PythonCodeRule, BusinessAssertionTemplate, PerformanceRule, SecurityRule,
RESTfulDesignRule, ErrorHandlingRule
)
from ..api_caller.caller import APIRequest, APIResponse
from ..rule_repository.repository import RuleRepository
class RuleExecutionError(Exception):
"""规则执行过程中发生的错误"""
pass
class RuleExecutionResult:
"""规则执行结果"""
def __init__(self,
rule: BaseRule,
is_valid: bool,
message: str = "",
details: Optional[Dict[str, Any]] = None,
error: Optional[Exception] = None):
"""
初始化规则执行结果
Args:
rule: 执行的规则
is_valid: 验证是否通过
message: 执行结果消息
details: 详细信息
error: 执行过程中发生的异常(如果有)
"""
self.rule = rule
self.rule_id = rule.id
self.rule_name = rule.name
self.rule_category = rule.category
self.is_valid = is_valid
self.message = message
self.details = details or {}
self.error = error
def __bool__(self):
"""允许直接使用结果对象作为布尔值,表示验证是否通过"""
return self.is_valid
def to_dict(self) -> Dict[str, Any]:
"""将结果转换为字典"""
return {
'rule_id': self.rule_id,
'rule_name': self.rule_name,
'rule_category': self.rule_category.value,
'is_valid': self.is_valid,
'message': self.message,
'details': self.details,
'error': str(self.error) if self.error else None
}
class RuleExecutor:
"""规则执行引擎"""
def __init__(self, rule_repository: RuleRepository):
"""
初始化规则执行引擎
Args:
rule_repository: 规则库实例
"""
self.rule_repository = rule_repository
self.logger = logging.getLogger(__name__)
def execute_rule(self, rule: BaseRule, context: Dict[str, Any]) -> RuleExecutionResult:
"""
执行单个规则
Args:
rule: 要执行的规则
context: 执行上下文,包含API请求、响应等信息
Returns:
执行结果
"""
if not rule.is_enabled:
return RuleExecutionResult(
rule=rule,
is_valid=True,
message=f"规则 {rule.id} 已禁用,跳过执行"
)
try:
# 根据规则类型选择适当的执行方法
if rule.category == RuleCategory.PYTHON_CODE or hasattr(rule, 'code') and rule.code:
return self._execute_python_code_rule(rule, context)
elif rule.category == RuleCategory.BUSINESS_LOGIC:
return self._execute_business_assertion_rule(rule, context)
elif rule.category == RuleCategory.PERFORMANCE:
return self._execute_performance_rule(rule, context)
elif rule.category == RuleCategory.SECURITY:
return self._execute_security_rule(rule, context)
elif rule.category == RuleCategory.API_DESIGN:
return self._execute_api_design_rule(rule, context)
elif rule.category == RuleCategory.ERROR_HANDLING:
return self._execute_error_handling_rule(rule, context)
else:
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"不支持的规则类型: {rule.category}"
)
except Exception as e:
self.logger.error(f"执行规则 {rule.id} 失败: {e}", exc_info=True)
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"规则执行失败: {e}",
error=e
)
def _execute_python_code_rule(self, rule: BaseRule, context: Dict[str, Any]) -> RuleExecutionResult:
"""执行Python代码规则"""
# 获取规则中的Python代码
code = getattr(rule, 'code', None)
if not code:
return RuleExecutionResult(
rule=rule,
is_valid=False,
message="规则未提供Python代码"
)
# 准备执行环境
namespace = {'__builtins__': __builtins__}
namespace.update(context)
try:
# 编译并执行代码
compiled_code = compile(code, f"<rule:{rule.id}>", 'exec')
exec(compiled_code, namespace)
# 查找并执行验证函数
entry_function = 'validate'
if hasattr(rule, 'entry_function') and rule.entry_function:
entry_function = rule.entry_function
if entry_function not in namespace:
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"找不到入口函数 '{entry_function}'"
)
validate_func = namespace[entry_function]
if not callable(validate_func):
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"'{entry_function}' 不是可调用的函数"
)
# 调用验证函数
timeout = getattr(rule, 'timeout', 5) # 默认5秒超时
result = self._execute_with_timeout(validate_func, (context,), {}, timeout)
# 处理执行结果
if isinstance(result, dict):
return RuleExecutionResult(
rule=rule,
is_valid=bool(result.get('is_valid', False)),
message=result.get('message', ''),
details=result.get('details', {})
)
else:
return RuleExecutionResult(
rule=rule,
is_valid=bool(result),
message=str(result) if result is not True else "验证通过"
)
except Exception as e:
self.logger.error(f"执行Python代码规则 {rule.id} 失败: {e}", exc_info=True)
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"执行代码失败: {e}",
error=e
)
def _execute_with_timeout(self, func: Callable, args: tuple, kwargs: Dict[str, Any], timeout: int) -> Any:
"""使用超时执行函数"""
result = [None]
exception = [None]
def target():
try:
result[0] = func(*args, **kwargs)
except Exception as e:
exception[0] = e
thread = threading.Thread(target=target)
thread.daemon = True
thread.start()
thread.join(timeout)
if thread.is_alive():
raise RuleExecutionError(f"规则执行超时(超过{timeout}秒)")
if exception[0]:
raise exception[0]
return result[0]
def _execute_business_assertion_rule(self, rule: BusinessAssertionTemplate, context: Dict[str, Any]) -> RuleExecutionResult:
"""执行业务断言规则"""
# 验证是否提供了所有必需的参数
if rule.expected_parameters:
missing_params = [p for p in rule.expected_parameters if p not in context]
if missing_params:
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"缺少必需的参数: {', '.join(missing_params)}"
)
if rule.template_language == "python_expression":
try:
# 使用eval执行Python表达式
result = eval(rule.template_expression, {'__builtins__': __builtins__}, context)
return RuleExecutionResult(
rule=rule,
is_valid=bool(result),
message="断言通过" if result else "断言失败"
)
except Exception as e:
self.logger.error(f"执行Python表达式断言失败: {e}", exc_info=True)
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"表达式执行失败: {e}",
error=e
)
else:
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"不支持的模板语言: {rule.template_language}"
)
def _execute_performance_rule(self, rule: PerformanceRule, context: Dict[str, Any]) -> RuleExecutionResult:
"""执行性能规则"""
response = context.get('api_response')
if not response or not isinstance(response, APIResponse):
return RuleExecutionResult(
rule=rule,
is_valid=False,
message="缺少有效的API响应对象"
)
# 获取响应时间(毫秒)
elapsed_time = response.elapsed_time * 1000 # 转换为毫秒
# 检查是否超过阈值
if elapsed_time > rule.threshold:
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"响应时间({elapsed_time:.2f}ms)超过阈值({rule.threshold}{rule.unit}",
details={
'actual_time': elapsed_time,
'threshold': rule.threshold,
'unit': rule.unit
}
)
return RuleExecutionResult(
rule=rule,
is_valid=True,
message=f"响应时间({elapsed_time:.2f}ms)在阈值范围内",
details={
'actual_time': elapsed_time,
'threshold': rule.threshold,
'unit': rule.unit
}
)
def _execute_security_rule(self, rule: SecurityRule, context: Dict[str, Any]) -> RuleExecutionResult:
"""执行安全规则"""
if rule.check_type == "transport_security":
request = context.get('api_request')
if not request or not isinstance(request, APIRequest):
return RuleExecutionResult(
rule=rule,
is_valid=False,
message="缺少有效的API请求对象"
)
url = str(request.url)
# 检查URL是否使用HTTPS
if not url.startswith('https://'):
return RuleExecutionResult(
rule=rule,
is_valid=False,
message="API请求必须使用HTTPS协议",
details={
'current_url': url,
'expected_protocol': 'https'
}
)
return RuleExecutionResult(
rule=rule,
is_valid=True,
message="API请求使用了HTTPS协议",
details={
'url': url
}
)
else:
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"不支持的安全检查类型: {rule.check_type}"
)
def _execute_api_design_rule(self, rule: RESTfulDesignRule, context: Dict[str, Any]) -> RuleExecutionResult:
"""执行API设计规则"""
import re
request = context.get('api_request')
if not request or not isinstance(request, APIRequest):
return RuleExecutionResult(
rule=rule,
is_valid=False,
message="缺少有效的API请求对象"
)
url = str(request.url)
# 解析URL,获取路径部分
from urllib.parse import urlparse
parsed_url = urlparse(url)
path = parsed_url.path
# 使用正则表达式验证路径
if rule.pattern and not re.match(rule.pattern, path):
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"API路径不符合{rule.design_aspect}规范",
details={
'current_path': path,
'expected_pattern': rule.pattern
}
)
return RuleExecutionResult(
rule=rule,
is_valid=True,
message=f"API路径符合{rule.design_aspect}规范",
details={
'path': path
}
)
def _execute_error_handling_rule(self, rule: ErrorHandlingRule, context: Dict[str, Any]) -> RuleExecutionResult:
"""执行错误处理规则"""
response = context.get('api_response')
if not response or not isinstance(response, APIResponse):
return RuleExecutionResult(
rule=rule,
is_valid=False,
message="缺少有效的API响应对象"
)
# 只验证4xx和5xx状态码
if response.status_code < 400:
return RuleExecutionResult(
rule=rule,
is_valid=True,
message="非错误响应,跳过验证",
details={
'status_code': response.status_code
}
)
# 验证状态码是否匹配
if rule.expected_status != -1 and response.status_code != rule.expected_status:
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"响应状态码({response.status_code})与预期({rule.expected_status})不符",
details={
'actual_status': response.status_code,
'expected_status': rule.expected_status
}
)
# 验证JSON响应
if not response.json_content:
return RuleExecutionResult(
rule=rule,
is_valid=False,
message="错误响应不是有效的JSON格式",
details={
'status_code': response.status_code,
'content_type': response.headers.get('Content-Type', '未知')
}
)
# 检查错误码
if rule.error_code != "*" and str(response.json_content.get('code', '')) != rule.error_code:
return RuleExecutionResult(
rule=rule,
is_valid=False,
message=f"错误码({response.json_content.get('code')})与预期({rule.error_code})不符",
details={
'actual_code': response.json_content.get('code'),
'expected_code': rule.error_code
}
)
# 验证错误消息
if rule.expected_message and rule.expected_message not in str(response.json_content.get('message', '')):
return RuleExecutionResult(
rule=rule,
is_valid=False,
message="错误消息与预期不符",
details={
'actual_message': response.json_content.get('message'),
'expected_message': rule.expected_message
}
)
return RuleExecutionResult(
rule=rule,
is_valid=True,
message="错误响应符合预期",
details={
'status_code': response.status_code,
'error_code': response.json_content.get('code'),
'error_message': response.json_content.get('message')
}
)
def execute_rules_for_lifecycle(self, lifecycle: RuleLifecycle, context: Dict[str, Any]) -> List[RuleExecutionResult]:
"""
执行特定生命周期阶段的所有规则
Args:
lifecycle: 生命周期阶段
context: 执行上下文
Returns:
执行结果列表
"""
# 获取适用于该生命周期阶段的所有规则
rules = self.rule_repository.get_rules_by_lifecycle(lifecycle)
# 执行规则
results = []
for rule in rules:
result = self.execute_rule(rule, context)
results.append(result)
return results
def execute_rules_for_target(self, target_type: TargetType, target_id: str, context: Dict[str, Any]) -> List[RuleExecutionResult]:
"""
执行特定目标的所有规则
Args:
target_type: 目标类型
target_id: 目标ID
context: 执行上下文
Returns:
执行结果列表
"""
# 获取适用于该目标的所有规则
rules = self.rule_repository.get_rules_for_target(target_type, target_id)
# 执行规则
results = []
for rule in rules:
result = self.execute_rule(rule, context)
results.append(result)
return results
def execute_specific_rules(self, rules: List[AnyRule], context: Dict[str, Any], lifecycle_phase: Optional[RuleLifecycle] = None) -> List[RuleExecutionResult]:
"""
执行一个明确指定的规则列表。
Args:
rules: 要执行的规则对象的列表。
context: 执行上下文,包含API请求、响应等信息。
lifecycle_phase: 可选的,名义上的生命周期阶段,可能用于上下文或某些规则的内部逻辑。
注意:此参数目前主要用于信息传递,核心执行逻辑在 execute_rule 中
并不直接依赖它来选择执行路径。
Returns:
一个包含每个规则执行结果的列表。
"""
results = []
if not rules:
self.logger.info("execute_specific_rules_called_with_no_rules")
return results
# 如果需要,可以将 lifecycle_phase 添加到 context 中传递给每个规则
# updated_context = context.copy()
# if lifecycle_phase:
# updated_context['current_lifecycle_phase'] = lifecycle_phase
for rule in rules:
# 使用现有的 execute_rule 方法执行单个规则
# result = self.execute_rule(rule, updated_context if lifecycle_phase else context)
result = self.execute_rule(rule, context) # 简化:暂时不修改context传递
results.append(result)
return results