59 lines
3.5 KiB
Python
59 lines
3.5 KiB
Python
from ddms_compliance_suite.test_framework_core import BaseAPITestCase, ValidationResult, APIResponseContext, APIRequestContext, TestSeverity
|
||
import re
|
||
from typing import Dict, Any, List, Optional
|
||
# TODO 获取资源的时候复数(get方法list)
|
||
class ResourceCollectionPluralCheckTestCase(BaseAPITestCase):
|
||
id = "TC-RESTful-004"
|
||
name = "资源集合复数命名检查"
|
||
description = "验证表示资源集合的路径是否使用复数形式。动词(如push、send等)不需要使用复数形式。"
|
||
severity = TestSeverity.MEDIUM
|
||
tags = ["normative", "restful", "url-structure"]
|
||
|
||
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.common_verbs = {
|
||
"push", "send", "publish", "subscribe", "create", "update", "delete",
|
||
"get", "set", "add", "remove", "search", "query", "find", "calculate",
|
||
"process", "validate", "verify", "check", "analyze", "export", "import",
|
||
"upload", "download", "sync", "login", "logout", "register", "activate",
|
||
"deactivate", "approve", "reject", "cancel", "confirm", "notify"
|
||
}
|
||
|
||
# 已知的单数形式名词,即使不以's'结尾也是正确的
|
||
self.known_singulars = {
|
||
"status", "gas", "analysis", "data", "info", "metadata", "media",
|
||
"equipment", "staff", "fish", "sheep", "deer", "series", "species",
|
||
"aircraft", "offspring", "feedback", "content", "news"
|
||
}
|
||
|
||
def validate_response(self, response_context: APIResponseContext, request_context: APIRequestContext) -> List[ValidationResult]:
|
||
path = self.endpoint_spec['path']
|
||
method = self.endpoint_spec['method']
|
||
|
||
# 这个检查通常适用于返回列表的GET请求,或者创建资源的POST请求
|
||
if method.lower() not in ['get', 'post']:
|
||
return [self.passed(f"跳过检查:{method} 方法,不适用于资源集合复数检查。")]
|
||
|
||
path_segments = [seg for seg in path.strip('/').split('/') if '{' not in seg and not re.match(r'v\d+', seg)]
|
||
|
||
if not path_segments:
|
||
return [self.passed("跳过检查:路径不含有效分段。")]
|
||
|
||
resource_segment = path_segments[-1]
|
||
|
||
# 检查是否为动词
|
||
if resource_segment.lower() in self.common_verbs:
|
||
return [self.passed(f"路径 '{path}' 的最后一个路径分段 '{resource_segment}' 是动词,不需要使用复数形式。")]
|
||
|
||
# 检查是否为已知的单数形式名词
|
||
if resource_segment.lower() in self.known_singulars:
|
||
return [self.passed(f"路径 '{path}' 的资源名 '{resource_segment}' 是已知的单数形式名词,符合规范。")]
|
||
|
||
# 对于其他名词,检查是否使用复数形式
|
||
if not resource_segment.endswith('s'):
|
||
message = f"路径 '{path}' 的最后一个路径分段 '{resource_segment}' 可能不是复数形式,建议对资源集合使用复数命名。"
|
||
return [self.failed(message, details={'path': path, 'segment': resource_segment})]
|
||
|
||
message = f"路径 '{path}' 的资源集合命名 '{resource_segment}' 符合复数命名规范。"
|
||
return [self.passed(message)] |