This commit is contained in:
gongwenxin 2025-05-19 17:09:09 +08:00
parent 331e397367
commit 156dcdfaf9
9 changed files with 14513 additions and 630 deletions

View File

@ -0,0 +1,84 @@
from ddms_compliance_suite.test_framework_core import BaseAPITestCase, TestSeverity, ValidationResult, APIRequestContext, APIResponseContext
import logging
class StatusCode200Check(BaseAPITestCase):
# 1. 元数据
id = "TC-STATUS-001"
name = "基本状态码 200 检查"
description = "验证 API 响应状态码是否为 200 OK。"
severity = TestSeverity.CRITICAL
tags = ["status_code", "smoke_test"]
# 适用于所有方法和路径 (默认)
# applicable_methods = None
# applicable_paths_regex = None
def __init__(self, endpoint_spec: dict, global_api_spec: dict):
super().__init__(endpoint_spec, global_api_spec)
self.logger.info(f"测试用例 {self.id} ({self.name}) 已针对端点 '{self.endpoint_spec.get('method')} {self.endpoint_spec.get('path')}' 初始化。")
def validate_response(self, response_context: APIResponseContext, request_context: APIRequestContext) -> list[ValidationResult]:
results = []
expected_status_code = 200
actual_status_code = response_context.status_code
if actual_status_code == expected_status_code:
results.append(
ValidationResult(
passed=True,
message=f"响应状态码为 {actual_status_code},符合预期 {expected_status_code}"
)
)
self.logger.info(f"状态码验证通过: {actual_status_code} == {expected_status_code} for {request_context.url}")
else:
results.append(
ValidationResult(
passed=False,
message=f"期望状态码 {expected_status_code},但收到 {actual_status_code}",
details={
"expected_status": expected_status_code,
"actual_status": actual_status_code,
"request_url": request_context.url,
"response_body_sample": (response_context.text_content or "")[:200] # 包含部分响应体以帮助诊断
}
)
)
self.logger.warning(f"状态码验证失败: 期望 {expected_status_code}, 实际 {actual_status_code} for {request_context.url}")
return results
class HeaderExistenceCheck(BaseAPITestCase):
id = "TC-HEADER-001"
name = "检查响应中是否存在 'X-Request-ID'"
description = "验证 API 响应是否包含 'X-Request-ID' 头。"
severity = TestSeverity.MEDIUM
tags = ["header", "observability"]
EXPECTED_HEADER = "X-Request-ID" # 示例,可以根据实际需要修改
def __init__(self, endpoint_spec: dict, global_api_spec: dict):
super().__init__(endpoint_spec, global_api_spec)
self.logger.info(f"测试用例 {self.id} ({self.name}) 已初始化 for endpoint {self.endpoint_spec.get('path')}")
def validate_response(self, response_context: APIResponseContext, request_context: APIRequestContext) -> list[ValidationResult]:
results = []
if self.EXPECTED_HEADER in response_context.headers:
results.append(
ValidationResult(
passed=True,
message=f"响应头中找到了期望的 '{self.EXPECTED_HEADER}'"
)
)
self.logger.info(f"请求头 '{self.EXPECTED_HEADER}' 存在于 {request_context.url} 的响应中。")
else:
results.append(
ValidationResult(
passed=False,
message=f"响应头中未找到期望的 '{self.EXPECTED_HEADER}'",
details={
"expected_header": self.EXPECTED_HEADER,
"actual_headers": list(response_context.headers.keys())
}
)
)
self.logger.warning(f"请求头 '{self.EXPECTED_HEADER}' 未在 {request_context.url} 的响应中找到。")
return results

File diff suppressed because it is too large Load Diff

View File

@ -60,6 +60,12 @@ def parse_args():
rule_group.add_argument('--create-basic-rules', action='store_true',
help='创建基本规则集性能、安全、RESTful设计、错误处理',default=False)
# 新增:自定义测试用例参数组
custom_tc_group = parser.add_argument_group('自定义测试用例选项')
custom_tc_group.add_argument('--custom-test-cases-dir',
default=None, # 或者 './custom_testcases' 如果想设为默认
help='存放自定义APITestCase Python文件的目录路径。如果未提供则不加载自定义测试。')
return parser.parse_args()
def list_yapi_categories(yapi_file: str):
@ -280,9 +286,10 @@ def main():
"""主函数"""
args = parse_args()
# 设置日志级别
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
logging.getLogger('ddms_compliance_suite').setLevel(logging.DEBUG)
logger.setLevel(logging.DEBUG)
logger.debug("已启用详细日志模式")
# 列出规则
if args.list_rules:
@ -307,10 +314,12 @@ def main():
categories = args.categories.split(',') if args.categories else None
tags = args.tags.split(',') if args.tags else None
# 创建测试编排器
# 实例化测试编排器
# 将 custom_test_cases_dir 参数传递给 APITestOrchestrator 的构造函数
orchestrator = APITestOrchestrator(
base_url=args.base_url,
rule_repo_path=args.rules_path
rule_repo_path=args.rules_path,
custom_test_cases_dir=args.custom_test_cases_dir # 新增参数
)
# 创建基本规则集
@ -329,27 +338,50 @@ def main():
if args.yapi:
logger.info(f"从YAPI文件运行测试: {args.yapi}")
summary = orchestrator.run_tests_from_yapi(args.yapi, categories)
summary = orchestrator.run_tests_from_yapi(
yapi_file_path=args.yapi,
categories=categories,
custom_test_cases_dir=args.custom_test_cases_dir # 也传递给具体执行方法,以支持运行时覆盖
)
elif args.swagger:
logger.info(f"从Swagger文件运行测试: {args.swagger}")
summary = orchestrator.run_tests_from_swagger(args.swagger, tags)
summary = orchestrator.run_tests_from_swagger(
swagger_file_path=args.swagger,
tags=tags,
custom_test_cases_dir=args.custom_test_cases_dir # 也传递给具体执行方法
)
else:
logger.error("必须提供YAPI或Swagger文件路径")
if not summary:
logger.error("测试执行失败")
return 1
# 打印结果摘要
summary.print_summary()
summary.print_summary_to_console()
# 保存结果
if args.output:
save_results(summary, args.output, args.format)
# 根据测试结果设置退出码
return 0 if summary.failed == 0 and summary.error == 0 else 1
# 直接访问 TestSummary 的属性
has_endpoint_errors = summary.endpoints_error > 0
has_endpoint_failures = summary.endpoints_failed > 0
# 或者,也可以关注测试用例级别的失败/错误
# has_test_case_errors = summary.test_cases_error > 0
# has_test_case_failures = summary.test_cases_failed > 0
if has_endpoint_errors or has_endpoint_failures:
return 1 # 表示测试运行中存在失败或错误
else:
return 0 # 所有端点测试通过或部分成功(无错误或关键失败)
if __name__ == "__main__":
sys.exit(main())
# python run_api_tests.py --base-url http://127.0.0.1:4523/m1/6389742-6086420-default --swagger assets/doc/井筒API示例swagger.json
# python run_api_tests.py --base-url http://127.0.0.1:4523/m1/6389742-6086420-default --swagger assets/doc/井筒API示例swagger.json --custom-test-cases-dir ./custom_testcases \
# --verbose \
# --output test_report.json

13650
test_report.json Normal file

File diff suppressed because it is too large Load Diff