""" 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("已关闭测试会话")