2025-06-19 18:33:03 +08:00

128 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import re
from typing import Dict, Any, List, Optional
from ddms_compliance_suite.test_framework_core import BaseAPITestCase, ValidationResult, APIResponseContext, APIRequestContext, TestSeverity
class URLVersionCheckCase(BaseAPITestCase):
"""
检查API URL是否包含版本号如v1, api/v2, v3.0等)并以/api开头
"""
id = "TC-DMS-URL-VERSION-001"
name = "DMS API URL版本号检查"
description = "检查API URL是否包含标准格式的版本号支持的格式包括v1, api/v2, v3.0, version/1, 1.0等,并且路径需要以/api开头"
severity = TestSeverity.MEDIUM
tags = ["url", "version", "dms-core", "api-design"]
# 这个测试用例不需要发送实际请求
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)
def validate_response(self, response_context: APIResponseContext, request_context: APIRequestContext) -> List[ValidationResult]:
"""
检查API URL是否以/api开头并包含版本号
"""
results = []
# 获取API路径
path = self.endpoint_spec.get('path', '')
if not path:
results.append(self.failed(
message="无法获取API路径",
details={"endpoint_spec_keys": list(self.endpoint_spec.keys())}
))
return results
# 检查是否是系统级API可能不需要遵循标准路径格式
is_system_api = re.match(r'^/(health|ping|status|metrics|system)(/|$)', path)
# 1. 检查路径是否以/api开头
starts_with_api = path.startswith('/api/')
if not starts_with_api and not is_system_api:
results.append(self.failed(
message=f"API路径 '{path}' 不是以'/api/'开头",
details={"full_path": path, "requirement": "路径必须以'/api/'开头"}
))
elif starts_with_api:
results.append(self.passed(
message=f"API路径 '{path}' 正确以'/api/'开头",
details={"full_path": path}
))
# 2. 检查路径中是否包含版本号
version_patterns = [
# 标准版本格式: /v1/, /v2/, /v3/ 等
r'/v\d+/',
# 带小数点的版本: /v1.0/, /v2.1/ 等
r'/v\d+\.\d+/',
# 使用 'version' 单词: /version/1/, /version/2/ 等
r'/version/\d+/',
# API前缀版本: /api/v1/, /api/v2/ 等
r'/api/v\d+/',
# 直接数字版本: /1/, /2/ (仅在特定位置)
r'/api/\d+/',
# 特殊格式: 如 /v1-beta/, /v2-alpha/ 等
r'/v\d+[\-_](alpha|beta|rc\d*)/',
# 年份版本: /2023/, /2024/ 等 (仅在特定位置)
r'/20\d{2}/',
]
# 检查是否包含版本号
matched_pattern = None
version_str = None
for pattern in version_patterns:
match = re.search(pattern, path)
if match:
matched_pattern = pattern
version_str = match.group(0).strip('/')
break
if matched_pattern and version_str:
results.append(self.passed(
message=f"API路径 '{path}' 包含版本标识: '{version_str}'",
details={
"pattern_matched": matched_pattern,
"version_string": version_str,
"full_path": path
}
))
else:
# 特殊情况检查是否是根API或系统级API可能不需要版本号
if is_system_api:
results.append(self.passed(
message=f"API路径 '{path}' 是系统级API不需要版本号",
details={"full_path": path, "api_type": "system"}
))
else:
results.append(self.failed(
message=f"API路径 '{path}' 不包含任何已知格式的版本标识",
details={
"full_path": path,
"supported_patterns": [p.replace('\\d+', 'N').replace('\\d{2}', 'NN') for p in version_patterns]
}
))
# 提供改进建议
# 确保建议路径始终以/api开头并包含版本号
base_path_parts = path.split('/')
base_path_parts = [p for p in base_path_parts if p] # 移除空字符串
if not starts_with_api:
# 如果不是以/api开头建议路径应该是/api/v1/原始路径
suggested_path = f"/api/v1/{'/'.join(base_path_parts)}"
else:
# 如果已经以/api开头但缺少版本号插入v1在api之后
suggested_path = "/api/v1"
if len(base_path_parts) > 1: # 有api后面的部分
suggested_path += f"/{'/'.join(base_path_parts[1:])}"
results.append(ValidationResult(
passed=False,
message=f"建议将路径修改为符合规范的格式,例如: '{suggested_path}'",
details={"original_path": path, "suggested_path": suggested_path}
))
return results