fix(exchange,case_validator),refactor(),feat(model): 解决 Pydantic 模型初始化与变量占位符的类型冲突,优化变量替换逻辑,重构 CaseInfo 模型并引入延迟校验机制

- 引入 SmartInt 和 SmartDict 类型,支持 YAML 占位符与业务类型的自动转换。
- 优化 CaseInfo 互斥校验逻辑,确保 request 与 api_action 二选一。
- 统一使用 Pydantic V2 的 model_config 规范。
- 将变量替换时机提前至模型实例化之前,支持占位符在校验前完成真实值注入,
保证了 int/bool 等字段的类型转换正确性。
- 优化断言渲染时机,支持响应提取值关联。
This commit is contained in:
2026-03-11 10:29:16 +08:00
parent 69a96a0060
commit 293b5160fe
39 changed files with 1359 additions and 1031 deletions

View File

@@ -10,10 +10,18 @@
@desc:
"""
import logging
from typing import List, Union, Any
from pydantic import TypeAdapter
from core.exchange import Exchange
from core.models import ValidateItem
logger = logging.getLogger(__name__)
VALIDATE_LIST_ADAPTER = TypeAdapter(List[ValidateItem])
class CaseValidator:
VALIDATORS = {}
@@ -26,22 +34,46 @@ class CaseValidator:
return decorator
@classmethod
def assert_all(cls, validate: dict):
if not validate:
def validate(cls,response: Any, validate_list: List[ValidateItem]):
"""
核心断言入口:适配 CaseInfo.validate_data (List[ValidateItem])
"""
if not validate_list:
return
for assert_type, cases in validate.items():
logger.info(f"键:{assert_type},值:{cases}")
validator = cls.VALIDATORS.get(assert_type)
logger.info(f"获取到的断言:{validator}")
# dicts = [
# item.model_dump(by_alias=True) if isinstance(item, ValidateItem) else item for item in validate_list
# ]
# rendered = exchanger.replace(dicts)
# # 触发 SmartInt/SmartDict 类型修复
# final_list = VALIDATE_LIST_ADAPTER.validate_python(rendered)
for item in validate_list:
# 1. 提取模型中的数据
# 此时 final_case 里的 item 已经是经过变量替换后的实体
actual = item.check
expect = item.expect
method = item.assert_method # 即模型中的 alias="assert"
msg = item.msg or f"Assert {actual} {method} {expect}"
# 2. 获取对应的断言函数
validator = cls.VALIDATORS.get(method)
if not validator:
raise KeyError(f"Unsupported validator: {assert_type}")
for msg, (a, b) in cases.items():
validator(a, b, msg)
logger.error(f"❌ 不支持的断言方式: {method}")
raise KeyError(f"Unsupported validator: {method}")
# 3. 执行断言
try:
validator(actual, expect, msg)
except AssertionError as e:
logger.error(
f"❌ 断言失败: {msg} | 实际值: {actual} ({type(actual)}), 期望值: {expect} ({type(expect)})")
raise e
@CaseValidator.register('equals')
def validate_equals(a, b, msg):
logger.info(f"assert {a} == {b}, {msg}执行这段代码")
logger.info(f"assert {a} == {b}, {msg} 执行这段代码")
print(f"assert {a} == {b}, {msg} 执行这段代码")
assert a == b, msg
@@ -64,17 +96,12 @@ def validate_not_contains(a, b, msg):
if __name__ == '__main__':
mock_case = {
"validate": {
"equals": {
"判断相等": ["Success.", "Success."]
},
"not_equals": {
"判断不相等": ["Success.", "Suc."]
}
}
}
resp=None
mock_case = [
{"check": 100, "expect": 100, "assert": "equals"},
{"check": "success", "expect": "success", "assert": "contains"}
]
final_validate_list = VALIDATE_LIST_ADAPTER.validate_python(mock_case)
case_validator = CaseValidator()
print(case_validator.VALIDATORS)
case_validator.assert_all(mock_case.get("validate"))
case_validator.validate(resp,final_validate_list)