#!/usr/bin/env python # -*- coding: utf-8 -*- """ API测试工具 此工具使用DDMS测试编排器从YAPI或Swagger定义执行API测试。 支持使用规则库进行高级验证。 """ import os import sys import json import logging import argparse from pathlib import Path from ddms_compliance_suite.test_orchestrator import APITestOrchestrator, TestSummary # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) def parse_args(): """解析命令行参数""" parser = argparse.ArgumentParser(description='DDMS API测试工具') # 基本参数 parser.add_argument('--base-url', required=True, help='API基础URL') parser.add_argument('--verbose', '-v', action='store_true', help='启用详细日志') parser.add_argument('--output', '-o', help='输出文件路径') parser.add_argument('--format', choices=['json', 'html'], default='json', help='输出格式') # API定义参数 api_group = parser.add_argument_group('API定义源') api_group.add_argument('--yapi', help='YAPI定义文件路径') api_group.add_argument('--swagger', help='Swagger定义文件路径') # 过滤参数 filter_group = parser.add_argument_group('过滤选项') filter_group.add_argument('--categories', help='YAPI分类,逗号分隔') filter_group.add_argument('--tags', help='Swagger标签,逗号分隔') filter_group.add_argument('--list-categories', action='store_true', help='列出YAPI分类') filter_group.add_argument('--list-tags', action='store_true', help='列出Swagger标签') # 新增:自定义测试用例参数组 custom_tc_group = parser.add_argument_group('自定义测试用例选项') custom_tc_group.add_argument('--custom-test-cases-dir', default=None, # 或者 './custom_testcases' 如果想设为默认 help='存放自定义APITestCase Python文件的目录路径。如果未提供,则不加载自定义测试。') # 新增:LLM 配置选项 llm_group = parser.add_argument_group('LLM 配置选项 (可选)') llm_group.add_argument('--llm-api-key', default=os.environ.get("OPENAI_API_KEY"), # 尝试从环境变量获取 help='LLM服务的API密钥 (例如 OpenAI API Key)。默认从环境变量 OPENAI_API_KEY 读取。') llm_group.add_argument('--llm-base-url', default="https://dashscope.aliyuncs.com/compatible-mode/v1", help='LLM服务的自定义基础URL (例如 OpenAI API代理)。') llm_group.add_argument('--llm-model-name', default="qwen-plus", # 设置一个常用的默认模型 help='要使用的LLM模型名称 (例如 "gpt-3.5-turbo", "gpt-4")。') llm_group.add_argument('--use-llm-for-request-body', action='store_true', default=False, # 默认不使用LLM生成请求体 help='是否启用LLM为API请求生成请求体数据。') llm_group.add_argument('--use-llm-for-path-params', action='store_true', default=False, help='是否启用LLM为API请求生成路径参数。') llm_group.add_argument('--use-llm-for-query-params', action='store_true', default=False, help='是否启用LLM为API请求生成查询参数。') llm_group.add_argument('--use-llm-for-headers', action='store_true', default=False, help='是否启用LLM为API请求生成头部参数。') return parser.parse_args() def list_yapi_categories(yapi_file: str): """列出YAPI分类""" from ddms_compliance_suite.input_parser.parser import InputParser logger.info(f"从YAPI文件解析分类: {yapi_file}") parser = InputParser() parsed_yapi = parser.parse_yapi_spec(yapi_file) if not parsed_yapi: logger.error(f"解析YAPI文件失败: {yapi_file}") return print("\nYAPI分类:") for i, category in enumerate(parsed_yapi.categories, 1): print(f"{i}. {category.get('name', '未命名')} - {category.get('desc', '无描述')}") def list_swagger_tags(swagger_file: str): """列出Swagger标签""" from ddms_compliance_suite.input_parser.parser import InputParser logger.info(f"从Swagger文件解析标签: {swagger_file}") parser = InputParser() parsed_swagger = parser.parse_swagger_spec(swagger_file) if not parsed_swagger: logger.error(f"解析Swagger文件失败: {swagger_file}") return print("\nSwagger标签:") for i, tag in enumerate(parsed_swagger.tags, 1): print(f"{i}. {tag.get('name', '未命名')} - {tag.get('description', '无描述')}") def save_results(summary: TestSummary, output_file: str, format_type: str): """保存测试结果""" if format_type == 'json': with open(output_file, 'w', encoding='utf-8') as f: f.write(summary.to_json(pretty=True)) logger.info(f"测试结果已保存为JSON: {output_file}") elif format_type == 'html': # 创建简单的HTML报告 html_content = f""" API测试报告

API测试报告

测试结果摘要

总测试数: {summary.total}

通过: {summary.passed}

失败: {summary.failed}

错误: {summary.error}

跳过: {summary.skipped}

成功率: {summary.success_rate:.2f}%

总耗时: {summary.duration:.2f}秒

开始时间: {summary.start_time.isoformat()}

结束时间: {summary.end_time.isoformat() if summary.end_time else 'N/A'}

详细测试结果

""" for result in summary.results: status_class = "pass" if result.status == "通过" else "fail" if result.status == "失败" else "error" if result.status == "错误" else "skip" response_code = result.api_response.status_code if result.api_response else "N/A" html_content += f""" """ html_content += """
端点 状态 消息 响应码 耗时(秒)
{result.endpoint_id} {result.status} {result.message} {response_code} {result.elapsed_time:.4f}
""" with open(output_file, 'w', encoding='utf-8') as f: f.write(html_content) logger.info(f"测试结果已保存为HTML: {output_file}") def main(): """主函数""" args = parse_args() if args.verbose: logging.getLogger('ddms_compliance_suite').setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG) logger.debug("已启用详细日志模式") # 检查是否提供了API定义源 if not args.yapi and not args.swagger: logger.error("请提供API定义源:--yapi 或 --swagger") return 1 # 列出分类/标签 if args.list_categories and args.yapi: list_yapi_categories(args.yapi) return 0 if args.list_tags and args.swagger: list_swagger_tags(args.swagger) return 0 # 解析分类/标签过滤器 categories = args.categories.split(',') if args.categories else None tags = args.tags.split(',') if args.tags else None logger.info(f"args.api_key: {args.llm_api_key}") # 实例化测试编排器 # 将 custom_test_cases_dir 参数传递给 APITestOrchestrator 的构造函数 orchestrator = APITestOrchestrator( base_url=args.base_url, custom_test_cases_dir=args.custom_test_cases_dir, llm_api_key=args.llm_api_key, llm_base_url=args.llm_base_url, llm_model_name=args.llm_model_name, use_llm_for_request_body=args.use_llm_for_request_body, use_llm_for_path_params=args.use_llm_for_path_params, use_llm_for_query_params=args.use_llm_for_query_params, use_llm_for_headers=args.use_llm_for_headers ) # 运行测试 summary = None if args.yapi: logger.info(f"从YAPI文件运行测试: {args.yapi}") 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( 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_to_console() # 保存结果 if args.output: save_results(summary, args.output, args.format) # 根据测试结果设置退出码 # 直接访问 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 --custom-test-cases-dir ./custom_testcases \ # --verbose \ # --output test_report.json