08ea5fbe98
添加用户管理视图、API和状态管理文件
284 lines
9.2 KiB
Python
284 lines
9.2 KiB
Python
"""
|
||
API测试器核心模块
|
||
"""
|
||
|
||
import requests
|
||
import time
|
||
from typing import Dict, Any, Optional, List
|
||
from dataclasses import dataclass, field
|
||
|
||
from .validation import ValidationEngine, ValidationRule
|
||
from .auth_manager import AuthManager
|
||
from config.settings import config
|
||
from utils.logger import TestLogger
|
||
from utils.reporter import TestResult as ReportTestResult, TestSummary
|
||
|
||
|
||
@dataclass
|
||
class TestResult:
|
||
"""测试结果"""
|
||
passed: bool
|
||
test_name: str
|
||
error_message: str = ""
|
||
response_time: float = 0.0
|
||
status_code: int = 0
|
||
request_data: Dict[str, Any] = field(default_factory=dict)
|
||
response_data: Dict[str, Any] = field(default_factory=dict)
|
||
|
||
|
||
class APITester:
|
||
"""API测试器"""
|
||
|
||
def __init__(self, logger: TestLogger = None, auto_auth: bool = True):
|
||
"""
|
||
初始化API测试器
|
||
|
||
Args:
|
||
logger: 日志记录器
|
||
auto_auth: 是否自动认证
|
||
"""
|
||
self.logger = logger or TestLogger("api_tester", config.logging_file, config.logging_level)
|
||
self.session = requests.Session()
|
||
self.auth_manager = AuthManager(self.logger)
|
||
self.validation_engine = ValidationEngine()
|
||
self.auto_auth = auto_auth
|
||
|
||
# 配置会话
|
||
self.session.headers.update({
|
||
"Content-Type": "application/json",
|
||
"Accept": "application/json"
|
||
})
|
||
|
||
# 如果启用自动认证,尝试登录
|
||
if auto_auth:
|
||
self._ensure_authenticated()
|
||
|
||
def _ensure_authenticated(self) -> bool:
|
||
"""
|
||
确保已认证
|
||
|
||
Returns:
|
||
是否认证成功
|
||
"""
|
||
return self.auth_manager.ensure_authenticated()
|
||
|
||
def _update_auth_header(self) -> None:
|
||
"""更新认证请求头"""
|
||
auth_header = self.auth_manager.get_auth_header(auto_refresh=True)
|
||
self.session.headers.update(auth_header)
|
||
|
||
def set_token(self, token: str) -> None:
|
||
"""
|
||
设置认证令牌(已弃用,建议使用AuthManager)
|
||
|
||
Args:
|
||
token: JWT令牌
|
||
"""
|
||
self.logger.warning("set_token方法已弃用,建议使用AuthManager")
|
||
self.session.headers.update({
|
||
"Authorization": f"Bearer {token}"
|
||
})
|
||
self.logger.info(f"已设置认证令牌")
|
||
|
||
def clear_token(self) -> None:
|
||
"""清除认证令牌(已弃用,建议使用AuthManager)"""
|
||
self.logger.warning("clear_token方法已弃用,建议使用AuthManager")
|
||
self.session.headers.pop("Authorization", None)
|
||
self.logger.info(f"已清除认证令牌")
|
||
|
||
def login(self, username: str = None, password: str = None) -> bool:
|
||
"""
|
||
用户登录
|
||
|
||
Args:
|
||
username: 用户名
|
||
password: 密码
|
||
|
||
Returns:
|
||
是否登录成功
|
||
"""
|
||
return self.auth_manager.login(username, password)
|
||
|
||
def request(
|
||
self,
|
||
method: str,
|
||
endpoint: str,
|
||
data: Dict[str, Any] = None,
|
||
params: Dict[str, Any] = None,
|
||
headers: Dict[str, str] = None,
|
||
expected_status: int = 200,
|
||
test_name: str = None,
|
||
require_auth: bool = True
|
||
) -> TestResult:
|
||
"""
|
||
发送HTTP请求
|
||
|
||
Args:
|
||
method: HTTP方法(GET, POST, PUT, DELETE)
|
||
endpoint: API端点
|
||
data: 请求体数据
|
||
params: URL参数
|
||
headers: 请求头
|
||
expected_status: 期望的状态码
|
||
test_name: 测试名称
|
||
require_auth: 是否需要认证
|
||
|
||
Returns:
|
||
测试结果
|
||
"""
|
||
if test_name is None:
|
||
test_name = f"{method} {endpoint}"
|
||
|
||
url = f"{config.api_base_url}{endpoint}"
|
||
|
||
# 如果需要认证,确保已认证并更新认证头
|
||
if require_auth and self.auto_auth:
|
||
if not self._ensure_authenticated():
|
||
return TestResult(
|
||
passed=False,
|
||
test_name=test_name,
|
||
error_message="认证失败"
|
||
)
|
||
self._update_auth_header()
|
||
|
||
self.logger.log_test_start(test_name)
|
||
self.logger.log_request(method, url, data, headers)
|
||
|
||
try:
|
||
start_time = time.time()
|
||
|
||
if method.upper() == "GET":
|
||
response = self.session.get(
|
||
url,
|
||
params=params,
|
||
headers=headers,
|
||
timeout=config.api_timeout
|
||
)
|
||
elif method.upper() == "POST":
|
||
response = self.session.post(
|
||
url,
|
||
json=data,
|
||
params=params,
|
||
headers=headers,
|
||
timeout=config.api_timeout
|
||
)
|
||
elif method.upper() == "PUT":
|
||
response = self.session.put(
|
||
url,
|
||
json=data,
|
||
params=params,
|
||
headers=headers,
|
||
timeout=config.api_timeout
|
||
)
|
||
elif method.upper() == "DELETE":
|
||
response = self.session.delete(
|
||
url,
|
||
params=params,
|
||
headers=headers,
|
||
timeout=config.api_timeout
|
||
)
|
||
else:
|
||
raise ValueError(f"不支持的HTTP方法: {method}")
|
||
|
||
response_time = (time.time() - start_time) * 1000
|
||
|
||
try:
|
||
response_data = response.json()
|
||
except:
|
||
response_data = {"raw": response.text}
|
||
|
||
self.logger.log_response(response.status_code, response_time, response_data)
|
||
|
||
# 验证状态码
|
||
passed, error = self.validation_engine.validate_status_code(expected_status, response.status_code)
|
||
|
||
if passed:
|
||
self.logger.log_test_end(test_name, True, response_time)
|
||
return TestResult(
|
||
passed=True,
|
||
test_name=test_name,
|
||
response_time=response_time,
|
||
status_code=response.status_code,
|
||
request_data=data or params or {},
|
||
response_data=response_data
|
||
)
|
||
else:
|
||
self.logger.log_test_end(test_name, False, response_time)
|
||
return TestResult(
|
||
passed=False,
|
||
test_name=test_name,
|
||
error_message=error,
|
||
response_time=response_time,
|
||
status_code=response.status_code,
|
||
request_data=data or params or {},
|
||
response_data=response_data
|
||
)
|
||
|
||
except requests.exceptions.Timeout:
|
||
error_msg = "请求超时"
|
||
self.logger.error(f"❌ {test_name} - {error_msg}")
|
||
return TestResult(
|
||
passed=False,
|
||
test_name=test_name,
|
||
error_message=error_msg,
|
||
response_time=config.api_timeout * 1000
|
||
)
|
||
|
||
except requests.exceptions.ConnectionError:
|
||
error_msg = "连接错误"
|
||
self.logger.error(f"❌ {test_name} - {error_msg}")
|
||
return TestResult(
|
||
passed=False,
|
||
test_name=test_name,
|
||
error_message=error_msg
|
||
)
|
||
|
||
except Exception as e:
|
||
self.logger.log_error(e)
|
||
return TestResult(
|
||
passed=False,
|
||
test_name=test_name,
|
||
error_message=f"未知错误: {str(e)}"
|
||
)
|
||
|
||
def validate(
|
||
self,
|
||
test_result: TestResult,
|
||
validation_rules: List[ValidationRule]
|
||
) -> TestResult:
|
||
"""
|
||
验证测试结果
|
||
|
||
Args:
|
||
test_result: 测试结果
|
||
validation_rules: 验证规则列表
|
||
|
||
Returns:
|
||
验证后的测试结果
|
||
"""
|
||
if not test_result.passed:
|
||
return test_result
|
||
|
||
for rule in validation_rules:
|
||
if rule.rule_type == "status_code":
|
||
passed, error = rule.validate(test_result.status_code)
|
||
elif rule.rule_type == "response_time":
|
||
passed, error = rule.validate(test_result.response_time)
|
||
elif rule.rule_type in ["contains", "equals", "json_path", "regex", "header", "schema"]:
|
||
passed, error = rule.validate(actual_data=test_result.response_data)
|
||
else:
|
||
passed, error = False, f"未知的验证规则: {rule.rule_type}"
|
||
|
||
self.logger.log_validation(rule.rule_type, passed, error)
|
||
|
||
if not passed:
|
||
test_result.passed = False
|
||
test_result.error_message = error
|
||
break
|
||
|
||
return test_result
|
||
|
||
def close(self) -> None:
|
||
"""关闭会话"""
|
||
self.session.close()
|
||
self.logger.info("已关闭测试会话") |