test:完善API测试框架
- 添加Godot内置API测试脚本 - 实现Python API客户端测试套件 - 添加快速测试和完整测试脚本 - 支持跨平台测试运行(Windows/Linux) - 更新测试文档和使用指南
This commit is contained in:
183
scripts/network/ApiTestScript.gd
Normal file
183
scripts/network/ApiTestScript.gd
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
# API测试脚本 - 验证更新后的接口逻辑
|
||||||
|
class_name ApiTestScript
|
||||||
|
|
||||||
|
# 测试用例
|
||||||
|
var test_cases = [
|
||||||
|
{
|
||||||
|
"name": "测试网络连接",
|
||||||
|
"operation": "network_test",
|
||||||
|
"method": "get_app_status"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "测试发送邮箱验证码",
|
||||||
|
"operation": "send_code",
|
||||||
|
"method": "send_email_verification",
|
||||||
|
"params": ["test@example.com"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "测试邮箱冲突检测",
|
||||||
|
"operation": "send_code",
|
||||||
|
"method": "send_email_verification",
|
||||||
|
"params": ["existing@example.com"] # 假设这个邮箱已存在
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "测试登录",
|
||||||
|
"operation": "login",
|
||||||
|
"method": "login",
|
||||||
|
"params": ["testuser", "password123"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "测试注册",
|
||||||
|
"operation": "register",
|
||||||
|
"method": "register",
|
||||||
|
"params": ["newuser", "newpassword123", "新用户", "newuser@example.com", "123456"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
print("=== API测试脚本启动 ===")
|
||||||
|
print("测试最新API v1.1.1的接口逻辑和toast显示")
|
||||||
|
|
||||||
|
# 延迟一下确保NetworkManager已初始化
|
||||||
|
await get_tree().create_timer(1.0).timeout
|
||||||
|
|
||||||
|
# 运行测试用例
|
||||||
|
run_tests()
|
||||||
|
|
||||||
|
func run_tests():
|
||||||
|
print("\n🧪 开始运行API测试用例...")
|
||||||
|
|
||||||
|
for i in range(test_cases.size()):
|
||||||
|
var test_case = test_cases[i]
|
||||||
|
print("\n--- 测试 %d: %s ---" % [i + 1, test_case.name])
|
||||||
|
|
||||||
|
await run_single_test(test_case)
|
||||||
|
|
||||||
|
# 测试间隔
|
||||||
|
await get_tree().create_timer(2.0).timeout
|
||||||
|
|
||||||
|
print("\n✅ 所有测试用例执行完成")
|
||||||
|
|
||||||
|
func run_single_test(test_case: Dictionary):
|
||||||
|
var operation = test_case.operation
|
||||||
|
var method = test_case.method
|
||||||
|
var params = test_case.get("params", [])
|
||||||
|
|
||||||
|
print("操作类型: ", operation)
|
||||||
|
print("调用方法: ", method)
|
||||||
|
print("参数: ", params)
|
||||||
|
|
||||||
|
# 创建回调函数
|
||||||
|
var callback = func(success: bool, data: Dictionary, error_info: Dictionary):
|
||||||
|
handle_test_response(test_case.name, operation, success, data, error_info)
|
||||||
|
|
||||||
|
# 调用对应的NetworkManager方法
|
||||||
|
var request_id = ""
|
||||||
|
match method:
|
||||||
|
"get_app_status":
|
||||||
|
request_id = NetworkManager.get_app_status(callback)
|
||||||
|
"send_email_verification":
|
||||||
|
if params.size() > 0:
|
||||||
|
request_id = NetworkManager.send_email_verification(params[0], callback)
|
||||||
|
"login":
|
||||||
|
if params.size() >= 2:
|
||||||
|
request_id = NetworkManager.login(params[0], params[1], callback)
|
||||||
|
"register":
|
||||||
|
if params.size() >= 5:
|
||||||
|
request_id = NetworkManager.register(params[0], params[1], params[2], params[3], params[4], callback)
|
||||||
|
_:
|
||||||
|
print("❌ 未知的测试方法: ", method)
|
||||||
|
return
|
||||||
|
|
||||||
|
if request_id != "":
|
||||||
|
print("✅ 请求已发送,ID: ", request_id)
|
||||||
|
else:
|
||||||
|
print("❌ 请求发送失败")
|
||||||
|
|
||||||
|
func handle_test_response(test_name: String, operation: String, success: bool, data: Dictionary, error_info: Dictionary):
|
||||||
|
print("\n=== %s 响应结果 ===" % test_name)
|
||||||
|
print("成功: ", success)
|
||||||
|
print("数据: ", data)
|
||||||
|
print("错误信息: ", error_info)
|
||||||
|
|
||||||
|
# 使用ResponseHandler处理响应
|
||||||
|
var result = ResponseHandler.handle_response(operation, success, data, error_info)
|
||||||
|
|
||||||
|
print("\n--- ResponseHandler处理结果 ---")
|
||||||
|
print("处理成功: ", result.success)
|
||||||
|
print("消息: ", result.message)
|
||||||
|
print("Toast类型: ", result.toast_type)
|
||||||
|
print("是否显示Toast: ", result.should_show_toast)
|
||||||
|
|
||||||
|
# 模拟Toast显示
|
||||||
|
if result.should_show_toast:
|
||||||
|
print("🍞 Toast显示: [%s] %s" % [result.toast_type.to_upper(), result.message])
|
||||||
|
|
||||||
|
# 检查特殊情况
|
||||||
|
check_special_cases(data, error_info)
|
||||||
|
|
||||||
|
func check_special_cases(data: Dictionary, error_info: Dictionary):
|
||||||
|
var response_code = error_info.get("response_code", 0)
|
||||||
|
var error_code = data.get("error_code", "")
|
||||||
|
|
||||||
|
print("\n--- 特殊情况检查 ---")
|
||||||
|
|
||||||
|
# 检查409冲突
|
||||||
|
if response_code == 409:
|
||||||
|
print("✅ 检测到409冲突状态码 - 邮箱冲突检测正常工作")
|
||||||
|
|
||||||
|
# 检查206测试模式
|
||||||
|
if response_code == 206 or error_code == "TEST_MODE_ONLY":
|
||||||
|
print("✅ 检测到206测试模式 - 测试模式处理正常工作")
|
||||||
|
if data.has("data") and data.data.has("verification_code"):
|
||||||
|
print("🔑 测试模式验证码: ", data.data.verification_code)
|
||||||
|
|
||||||
|
# 检查429频率限制
|
||||||
|
if response_code == 429 or error_code == "TOO_MANY_REQUESTS":
|
||||||
|
print("✅ 检测到429频率限制 - 频率限制处理正常工作")
|
||||||
|
if data.has("throttle_info"):
|
||||||
|
print("⏰ 限制信息: ", data.throttle_info)
|
||||||
|
|
||||||
|
# 检查其他重要状态码
|
||||||
|
match response_code:
|
||||||
|
200:
|
||||||
|
print("✅ 200 成功响应")
|
||||||
|
201:
|
||||||
|
print("✅ 201 创建成功")
|
||||||
|
400:
|
||||||
|
print("⚠️ 400 请求参数错误")
|
||||||
|
401:
|
||||||
|
print("⚠️ 401 认证失败")
|
||||||
|
404:
|
||||||
|
print("⚠️ 404 资源不存在")
|
||||||
|
500:
|
||||||
|
print("❌ 500 服务器内部错误")
|
||||||
|
503:
|
||||||
|
print("❌ 503 服务不可用")
|
||||||
|
|
||||||
|
# 手动触发测试的方法
|
||||||
|
func test_email_conflict():
|
||||||
|
print("\n🧪 手动测试邮箱冲突检测...")
|
||||||
|
var callback = func(success: bool, data: Dictionary, error_info: Dictionary):
|
||||||
|
handle_test_response("邮箱冲突测试", "send_code", success, data, error_info)
|
||||||
|
|
||||||
|
NetworkManager.send_email_verification("existing@example.com", callback)
|
||||||
|
|
||||||
|
func test_rate_limit():
|
||||||
|
print("\n🧪 手动测试频率限制...")
|
||||||
|
var callback = func(success: bool, data: Dictionary, error_info: Dictionary):
|
||||||
|
handle_test_response("频率限制测试", "send_code", success, data, error_info)
|
||||||
|
|
||||||
|
# 快速发送多个请求来触发频率限制
|
||||||
|
for i in range(3):
|
||||||
|
NetworkManager.send_email_verification("test@example.com", callback)
|
||||||
|
await get_tree().create_timer(0.1).timeout
|
||||||
|
|
||||||
|
func test_test_mode():
|
||||||
|
print("\n🧪 手动测试测试模式...")
|
||||||
|
var callback = func(success: bool, data: Dictionary, error_info: Dictionary):
|
||||||
|
handle_test_response("测试模式测试", "send_code", success, data, error_info)
|
||||||
|
|
||||||
|
NetworkManager.send_email_verification("testmode@example.com", callback)
|
||||||
1
scripts/network/ApiTestScript.gd.uid
Normal file
1
scripts/network/ApiTestScript.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://4gamylhvy4nn
|
||||||
1
scripts/web_font_handler.gd.uid
Normal file
1
scripts/web_font_handler.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dvgsil07gh4tl
|
||||||
@@ -4,7 +4,39 @@
|
|||||||
|
|
||||||
## 测试脚本说明
|
## 测试脚本说明
|
||||||
|
|
||||||
### 1. `simple_api_test.py` - 简化版测试
|
### 1. `quick_test.py` - 快速测试(推荐)
|
||||||
|
快速验证API接口的基本功能,适合日常检查。
|
||||||
|
|
||||||
|
**使用方法:**
|
||||||
|
```bash
|
||||||
|
python tests/api/quick_test.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**测试内容:**
|
||||||
|
- ✅ 应用状态检查
|
||||||
|
- ✅ 发送邮箱验证码
|
||||||
|
- ✅ 发送验证码(无效邮箱)
|
||||||
|
- ✅ 用户登录
|
||||||
|
- ✅ 用户注册(无邮箱)
|
||||||
|
- ✅ 发送登录验证码
|
||||||
|
|
||||||
|
### 2. `api_client_test.py` - 完整测试套件
|
||||||
|
全面的API接口测试,包含所有业务流程和错误场景。
|
||||||
|
|
||||||
|
**使用方法:**
|
||||||
|
```bash
|
||||||
|
python tests/api/api_client_test.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**测试内容:**
|
||||||
|
- 🔄 完整的邮箱验证流程
|
||||||
|
- 🔄 用户注册流程(带邮箱验证)
|
||||||
|
- 🔄 用户登录流程(密码+验证码)
|
||||||
|
- 🔄 密码重置流程
|
||||||
|
- 🔄 错误场景测试
|
||||||
|
- 🔄 频率限制测试
|
||||||
|
|
||||||
|
### 3. `simple_api_test.py` - 简化版测试
|
||||||
快速验证API接口的基本连通性和功能。
|
快速验证API接口的基本连通性和功能。
|
||||||
|
|
||||||
**使用方法:**
|
**使用方法:**
|
||||||
@@ -24,7 +56,7 @@ python tests/api/simple_api_test.py http://localhost:3000
|
|||||||
- ✅ 无效登录测试
|
- ✅ 无效登录测试
|
||||||
- ✅ 管理员登录测试
|
- ✅ 管理员登录测试
|
||||||
|
|
||||||
### 2. `api_test.py` - 完整版测试
|
### 4. `api_test.py` - 完整版测试
|
||||||
全面的API接口测试,包括边界条件和错误处理。
|
全面的API接口测试,包括边界条件和错误处理。
|
||||||
|
|
||||||
**使用方法:**
|
**使用方法:**
|
||||||
@@ -143,8 +175,19 @@ tester.run_basic_tests()
|
|||||||
|
|
||||||
## 依赖要求
|
## 依赖要求
|
||||||
|
|
||||||
|
### 安装依赖
|
||||||
```bash
|
```bash
|
||||||
|
# 安装Python依赖
|
||||||
|
pip install -r tests/api/requirements.txt
|
||||||
|
|
||||||
|
# 或者手动安装
|
||||||
pip install requests
|
pip install requests
|
||||||
```
|
```
|
||||||
|
|
||||||
测试脚本使用Python标准库和requests库,无需额外依赖。
|
### 依赖包说明
|
||||||
|
- **requests** - HTTP请求库,用于发送API请求
|
||||||
|
- **json** - JSON数据处理(Python标准库)
|
||||||
|
- **time** - 时间处理(Python标准库)
|
||||||
|
- **sys** - 系统功能(Python标准库)
|
||||||
|
|
||||||
|
测试脚本主要使用Python标准库和requests库,依赖最小化。
|
||||||
427
tests/api/api_client_test.py
Normal file
427
tests/api/api_client_test.py
Normal file
@@ -0,0 +1,427 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
API客户端测试脚本
|
||||||
|
用于在没有Godot引擎的情况下测试后端API接口
|
||||||
|
|
||||||
|
使用方法:
|
||||||
|
python api_client_test.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
from typing import Dict, Any, Optional
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# API配置
|
||||||
|
API_BASE_URL = "https://whaletownend.xinghangee.icu"
|
||||||
|
DEFAULT_TIMEOUT = 30
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TestResult:
|
||||||
|
"""测试结果数据类"""
|
||||||
|
success: bool
|
||||||
|
message: str
|
||||||
|
response_code: int
|
||||||
|
response_data: Dict[str, Any]
|
||||||
|
error_info: Optional[Dict[str, Any]] = None
|
||||||
|
execution_time: float = 0.0
|
||||||
|
|
||||||
|
class APIClient:
|
||||||
|
"""API客户端类"""
|
||||||
|
|
||||||
|
def __init__(self, base_url: str = API_BASE_URL):
|
||||||
|
self.base_url = base_url
|
||||||
|
self.session = requests.Session()
|
||||||
|
self.session.headers.update({
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'User-Agent': 'WhaleTown-API-Test/1.0'
|
||||||
|
})
|
||||||
|
|
||||||
|
def _make_request(self, method: str, endpoint: str, data: Optional[Dict] = None,
|
||||||
|
timeout: int = DEFAULT_TIMEOUT) -> TestResult:
|
||||||
|
"""发送HTTP请求"""
|
||||||
|
url = f"{self.base_url}{endpoint}"
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(f"🚀 发送 {method} 请求到: {url}")
|
||||||
|
if data:
|
||||||
|
print(f"📦 请求数据: {json.dumps(data, ensure_ascii=False, indent=2)}")
|
||||||
|
|
||||||
|
response = self.session.request(
|
||||||
|
method=method,
|
||||||
|
url=url,
|
||||||
|
json=data if data else None,
|
||||||
|
timeout=timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
execution_time = time.time() - start_time
|
||||||
|
|
||||||
|
print(f"📊 响应状态码: {response.status_code}")
|
||||||
|
print(f"⏱️ 执行时间: {execution_time:.3f}s")
|
||||||
|
|
||||||
|
# 尝试解析JSON响应
|
||||||
|
try:
|
||||||
|
response_data = response.json()
|
||||||
|
print(f"📄 响应数据: {json.dumps(response_data, ensure_ascii=False, indent=2)}")
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
response_data = {"raw_response": response.text}
|
||||||
|
print(f"📄 原始响应: {response.text}")
|
||||||
|
|
||||||
|
# 判断请求是否成功
|
||||||
|
is_success = False
|
||||||
|
if 200 <= response.status_code < 300:
|
||||||
|
# HTTP成功状态码
|
||||||
|
business_success = response_data.get("success", True)
|
||||||
|
if business_success:
|
||||||
|
is_success = True
|
||||||
|
elif response.status_code == 206 and response_data.get("error_code") == "TEST_MODE_ONLY":
|
||||||
|
# 测试模式也算成功
|
||||||
|
is_success = True
|
||||||
|
|
||||||
|
return TestResult(
|
||||||
|
success=is_success,
|
||||||
|
message=response_data.get("message", ""),
|
||||||
|
response_code=response.status_code,
|
||||||
|
response_data=response_data,
|
||||||
|
execution_time=execution_time
|
||||||
|
)
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
execution_time = time.time() - start_time
|
||||||
|
return TestResult(
|
||||||
|
success=False,
|
||||||
|
message="请求超时",
|
||||||
|
response_code=0,
|
||||||
|
response_data={},
|
||||||
|
error_info={"error_type": "TIMEOUT"},
|
||||||
|
execution_time=execution_time
|
||||||
|
)
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
execution_time = time.time() - start_time
|
||||||
|
return TestResult(
|
||||||
|
success=False,
|
||||||
|
message="网络连接失败",
|
||||||
|
response_code=0,
|
||||||
|
response_data={},
|
||||||
|
error_info={"error_type": "CONNECTION_ERROR"},
|
||||||
|
execution_time=execution_time
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
execution_time = time.time() - start_time
|
||||||
|
return TestResult(
|
||||||
|
success=False,
|
||||||
|
message=f"请求异常: {str(e)}",
|
||||||
|
response_code=0,
|
||||||
|
response_data={},
|
||||||
|
error_info={"error_type": "UNKNOWN_ERROR", "details": str(e)},
|
||||||
|
execution_time=execution_time
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_app_status(self) -> TestResult:
|
||||||
|
"""获取应用状态"""
|
||||||
|
return self._make_request("GET", "/")
|
||||||
|
|
||||||
|
def login(self, identifier: str, password: str) -> TestResult:
|
||||||
|
"""用户登录"""
|
||||||
|
data = {
|
||||||
|
"identifier": identifier,
|
||||||
|
"password": password
|
||||||
|
}
|
||||||
|
return self._make_request("POST", "/auth/login", data)
|
||||||
|
|
||||||
|
def register(self, username: str, password: str, nickname: str,
|
||||||
|
email: str = "", email_verification_code: str = "") -> TestResult:
|
||||||
|
"""用户注册"""
|
||||||
|
data = {
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
"nickname": nickname
|
||||||
|
}
|
||||||
|
|
||||||
|
if email:
|
||||||
|
data["email"] = email
|
||||||
|
if email_verification_code:
|
||||||
|
data["email_verification_code"] = email_verification_code
|
||||||
|
|
||||||
|
return self._make_request("POST", "/auth/register", data)
|
||||||
|
|
||||||
|
def send_email_verification(self, email: str) -> TestResult:
|
||||||
|
"""发送邮箱验证码"""
|
||||||
|
data = {"email": email}
|
||||||
|
return self._make_request("POST", "/auth/send-email-verification", data)
|
||||||
|
|
||||||
|
def verify_email(self, email: str, verification_code: str) -> TestResult:
|
||||||
|
"""验证邮箱验证码"""
|
||||||
|
data = {
|
||||||
|
"email": email,
|
||||||
|
"verification_code": verification_code
|
||||||
|
}
|
||||||
|
return self._make_request("POST", "/auth/verify-email", data)
|
||||||
|
|
||||||
|
def send_login_verification_code(self, identifier: str) -> TestResult:
|
||||||
|
"""发送登录验证码"""
|
||||||
|
data = {"identifier": identifier}
|
||||||
|
return self._make_request("POST", "/auth/send-login-verification-code", data)
|
||||||
|
|
||||||
|
def verification_code_login(self, identifier: str, verification_code: str) -> TestResult:
|
||||||
|
"""验证码登录"""
|
||||||
|
data = {
|
||||||
|
"identifier": identifier,
|
||||||
|
"verification_code": verification_code
|
||||||
|
}
|
||||||
|
return self._make_request("POST", "/auth/verification-code-login", data)
|
||||||
|
|
||||||
|
def forgot_password(self, identifier: str) -> TestResult:
|
||||||
|
"""忘记密码 - 发送重置验证码"""
|
||||||
|
data = {"identifier": identifier}
|
||||||
|
return self._make_request("POST", "/auth/forgot-password", data)
|
||||||
|
|
||||||
|
def reset_password(self, identifier: str, verification_code: str, new_password: str) -> TestResult:
|
||||||
|
"""重置密码"""
|
||||||
|
data = {
|
||||||
|
"identifier": identifier,
|
||||||
|
"verification_code": verification_code,
|
||||||
|
"new_password": new_password
|
||||||
|
}
|
||||||
|
return self._make_request("POST", "/auth/reset-password", data)
|
||||||
|
|
||||||
|
class APITester:
|
||||||
|
"""API测试器"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.client = APIClient()
|
||||||
|
self.test_results = []
|
||||||
|
self.test_email = "test@example.com"
|
||||||
|
self.test_username = "testuser"
|
||||||
|
self.test_password = "password123"
|
||||||
|
self.verification_code = None
|
||||||
|
|
||||||
|
def print_header(self, title: str):
|
||||||
|
"""打印测试标题"""
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print(f"🧪 {title}")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
def print_result(self, test_name: str, result: TestResult):
|
||||||
|
"""打印测试结果"""
|
||||||
|
status = "✅ 成功" if result.success else "❌ 失败"
|
||||||
|
print(f"\n📋 测试: {test_name}")
|
||||||
|
print(f"🎯 结果: {status}")
|
||||||
|
print(f"💬 消息: {result.message}")
|
||||||
|
print(f"📊 状态码: {result.response_code}")
|
||||||
|
print(f"⏱️ 耗时: {result.execution_time:.3f}s")
|
||||||
|
|
||||||
|
# 检查特殊情况
|
||||||
|
if result.response_code == 206:
|
||||||
|
print("🧪 检测到测试模式响应")
|
||||||
|
elif result.response_code == 409:
|
||||||
|
print("⚠️ 检测到资源冲突")
|
||||||
|
elif result.response_code == 429:
|
||||||
|
print("⏰ 检测到频率限制")
|
||||||
|
|
||||||
|
# 提取验证码(如果有)
|
||||||
|
if result.response_data.get("data", {}).get("verification_code"):
|
||||||
|
self.verification_code = result.response_data["data"]["verification_code"]
|
||||||
|
print(f"🔑 获取到验证码: {self.verification_code}")
|
||||||
|
|
||||||
|
self.test_results.append((test_name, result))
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
def test_app_status(self):
|
||||||
|
"""测试应用状态"""
|
||||||
|
self.print_header("应用状态测试")
|
||||||
|
result = self.client.get_app_status()
|
||||||
|
self.print_result("获取应用状态", result)
|
||||||
|
|
||||||
|
def test_email_verification_flow(self):
|
||||||
|
"""测试邮箱验证流程"""
|
||||||
|
self.print_header("邮箱验证流程测试")
|
||||||
|
|
||||||
|
# 1. 发送邮箱验证码
|
||||||
|
result = self.client.send_email_verification(self.test_email)
|
||||||
|
self.print_result("发送邮箱验证码", result)
|
||||||
|
|
||||||
|
if self.verification_code:
|
||||||
|
# 2. 验证邮箱验证码
|
||||||
|
result = self.client.verify_email(self.test_email, self.verification_code)
|
||||||
|
self.print_result("验证邮箱验证码", result)
|
||||||
|
|
||||||
|
def test_registration_flow(self):
|
||||||
|
"""测试注册流程"""
|
||||||
|
self.print_header("用户注册流程测试")
|
||||||
|
|
||||||
|
# 使用之前获取的验证码进行注册
|
||||||
|
if self.verification_code:
|
||||||
|
result = self.client.register(
|
||||||
|
username=self.test_username,
|
||||||
|
password=self.test_password,
|
||||||
|
nickname="测试用户",
|
||||||
|
email=self.test_email,
|
||||||
|
email_verification_code=self.verification_code
|
||||||
|
)
|
||||||
|
self.print_result("用户注册(带邮箱验证)", result)
|
||||||
|
else:
|
||||||
|
# 无邮箱注册
|
||||||
|
result = self.client.register(
|
||||||
|
username=f"{self.test_username}_no_email",
|
||||||
|
password=self.test_password,
|
||||||
|
nickname="测试用户(无邮箱)"
|
||||||
|
)
|
||||||
|
self.print_result("用户注册(无邮箱)", result)
|
||||||
|
|
||||||
|
def test_login_flow(self):
|
||||||
|
"""测试登录流程"""
|
||||||
|
self.print_header("用户登录流程测试")
|
||||||
|
|
||||||
|
# 1. 密码登录
|
||||||
|
result = self.client.login(self.test_username, self.test_password)
|
||||||
|
self.print_result("密码登录", result)
|
||||||
|
|
||||||
|
# 2. 发送登录验证码
|
||||||
|
result = self.client.send_login_verification_code(self.test_email)
|
||||||
|
self.print_result("发送登录验证码", result)
|
||||||
|
|
||||||
|
# 3. 验证码登录
|
||||||
|
if self.verification_code:
|
||||||
|
result = self.client.verification_code_login(self.test_email, self.verification_code)
|
||||||
|
self.print_result("验证码登录", result)
|
||||||
|
|
||||||
|
def test_password_reset_flow(self):
|
||||||
|
"""测试密码重置流程"""
|
||||||
|
self.print_header("密码重置流程测试")
|
||||||
|
|
||||||
|
# 1. 发送重置验证码
|
||||||
|
result = self.client.forgot_password(self.test_email)
|
||||||
|
self.print_result("发送重置验证码", result)
|
||||||
|
|
||||||
|
# 2. 重置密码
|
||||||
|
if self.verification_code:
|
||||||
|
result = self.client.reset_password(
|
||||||
|
identifier=self.test_email,
|
||||||
|
verification_code=self.verification_code,
|
||||||
|
new_password="newpassword123"
|
||||||
|
)
|
||||||
|
self.print_result("重置密码", result)
|
||||||
|
|
||||||
|
def test_error_scenarios(self):
|
||||||
|
"""测试错误场景"""
|
||||||
|
self.print_header("错误场景测试")
|
||||||
|
|
||||||
|
# 1. 无效邮箱格式
|
||||||
|
result = self.client.send_email_verification("invalid-email")
|
||||||
|
self.print_result("无效邮箱格式", result)
|
||||||
|
|
||||||
|
# 2. 错误的验证码
|
||||||
|
result = self.client.verify_email(self.test_email, "000000")
|
||||||
|
self.print_result("错误验证码", result)
|
||||||
|
|
||||||
|
# 3. 用户名冲突
|
||||||
|
result = self.client.register(
|
||||||
|
username=self.test_username, # 重复用户名
|
||||||
|
password=self.test_password,
|
||||||
|
nickname="重复用户"
|
||||||
|
)
|
||||||
|
self.print_result("用户名冲突", result)
|
||||||
|
|
||||||
|
# 4. 错误的登录凭据
|
||||||
|
result = self.client.login("nonexistent", "wrongpassword")
|
||||||
|
self.print_result("错误登录凭据", result)
|
||||||
|
|
||||||
|
def test_rate_limiting(self):
|
||||||
|
"""测试频率限制"""
|
||||||
|
self.print_header("频率限制测试")
|
||||||
|
|
||||||
|
print("🔄 快速发送多个验证码请求以触发频率限制...")
|
||||||
|
for i in range(3):
|
||||||
|
result = self.client.send_email_verification(f"test{i}@example.com")
|
||||||
|
self.print_result(f"验证码请求 #{i+1}", result)
|
||||||
|
|
||||||
|
if result.response_code == 429:
|
||||||
|
print("✅ 成功触发频率限制")
|
||||||
|
break
|
||||||
|
|
||||||
|
time.sleep(0.5) # 短暂延迟
|
||||||
|
|
||||||
|
def run_all_tests(self):
|
||||||
|
"""运行所有测试"""
|
||||||
|
print("🎯 开始API接口测试")
|
||||||
|
print(f"🌐 测试服务器: {API_BASE_URL}")
|
||||||
|
print(f"📧 测试邮箱: {self.test_email}")
|
||||||
|
print(f"👤 测试用户名: {self.test_username}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 基础测试
|
||||||
|
self.test_app_status()
|
||||||
|
|
||||||
|
# 邮箱验证流程
|
||||||
|
self.test_email_verification_flow()
|
||||||
|
|
||||||
|
# 注册流程
|
||||||
|
self.test_registration_flow()
|
||||||
|
|
||||||
|
# 登录流程
|
||||||
|
self.test_login_flow()
|
||||||
|
|
||||||
|
# 密码重置流程
|
||||||
|
self.test_password_reset_flow()
|
||||||
|
|
||||||
|
# 错误场景测试
|
||||||
|
self.test_error_scenarios()
|
||||||
|
|
||||||
|
# 频率限制测试
|
||||||
|
self.test_rate_limiting()
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n⚠️ 测试被用户中断")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n❌ 测试过程中发生异常: {e}")
|
||||||
|
finally:
|
||||||
|
self.print_summary()
|
||||||
|
|
||||||
|
def print_summary(self):
|
||||||
|
"""打印测试总结"""
|
||||||
|
self.print_header("测试总结")
|
||||||
|
|
||||||
|
total_tests = len(self.test_results)
|
||||||
|
successful_tests = sum(1 for _, result in self.test_results if result.success)
|
||||||
|
failed_tests = total_tests - successful_tests
|
||||||
|
|
||||||
|
print(f"📊 总测试数: {total_tests}")
|
||||||
|
print(f"✅ 成功: {successful_tests}")
|
||||||
|
print(f"❌ 失败: {failed_tests}")
|
||||||
|
print(f"📈 成功率: {(successful_tests/total_tests*100):.1f}%" if total_tests > 0 else "0%")
|
||||||
|
|
||||||
|
if failed_tests > 0:
|
||||||
|
print("\n❌ 失败的测试:")
|
||||||
|
for test_name, result in self.test_results:
|
||||||
|
if not result.success:
|
||||||
|
print(f" • {test_name}: {result.message} (状态码: {result.response_code})")
|
||||||
|
|
||||||
|
print(f"\n⏱️ 总执行时间: {sum(result.execution_time for _, result in self.test_results):.3f}s")
|
||||||
|
print("🎉 测试完成!")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
print("🐋 WhaleTown API 接口测试工具")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# 检查网络连接
|
||||||
|
try:
|
||||||
|
response = requests.get(API_BASE_URL, timeout=5)
|
||||||
|
print(f"✅ 服务器连接正常 (状态码: {response.status_code})")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 无法连接到服务器: {e}")
|
||||||
|
print("请检查网络连接或服务器地址")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 运行测试
|
||||||
|
tester = APITester()
|
||||||
|
tester.run_all_tests()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
150
tests/api/quick_test.py
Normal file
150
tests/api/quick_test.py
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
快速API测试脚本
|
||||||
|
用于快速验证API接口的基本功能
|
||||||
|
|
||||||
|
使用方法:
|
||||||
|
python quick_test.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# API配置
|
||||||
|
API_BASE_URL = "https://whaletownend.xinghangee.icu"
|
||||||
|
|
||||||
|
def test_api_endpoint(method, endpoint, data=None, description=""):
|
||||||
|
"""测试单个API端点"""
|
||||||
|
url = f"{API_BASE_URL}{endpoint}"
|
||||||
|
|
||||||
|
print(f"\n🧪 测试: {description}")
|
||||||
|
print(f"📡 {method} {url}")
|
||||||
|
|
||||||
|
if data:
|
||||||
|
print(f"📦 数据: {json.dumps(data, ensure_ascii=False)}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
if method == "GET":
|
||||||
|
response = requests.get(url, timeout=10)
|
||||||
|
elif method == "POST":
|
||||||
|
response = requests.post(url, json=data, timeout=10)
|
||||||
|
else:
|
||||||
|
print(f"❌ 不支持的方法: {method}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"📊 状态码: {response.status_code}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response_data = response.json()
|
||||||
|
print(f"📄 响应: {json.dumps(response_data, ensure_ascii=False, indent=2)}")
|
||||||
|
|
||||||
|
# 检查特殊状态码
|
||||||
|
if response.status_code == 206:
|
||||||
|
print("🧪 测试模式响应")
|
||||||
|
if response_data.get("data", {}).get("verification_code"):
|
||||||
|
print(f"🔑 验证码: {response_data['data']['verification_code']}")
|
||||||
|
elif response.status_code == 409:
|
||||||
|
print("⚠️ 资源冲突")
|
||||||
|
elif response.status_code == 429:
|
||||||
|
print("⏰ 频率限制")
|
||||||
|
|
||||||
|
return 200 <= response.status_code < 300 or response.status_code == 206
|
||||||
|
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print(f"📄 原始响应: {response.text}")
|
||||||
|
return 200 <= response.status_code < 300
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
print("❌ 请求超时")
|
||||||
|
return False
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
print("❌ 连接失败")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 请求异常: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
print("🐋 WhaleTown API 快速测试")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
# 测试用例
|
||||||
|
tests = [
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"endpoint": "/",
|
||||||
|
"description": "获取应用状态"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"endpoint": "/auth/send-email-verification",
|
||||||
|
"data": {"email": "test@example.com"},
|
||||||
|
"description": "发送邮箱验证码"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"endpoint": "/auth/send-email-verification",
|
||||||
|
"data": {"email": "invalid-email"},
|
||||||
|
"description": "发送验证码(无效邮箱)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"endpoint": "/auth/login",
|
||||||
|
"data": {"identifier": "testuser", "password": "password123"},
|
||||||
|
"description": "用户登录"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"endpoint": "/auth/register",
|
||||||
|
"data": {
|
||||||
|
"username": "testuser_quick",
|
||||||
|
"password": "password123",
|
||||||
|
"nickname": "快速测试用户"
|
||||||
|
},
|
||||||
|
"description": "用户注册(无邮箱)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"endpoint": "/auth/send-login-verification-code",
|
||||||
|
"data": {"identifier": "test@example.com"},
|
||||||
|
"description": "发送登录验证码"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
# 执行测试
|
||||||
|
success_count = 0
|
||||||
|
total_count = len(tests)
|
||||||
|
|
||||||
|
for test in tests:
|
||||||
|
success = test_api_endpoint(
|
||||||
|
method=test["method"],
|
||||||
|
endpoint=test["endpoint"],
|
||||||
|
data=test.get("data"),
|
||||||
|
description=test["description"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
success_count += 1
|
||||||
|
print("✅ 测试通过")
|
||||||
|
else:
|
||||||
|
print("❌ 测试失败")
|
||||||
|
|
||||||
|
print("-" * 30)
|
||||||
|
|
||||||
|
# 打印总结
|
||||||
|
print(f"\n📊 测试总结:")
|
||||||
|
print(f"总测试数: {total_count}")
|
||||||
|
print(f"成功: {success_count}")
|
||||||
|
print(f"失败: {total_count - success_count}")
|
||||||
|
print(f"成功率: {(success_count/total_count*100):.1f}%")
|
||||||
|
|
||||||
|
if success_count == total_count:
|
||||||
|
print("🎉 所有测试通过!")
|
||||||
|
else:
|
||||||
|
print("⚠️ 部分测试失败,请检查API服务")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
2
tests/api/requirements.txt
Normal file
2
tests/api/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Python API测试依赖包
|
||||||
|
requests>=2.25.0
|
||||||
55
tests/api/run_tests.bat
Normal file
55
tests/api/run_tests.bat
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
echo 🐋 WhaleTown API 测试工具
|
||||||
|
echo ========================
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo 请选择要运行的测试:
|
||||||
|
echo 1. 快速测试 (推荐)
|
||||||
|
echo 2. 完整测试套件
|
||||||
|
echo 3. 简单连接测试
|
||||||
|
echo 4. 安装依赖
|
||||||
|
echo 5. 退出
|
||||||
|
echo.
|
||||||
|
|
||||||
|
set /p choice=请输入选择 (1-5):
|
||||||
|
|
||||||
|
if "%choice%"=="1" (
|
||||||
|
echo.
|
||||||
|
echo 🚀 运行快速测试...
|
||||||
|
python quick_test.py
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%choice%"=="2" (
|
||||||
|
echo.
|
||||||
|
echo 🚀 运行完整测试套件...
|
||||||
|
python api_client_test.py
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%choice%"=="3" (
|
||||||
|
echo.
|
||||||
|
echo 🚀 运行简单连接测试...
|
||||||
|
python simple_api_test.py
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%choice%"=="4" (
|
||||||
|
echo.
|
||||||
|
echo 📦 安装Python依赖...
|
||||||
|
pip install -r requirements.txt
|
||||||
|
echo 依赖安装完成!
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%choice%"=="5" (
|
||||||
|
echo 👋 再见!
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ❌ 无效选择,请重新运行脚本
|
||||||
|
|
||||||
|
:end
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
71
tests/api/run_tests.sh
Normal file
71
tests/api/run_tests.sh
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# WhaleTown API 测试工具
|
||||||
|
echo "🐋 WhaleTown API 测试工具"
|
||||||
|
echo "========================"
|
||||||
|
|
||||||
|
# 检查Python是否安装
|
||||||
|
if ! command -v python3 &> /dev/null; then
|
||||||
|
echo "❌ 错误: 未找到 python3,请先安装Python"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查是否在正确的目录
|
||||||
|
if [ ! -f "quick_test.py" ]; then
|
||||||
|
echo "❌ 错误: 请在 tests/api 目录下运行此脚本"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "请选择要运行的测试:"
|
||||||
|
echo "1. 快速测试 (推荐)"
|
||||||
|
echo "2. 完整测试套件"
|
||||||
|
echo "3. 简单连接测试"
|
||||||
|
echo "4. 安装依赖"
|
||||||
|
echo "5. 退出"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -p "请输入选择 (1-5): " choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
1)
|
||||||
|
echo ""
|
||||||
|
echo "🚀 运行快速测试..."
|
||||||
|
python3 quick_test.py
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
echo ""
|
||||||
|
echo "🚀 运行完整测试套件..."
|
||||||
|
python3 api_client_test.py
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
echo ""
|
||||||
|
echo "🚀 运行简单连接测试..."
|
||||||
|
python3 simple_api_test.py
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
echo ""
|
||||||
|
echo "📦 安装Python依赖..."
|
||||||
|
if command -v pip3 &> /dev/null; then
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
elif command -v pip &> /dev/null; then
|
||||||
|
pip install -r requirements.txt
|
||||||
|
else
|
||||||
|
echo "❌ 错误: 未找到 pip,请手动安装依赖"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✅ 依赖安装完成!"
|
||||||
|
;;
|
||||||
|
5)
|
||||||
|
echo "👋 再见!"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "❌ 无效选择,请重新运行脚本"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "测试完成!按任意键继续..."
|
||||||
|
read -n 1
|
||||||
Reference in New Issue
Block a user