from ddms_compliance_suite.test_framework_core import BaseAPITestCase, ValidationResult, APIResponseContext, APIRequestContext, TestSeverity from typing import Dict, Any, List, Optional import re class PaginationParamsCheckTestCase(BaseAPITestCase): """ 检查API请求中是否包含标准分页参数:pageNo、pageSize和isSearchCount 只有名称含有"查询"一类,并且不含有"详情"一类的API才应用这个验证 """ id = "TC-DMS-PAGINATION-001" name = "分页参数检查" description = "检查API请求参数中是否包含标准分页参数:pageNo、pageSize和isSearchCount。只有名称含有'查询'、'列表'等并且不含有'详情'一类的API才应用此验证。" severity = TestSeverity.MEDIUM tags = ["pagination", "params", "backend-guide"] # 这个测试用例不需要发送实际请求 skip_execution = True def __init__(self, endpoint_spec: Dict[str, Any], global_api_spec: Dict[str, Any], json_schema_validator: Optional[Any] = None, llm_service: Optional[Any] = None): super().__init__(endpoint_spec, global_api_spec, json_schema_validator, llm_service=llm_service) # 定义需要验证的API名称关键词 self.include_keywords = ["查询", "列表", "分页", "page", "list", "query", "search", "find"] # 定义排除的API名称关键词 self.exclude_keywords = ["详情", "明细", "detail", "info", "get", "查看"] def validate_response(self, response_context: APIResponseContext, request_context: APIRequestContext) -> List[ValidationResult]: """ 检查API请求中是否包含标准分页参数 """ results = [] # 获取API路径和方法 path = self.endpoint_spec.get('path', '') method = self.endpoint_spec.get('method', '').lower() # 获取API的摘要和描述信息 summary = self.endpoint_spec.get('summary', '') description = self.endpoint_spec.get('description', '') operation_id = self.endpoint_spec.get('operationId', '') # 组合所有可能包含API名称或功能描述的字段 api_description_text = f"{summary} {description} {operation_id} {path}".lower() # 检查是否包含需要验证的关键词,且不包含排除的关键词 contains_include_keyword = any(keyword.lower() in api_description_text for keyword in self.include_keywords) contains_exclude_keyword = any(keyword.lower() in api_description_text for keyword in self.exclude_keywords) # 如果不满足准入规则,直接返回通过 if not contains_include_keyword or contains_exclude_keyword: results.append(self.passed( message=f"跳过检查:API不符合分页参数检查的准入规则(需包含'查询'/'列表'等关键词,且不包含'详情'等关键词)", details={ "path": path, "method": method.upper(), "summary": summary, "contains_include_keyword": contains_include_keyword, "contains_exclude_keyword": contains_exclude_keyword } )) return results # 如果是GET请求或可能返回列表的请求,才进行检查 if method not in ['get', 'post']: results.append(self.passed( message=f"跳过检查:{method.upper()} 方法,不适用于分页参数检查" )) return results # 初始化检查结果 found_page_no = False found_page_size = False found_is_search_count = False # 检查查询参数 parameters = self.endpoint_spec.get('parameters', []) for param in parameters: param_name = param.get('name', '') param_in = param.get('in', '') if param_in == 'query': if param_name == 'pageNo': found_page_no = True elif param_name == 'pageSize': found_page_size = True elif param_name == 'isSearchCount': found_is_search_count = True # 检查请求体(如果是POST请求) if method == 'post': request_body = self.endpoint_spec.get('requestBody', {}) content = request_body.get('content', {}) for media_type, media_content in content.items(): if 'schema' in media_content: schema = self._get_resolved_schema(media_content['schema']) if 'properties' in schema: properties = schema['properties'] # 检查请求体属性 if 'pageNo' in properties: found_page_no = True if 'pageSize' in properties: found_page_size = True if 'isSearchCount' in properties: found_is_search_count = True # 汇总检查结果 if found_page_no and found_page_size and found_is_search_count: results.append(self.passed( message=f"API请求包含所有标准分页参数:pageNo、pageSize和isSearchCount", details={"path": path, "method": method.upper()} )) else: # 计算缺失的参数 missing_params = [] if not found_page_no: missing_params.append("pageNo") if not found_page_size: missing_params.append("pageSize") if not found_is_search_count: missing_params.append("isSearchCount") if missing_params: results.append(self.failed( message=f"API请求缺少标准分页参数:{', '.join(missing_params)}", details={ "path": path, "method": method.upper(), "missing_params": missing_params, "found_params": { "pageNo": found_page_no, "pageSize": found_page_size, "isSearchCount": found_is_search_count } } )) return results