refactor: 重构执行引擎为上下文驱动架构

- 优化 WorkflowExecutor 与 Exchange支持 ExecutionEnv 资源注入。
 - 实现 Session 级别连接复用与变量池内存镜像化,消除重复 I/O 开销。
 - 引入 ChainMap 实现动态上下文切换,解决参数化变量与全局提取变量的优先级覆盖。
 - 完善变量提取与断言逻辑,确保跨用例变量流转的可靠性。
This commit is contained in:
2026-03-14 11:45:52 +08:00
parent 2116016a0d
commit 00791809df
9 changed files with 276 additions and 289 deletions

View File

@@ -12,8 +12,7 @@ from typing import Any, Union, TypeVar
import jsonpath
from lxml import etree
from core.models import CaseInfo
from core.models import RawSchema
from core.settings import EXTRACT_CACHE
from core.templates import Template
from commons.file_processors.yaml_processor import YamlProcessor
@@ -25,16 +24,20 @@ T = TypeVar("T", bound=Union[dict, list, str, Any])
class Exchange:
def __init__(self, cache_path: str):
self.cache_path = cache_path
self.file_handler = YamlProcessor(filepath=self.cache_path)
# 1. 增加内存缓存,避免频繁磁盘 I/O
self._variable_cache = self.file_handler.load() or {}
def __init__(self, variable_cache: dict[str, Any]):
self._cache = variable_cache
# 匹配标准变量 ${var},排除函数调用 ${func()}
# self.var_only_pattern = re.compile(r"\$\{([a-zA-Z_]\w*)}")
self.var_only_pattern = re.compile(r"^\$\{([a-zA-Z_]\w*)}$")
def extract(self, resp, var_name: str, attr: str, expr: str, index: int = 0):
@property
def global_vars(self) -> dict:
return self._cache
@global_vars.setter
def global_vars(self, global_vars: dict) -> None:
self._cache = global_vars
def extract(self, resp: Any, var_name: str, attr: str, expr: str, index: int = 0):
"""
从响应中提取数据并更新到缓存及文件
:param resp: Response 对象
@@ -63,8 +66,12 @@ class Exchange:
res = jsonpath.jsonpath(target_data, expr)
if res: value = res[index]
elif expr.startswith("/") or expr.startswith("./"): # XPath 模式
html_content = getattr(resp, "text", "") # 使用 getattr 防护
if not html_content:
logger.warning("XPath 提取失败:响应文本为空")
return
# 将文本解析为 HTML 树
html_content = resp.text
# html_content = resp.text
tree = etree.HTML(html_content)
res = tree.xpath(expr)
if res:
@@ -79,8 +86,7 @@ class Exchange:
logger.warning(f"变量 [{var_name}] 未通过表达式 [{expr}] 提取到数据")
value = "not data"
self._variable_cache[var_name] = value
self.file_handler.save(self._variable_cache)
self._cache[var_name] = value
logger.info(f"变量提取成功: {var_name} -> {value} (Type: {type(value).__name__})")
except Exception as e:
@@ -103,13 +109,13 @@ class Exchange:
if full_match:
var_name = full_match.group(1)
return self._variable_cache.get(var_name, content)
return self._cache.get(var_name, content)
# B. 场景:混合文本或函数调用
# 例子:"Bearer ${token}" 或 "${gen_phone()}"
if "${" in content:
# 调用你提供的 Template 类
return Template(content).render(self._variable_cache)
return Template(content).render(self._cache)
return content
@@ -129,13 +135,12 @@ class Exchange:
if __name__ == "__main__":
from core.models import CaseInfo, RequestModel
from core.models import RawSchema, HttpAction
# 模拟外部写入一个初始变量
with open(EXTRACT_CACHE, "w") as f:
f.write("existing_var: '100'\n")
file_handler = YamlProcessor(filepath=EXTRACT_CACHE)
variable_cache_ = file_handler.load() or {}
ex = Exchange(EXTRACT_CACHE)
ex = Exchange(variable_cache_)
# --- 场景 1: 变量提取验证 ---
@@ -157,7 +162,7 @@ if __name__ == "__main__":
# 定义一个复杂的 CaseInfo
raw_case = {
"title": "测试用例",
"request": {
"action": {
"method": "POST",
"url": "http://api.com/${token}", # 混合文本 -> 应转为 str
"json_body": {
@@ -170,20 +175,21 @@ if __name__ == "__main__":
}
print("\n>>> 执行替换...")
new_case = ex.replace(raw_case)
print(new_case)
new_case = CaseInfo(**new_case)
# --- 校验结果 ---
new_case_one = ex.replace(raw_case)
print(new_case_one)
RawSchema(**new_case_one)
print(new_case_one.get("action"))
action = HttpAction(**new_case_one.get("action"))
print(action)
# # --- 校验结果 ---
print("\n--- 验证结果 ---")
print(f"URL (混合文本): {new_case.request.url} | 类型: {type(new_case.request.url)}")
print(f"ID (类型保持): {new_case.request.json_body['id']} | 类型: {type(new_case.request.json_body['id'])}")
print(f"Timeout (自动转换): {new_case.request.timeout} | 类型: {type(new_case.request.timeout)}")
print(f"URL (混合文本): {action.url} | 类型: {type(action.url)}")
print(f"ID (类型保持): {action.json_body['id']} | 类型: {type(action.json_body['id'])}")
print(f"Timeout (自动转换): {action.timeout} | 类型: {type(action.timeout)}")
# #
assert isinstance(action.json_body['id'], int)
# #
assert action.url == "http://api.com/auth_123"
assert action.timeout == 100
assert isinstance(new_case.request.json_body['id'], int)
assert new_case.request.url == "http://api.com/auth_123"
assert new_case.request.timeout == 100
# if os.path.exists(cache_path): os.remove(cache_path)
print("\nExchange 场景全部验证通过!")