2534 lines
99 KiB
Python
2534 lines
99 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
整合后端接口性能测试工具
|
||
整合了所有10个模块的接口测试:
|
||
1. 基础接口 (flask_wht_server_api.py)
|
||
2. 认证接口 (auth_routes.py)
|
||
3. 工作台接口 (dashboard_routes.py)
|
||
4. 企业接口 (enterprise_routes.py)
|
||
5. 图片接口 (image_routes.py)
|
||
6. 日志接口 (log_routes.py)
|
||
7. 文章接口 (article_routes.py)
|
||
8. 员工接口 (employee_routes.py)
|
||
9. 作者接口 (author_routes.py)
|
||
10. 搜索服务接口 (flask_wht_server_search.py)
|
||
"""
|
||
import requests
|
||
import time
|
||
import csv
|
||
from datetime import datetime
|
||
import io
|
||
from PIL import Image
|
||
import sys
|
||
|
||
# API配置
|
||
API_BASE_URL = "http://127.0.0.1:8216" # 主服务
|
||
SEARCH_API_BASE_URL = "http://127.0.0.1:8321" # 搜索服务
|
||
USERNAME = "13621242430"
|
||
PASSWORD = "admin123"
|
||
|
||
# 测试结果存储
|
||
test_results = []
|
||
api_docs = []
|
||
|
||
# 全局token
|
||
global_token = None
|
||
|
||
# 测试模块选择(可配置)
|
||
TEST_MODULES = {
|
||
'basic': True, # 基础接口
|
||
'auth': True, # 认证接口
|
||
'dashboard': True, # 工作台
|
||
'enterprise': True, # 企业
|
||
'image': True, # 图片
|
||
'log': True, # 日志
|
||
'article': True, # 文章
|
||
'employee': True, # 员工
|
||
'author': True, # 作者
|
||
'search': False, # 搜索服务(默认关闭,需要单独服务)
|
||
}
|
||
|
||
def print_module_header(module_name, module_num, total_modules):
|
||
"""打印模块测试头部"""
|
||
print("\n" + "="*80)
|
||
print(f"[{module_num}/{total_modules}] {module_name}")
|
||
print("="*80)
|
||
|
||
def login():
|
||
"""登录获取token"""
|
||
global global_token
|
||
|
||
print("\n" + "="*80)
|
||
print("正在登录...")
|
||
url = f"{API_BASE_URL}/api/auth/login"
|
||
data = {"username": USERNAME, "password": PASSWORD}
|
||
|
||
start_time = time.time()
|
||
response = requests.post(url, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
|
||
if response.status_code == 200:
|
||
global_token = response.json()['data']['token']
|
||
print(f"✓ 登录成功,耗时: {elapsed_time:.2f}ms")
|
||
return global_token
|
||
else:
|
||
print(f"✗ 登录失败: {response.status_code}")
|
||
return None
|
||
|
||
# ============================================================================
|
||
# 模块1: 基础接口测试
|
||
# ============================================================================
|
||
def test_basic_apis():
|
||
"""测试基础接口"""
|
||
if not TEST_MODULES['basic']:
|
||
return
|
||
|
||
print_module_header("基础接口测试", 1, 10)
|
||
|
||
# 根路径
|
||
print("\n[1/3] 测试根路径...")
|
||
url = f"{API_BASE_URL}/"
|
||
for i in range(3):
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '基础接口',
|
||
'endpoint': '/',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '基础接口',
|
||
'endpoint': '/',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 健康检查
|
||
print("[2/3] 测试健康检查...")
|
||
url = f"{API_BASE_URL}/health"
|
||
for i in range(3):
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '基础接口',
|
||
'endpoint': '/health',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '基础接口',
|
||
'endpoint': '/health',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 404测试
|
||
print("[3/3] 测试404错误处理...")
|
||
url = f"{API_BASE_URL}/nonexistent-test"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '基础接口',
|
||
'endpoint': '/nonexistent-test',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 404,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '基础接口',
|
||
'endpoint': '/nonexistent-test',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 添加API文档
|
||
api_docs.extend([
|
||
{'module': '基础接口', 'endpoint': '/', 'method': 'GET', 'description': '根路径', 'auth': '无'},
|
||
{'module': '基础接口', 'endpoint': '/health', 'method': 'GET', 'description': '健康检查', 'auth': '无'},
|
||
])
|
||
|
||
# ============================================================================
|
||
# 模块2: 认证接口测试 (原脚本测试数: 11个)
|
||
# ============================================================================
|
||
def test_auth_apis(token):
|
||
"""测试认证接口 - 包含统一登录、员工登录、登出、错误场景等"""
|
||
if not TEST_MODULES['auth']:
|
||
return
|
||
|
||
print_module_header("认证接口测试", 2, 10)
|
||
|
||
# 1. 统一登录 - 用户名
|
||
print("\n[1/11] 测试统一登录(用户名)...")
|
||
url = f"{API_BASE_URL}/api/auth/login"
|
||
data = {"username": USERNAME, "password": PASSWORD}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/login (用户名)',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/login (用户名)',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 2. 统一登录 - 手机号
|
||
print("[2/11] 测试统一登录(手机号)...")
|
||
data = {"phone": USERNAME, "password": PASSWORD}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/login (手机号)',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/login (手机号)',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 3. 统一登录 - 错误密码
|
||
print("[3/11] 测试统一登录(错误密码)...")
|
||
data = {"username": USERNAME, "password": "wrong_password"}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/login (错误密码)',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 401,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/login (错误密码)',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 4. 统一登录 - 缺少参数
|
||
print("[4/11] 测试统一登录(缺少参数)...")
|
||
data = {"username": USERNAME}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/login (缺少参数)',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 400,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/login (缺少参数)',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 5. 员工登录 - 正常
|
||
print("[5/11] 测试员工登录...")
|
||
url = f"{API_BASE_URL}/api/auth/employee/login"
|
||
data = {"phone": USERNAME, "password": PASSWORD}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/employee/login',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/employee/login',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 6. 员工登录 - 错误密码
|
||
print("[6/11] 测试员工登录(错误密码)...")
|
||
data = {"phone": USERNAME, "password": "wrong_password"}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/employee/login (错误密码)',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 401,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/employee/login (错误密码)',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 7. 登出 - 带token
|
||
print("[7/11] 测试用户登出(带token)...")
|
||
url = f"{API_BASE_URL}/api/auth/logout"
|
||
headers = {"Authorization": f"Bearer {token}"}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/logout (带token)',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/logout (带token)',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 8. 登出 - 不带token
|
||
print("[8/11] 测试用户登出(不带token)...")
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/logout (不带token)',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': '/api/auth/logout (不带token)',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 9-11. 并发登录测试
|
||
print("[9-11/11] 测试并发登录(3次)...")
|
||
url = f"{API_BASE_URL}/api/auth/login"
|
||
data = {"username": USERNAME, "password": PASSWORD}
|
||
import concurrent.futures
|
||
|
||
def single_login():
|
||
start_time = time.time()
|
||
response = requests.post(url, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
return response.status_code, elapsed_time
|
||
|
||
try:
|
||
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
|
||
futures = [executor.submit(single_login) for _ in range(3)]
|
||
results = [f.result() for f in concurrent.futures.as_completed(futures)]
|
||
|
||
for i, (status, elapsed) in enumerate(results, 1):
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': f'/api/auth/login (并发{i})',
|
||
'method': 'POST',
|
||
'status_code': status,
|
||
'elapsed_time_ms': f"{elapsed:.2f}",
|
||
'success': status == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
for i in range(3):
|
||
test_results.append({
|
||
'module': '认证接口',
|
||
'endpoint': f'/api/auth/login (并发{i+1})',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
api_docs.extend([
|
||
{'module': '认证接口', 'endpoint': '/api/auth/login', 'method': 'POST', 'description': '统一登录(支持用户名/手机号)', 'auth': '无'},
|
||
{'module': '认证接口', 'endpoint': '/api/auth/employee/login', 'method': 'POST', 'description': '员工登录', 'auth': '无'},
|
||
{'module': '认证接口', 'endpoint': '/api/auth/logout', 'method': 'POST', 'description': '用户登出', 'auth': '可选'},
|
||
])
|
||
|
||
# ============================================================================
|
||
# 模块3: 工作台接口测试 (原脚本测试数: 7个)
|
||
# ============================================================================
|
||
def test_dashboard_apis(token):
|
||
"""测试工作台接口 - 概览、最近发布、热门产品"""
|
||
if not TEST_MODULES['dashboard']:
|
||
return
|
||
|
||
print_module_header("工作台接口测试", 3, 10)
|
||
|
||
headers = {"Authorization": f"Bearer {token}"}
|
||
|
||
# 1-5. 工作台概览 (多次测试获取平均性能)
|
||
print("\n[1-5/9] 测试工作台概览(5次)...")
|
||
url = f"{API_BASE_URL}/api/dashboard/overview"
|
||
for i in range(5):
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '工作台',
|
||
'endpoint': f'/api/dashboard/overview (第{i+1}次)',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '工作台',
|
||
'endpoint': f'/api/dashboard/overview (第{i+1}次)',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 6-8. 最近发布 (不同limit参数)
|
||
print("[6-8/9] 测试最近发布(不同参数)...")
|
||
url = f"{API_BASE_URL}/api/dashboard/recent-publishes"
|
||
test_cases = [
|
||
{"limit": 5, "desc": "5条记录"},
|
||
{"limit": 10, "desc": "10条记录"},
|
||
{"limit": 20, "desc": "20条记录"},
|
||
]
|
||
|
||
for i, params in enumerate(test_cases, 1):
|
||
desc = params.pop('desc')
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '工作台',
|
||
'endpoint': f'/api/dashboard/recent-publishes ({desc})',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '工作台',
|
||
'endpoint': f'/api/dashboard/recent-publishes ({desc})',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 9. 热门产品
|
||
print("[9/9] 测试热门产品...")
|
||
url = f"{API_BASE_URL}/api/dashboard/hot-products"
|
||
test_cases = [
|
||
{"limit": 4, "desc": "4个产品"},
|
||
{"limit": 8, "desc": "8个产品"},
|
||
{"limit": 12, "desc": "12个产品"},
|
||
]
|
||
|
||
for i, params in enumerate(test_cases, 1):
|
||
desc = params.pop('desc')
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '工作台',
|
||
'endpoint': f'/api/dashboard/hot-products ({desc})',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '工作台',
|
||
'endpoint': f'/api/dashboard/hot-products ({desc})',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
api_docs.extend([
|
||
{'module': '工作台', 'endpoint': '/api/dashboard/overview', 'method': 'GET', 'description': '工作台概览(统计数据)', 'auth': '需要'},
|
||
{'module': '工作台', 'endpoint': '/api/dashboard/recent-publishes', 'method': 'GET', 'description': '最近发布(可指定条数)', 'auth': '需要'},
|
||
{'module': '工作台', 'endpoint': '/api/dashboard/hot-products', 'method': 'GET', 'description': '热门产品(按发布次数)', 'auth': '需要'},
|
||
])
|
||
|
||
# ============================================================================
|
||
# 模块4: 企业接口测试 (原脚本测试数: 15个)
|
||
# ============================================================================
|
||
def test_enterprise_apis(token):
|
||
"""测试企业接口 - 列表、统计、信息、CRUD操作"""
|
||
if not TEST_MODULES['enterprise']:
|
||
return
|
||
|
||
print_module_header("企业接口测试", 4, 10)
|
||
|
||
headers = {"Authorization": f"Bearer {token}"}
|
||
|
||
# 1-5. 企业列表 (不同查询参数)
|
||
print("\n[1-5/17] 测试企业列表(不同参数)...")
|
||
url = f"{API_BASE_URL}/api/enterprises/list"
|
||
test_cases = [
|
||
{"page": 1, "pageSize": 20, "desc": "基本查询"},
|
||
{"page": 1, "pageSize": 10, "desc": "每页10条"},
|
||
{"page": 1, "pageSize": 20, "keyword": "企业", "desc": "关键词搜索"},
|
||
{"page": 1, "pageSize": 20, "status": "active", "desc": "状态筛选"},
|
||
{"page": 2, "pageSize": 20, "desc": "第2页"},
|
||
]
|
||
|
||
for i, params in enumerate(test_cases, 1):
|
||
desc = params.pop('desc')
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '企业接口',
|
||
'endpoint': f'/api/enterprises/list ({desc})',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '企业接口',
|
||
'endpoint': f'/api/enterprises/list ({desc})',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 6-10. 企业统计 (多次测试)
|
||
print("[6-10/17] 测试企业统计(5次)...")
|
||
url = f"{API_BASE_URL}/api/enterprises/stats"
|
||
for i in range(5):
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '企业接口',
|
||
'endpoint': f'/api/enterprises/stats (第{i+1}次)',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '企业接口',
|
||
'endpoint': f'/api/enterprises/stats (第{i+1}次)',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 11-15. 企业信息 (多次测试)
|
||
print("[11-15/17] 测试企业信息(5次)...")
|
||
url = f"{API_BASE_URL}/api/enterprises/info"
|
||
for i in range(5):
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '企业接口',
|
||
'endpoint': f'/api/enterprises/info (第{i+1}次)',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '企业接口',
|
||
'endpoint': f'/api/enterprises/info (第{i+1}次)',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 16. 更新企业信息
|
||
print("[16/17] 测试更新企业信息...")
|
||
url = f"{API_BASE_URL}/api/enterprises/info"
|
||
data = {"short_name": f"测试更新{int(time.time())}", "email": "test@example.com"}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.put(url, headers=headers, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '企业接口',
|
||
'endpoint': '/api/enterprises/info (PUT)',
|
||
'method': 'PUT',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '企业接口',
|
||
'endpoint': '/api/enterprises/info (PUT)',
|
||
'method': 'PUT',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 17. 修改密码
|
||
print("[17/17] 测试修改密码...")
|
||
url = f"{API_BASE_URL}/api/enterprises/change-password"
|
||
data = {"old_password": "wrong", "new_password": "test123"}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.put(url, headers=headers, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '企业接口',
|
||
'endpoint': '/api/enterprises/change-password',
|
||
'method': 'PUT',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 400],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '企业接口',
|
||
'endpoint': '/api/enterprises/change-password',
|
||
'method': 'PUT',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
api_docs.extend([
|
||
{'module': '企业接口', 'endpoint': '/api/enterprises/list', 'method': 'GET', 'description': '企业列表(支持分页、搜索、筛选)', 'auth': '需要'},
|
||
{'module': '企业接口', 'endpoint': '/api/enterprises/stats', 'method': 'GET', 'description': '企业统计(总数、激活数等)', 'auth': '需要'},
|
||
{'module': '企业接口', 'endpoint': '/api/enterprises/info', 'method': 'GET', 'description': '企业信息', 'auth': '需要'},
|
||
{'module': '企业接口', 'endpoint': '/api/enterprises/info', 'method': 'PUT', 'description': '更新企业信息', 'auth': '需要'},
|
||
{'module': '企业接口', 'endpoint': '/api/enterprises/change-password', 'method': 'PUT', 'description': '修改密码', 'auth': '需要'},
|
||
])
|
||
|
||
# ============================================================================
|
||
# 模块5: 图片接口测试 (原脚本测试数: 17个)
|
||
# ============================================================================
|
||
def test_image_apis(token):
|
||
"""测试图片接口 - 图片、标签、类型管理"""
|
||
if not TEST_MODULES['image']:
|
||
return
|
||
|
||
print_module_header("图片接口测试", 5, 10)
|
||
|
||
headers = {"Authorization": f"Bearer {token}"}
|
||
|
||
# 1-2. 图片列表
|
||
print("\n[1-2/17] 测试图片列表(不同参数)...")
|
||
url = f"{API_BASE_URL}/api/images/list"
|
||
test_cases = [
|
||
{"page": 1, "pageSize": 20, "desc": "第1页"},
|
||
{"page": 1, "pageSize": 10, "desc": "每页10条"},
|
||
]
|
||
|
||
for i, params in enumerate(test_cases, 1):
|
||
desc = params.pop('desc')
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': f'/api/images/list ({desc})',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': f'/api/images/list ({desc})',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 3. 图片仪表盘
|
||
print("[3/17] 测试图片仪表盘...")
|
||
url = f"{API_BASE_URL}/api/images/list_dashboard"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': '/api/images/list_dashboard',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': '/api/images/list_dashboard',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 4. 上传图片
|
||
print("[4/17] 测试上传图片...")
|
||
url = f"{API_BASE_URL}/api/images/upload"
|
||
try:
|
||
img = Image.new('RGB', (100, 100))
|
||
img_bytes = io.BytesIO()
|
||
img.save(img_bytes, format='PNG')
|
||
img_bytes.seek(0)
|
||
|
||
files = {'image': ('test.png', img_bytes, 'image/png')}
|
||
data = {'image_name': '测试图片', 'image_type_name': '测试类型', 'tag_keywords': '测试,性能'}
|
||
|
||
start_time = time.time()
|
||
response = requests.post(url, headers=headers, files=files, data=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': '/api/images/upload',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 400],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': '/api/images/upload',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 5. 删除图片
|
||
print("[5/17] 测试删除图片...")
|
||
url = f"{API_BASE_URL}/api/images/999999"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.delete(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': '/api/images/<id>',
|
||
'method': 'DELETE',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 404],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': '/api/images/<id>',
|
||
'method': 'DELETE',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 6. 标签名称列表
|
||
print("[6/17] 测试标签名称列表...")
|
||
url = f"{API_BASE_URL}/api/images/tags/names/list"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params={"page": 1, "pageSize": 20}, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': '/api/images/tags/names/list',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': '/api/images/tags/names/list',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 7. 创建标签名称
|
||
print("[7/17] 测试创建标签名称...")
|
||
url = f"{API_BASE_URL}/api/images/tags/names/create"
|
||
data = {"tag_name": f"测试标签{int(time.time())}", "description": "测试"}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, headers=headers, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': '/api/images/tags/names/create',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 400],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': '/api/images/tags/names/create',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 8-11. 标签名称CRUD操作
|
||
print("[8-11/17] 测试标签名称CRUD...")
|
||
endpoints = [
|
||
('/api/images/tags/names/999999', 'PUT', '更新标签名称'),
|
||
('/api/images/tags/names/999999', 'DELETE', '删除标签名称'),
|
||
('/api/images/tags/relations/list', 'GET', '标签关系列表'),
|
||
('/api/images/types/list', 'GET', '图片类型列表'),
|
||
]
|
||
|
||
for endpoint, method, desc in endpoints:
|
||
url = f"{API_BASE_URL}{endpoint}"
|
||
try:
|
||
start_time = time.time()
|
||
if method == 'GET':
|
||
response = requests.get(url, headers=headers, params={"page": 1, "pageSize": 20}, timeout=30)
|
||
elif method == 'PUT':
|
||
response = requests.put(url, headers=headers, json={"description": "更新"}, timeout=30)
|
||
else:
|
||
response = requests.delete(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': f'{endpoint} ({desc})',
|
||
'method': method,
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 404],
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': f'{endpoint} ({desc})',
|
||
'method': method,
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 12-17. 图片类型和标签管理
|
||
print("[12-17/17] 测试类型和标签管理...")
|
||
test_cases = [
|
||
('/api/images/types/create', 'POST', {"type_name": f"测试类型{int(time.time())}"}, '创建类型'),
|
||
('/api/images/types/999999', 'PUT', {"type_name": "更新类型"}, '更新类型'),
|
||
('/api/images/types/999999', 'DELETE', None, '删除类型'),
|
||
('/api/images/tags/list', 'GET', None, '标签列表'),
|
||
('/api/images/tags/create', 'POST', {"image_id": 1, "tag_id": 1}, '创建标签'),
|
||
('/api/images/tags/999999', 'DELETE', None, '删除标签'),
|
||
]
|
||
|
||
for endpoint, method, data, desc in test_cases:
|
||
url = f"{API_BASE_URL}{endpoint}"
|
||
try:
|
||
start_time = time.time()
|
||
if method == 'GET':
|
||
response = requests.get(url, headers=headers, params={"page": 1, "pageSize": 20}, timeout=30)
|
||
elif method == 'POST':
|
||
response = requests.post(url, headers=headers, json=data, timeout=30)
|
||
elif method == 'PUT':
|
||
response = requests.put(url, headers=headers, json=data, timeout=30)
|
||
else:
|
||
response = requests.delete(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': f'{endpoint} ({desc})',
|
||
'method': method,
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 400, 404, 409],
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '图片接口',
|
||
'endpoint': f'{endpoint} ({desc})',
|
||
'method': method,
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
api_docs.extend([
|
||
{'module': '图片接口', 'endpoint': '/api/images/list', 'method': 'GET', 'description': '图片列表', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/list_dashboard', 'method': 'GET', 'description': '图片仪表盘', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/upload', 'method': 'POST', 'description': '上传图片', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/<id>', 'method': 'DELETE', 'description': '删除图片', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/tags/names/list', 'method': 'GET', 'description': '标签名称列表', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/tags/names/create', 'method': 'POST', 'description': '创建标签名称', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/tags/names/<id>', 'method': 'PUT', 'description': '更新标签名称', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/tags/names/<id>', 'method': 'DELETE', 'description': '删除标签名称', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/tags/relations/list', 'method': 'GET', 'description': '标签关系列表', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/types/list', 'method': 'GET', 'description': '图片类型列表', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/types/create', 'method': 'POST', 'description': '创建类型', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/types/<id>', 'method': 'PUT', 'description': '更新类型', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/types/<id>', 'method': 'DELETE', 'description': '删除类型', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/tags/list', 'method': 'GET', 'description': '标签列表', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/tags/create', 'method': 'POST', 'description': '创建标签', 'auth': '需要'},
|
||
{'module': '图片接口', 'endpoint': '/api/images/tags/<id>', 'method': 'DELETE', 'description': '删除标签', 'auth': '需要'},
|
||
])
|
||
|
||
# ============================================================================
|
||
# 模块6: 日志接口测试 (原脚本测试数: 7个)
|
||
# ============================================================================
|
||
def test_log_apis(token):
|
||
"""测试日志接口 - 操作日志、文件日志"""
|
||
if not TEST_MODULES['log']:
|
||
return
|
||
|
||
print_module_header("日志接口测试", 6, 10)
|
||
|
||
headers = {"Authorization": f"Bearer {token}"}
|
||
|
||
# 1-3. 获取操作日志 (不同参数)
|
||
print("\n[1-3/7] 测试获取操作日志...")
|
||
url = f"{API_BASE_URL}/api/log/logs"
|
||
test_cases = [
|
||
{"page": 1, "size": 10, "desc": "第1页10条"},
|
||
{"page": 1, "size": 20, "desc": "第1页20条"},
|
||
{"page": 2, "size": 10, "desc": "第2页10条"},
|
||
]
|
||
|
||
for i, params in enumerate(test_cases, 1):
|
||
desc = params.pop('desc')
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '日志接口',
|
||
'endpoint': f'/api/log/logs ({desc})',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 500],
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '日志接口',
|
||
'endpoint': f'/api/log/logs ({desc})',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 4. 日志文件列表
|
||
print("[4/7] 测试日志文件列表...")
|
||
url = f"{API_BASE_URL}/api/log/logs/files"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '日志接口',
|
||
'endpoint': '/api/log/logs/files',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '日志接口',
|
||
'endpoint': '/api/log/logs/files',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 5-7. 文件日志内容 (不同类型和行数)
|
||
print("[5-7/7] 测试文件日志内容...")
|
||
url = f"{API_BASE_URL}/api/log/logs/file"
|
||
test_cases = [
|
||
{"type": "article", "lines": 50, "desc": "article日志50行"},
|
||
{"type": "error", "lines": 50, "desc": "error日志50行"},
|
||
{"type": "article", "lines": 100, "desc": "article日志100行"},
|
||
]
|
||
|
||
for i, params in enumerate(test_cases, 1):
|
||
desc = params.pop('desc')
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '日志接口',
|
||
'endpoint': f'/api/log/logs/file ({desc})',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 404],
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '日志接口',
|
||
'endpoint': f'/api/log/logs/file ({desc})',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
api_docs.extend([
|
||
{'module': '日志接口', 'endpoint': '/api/log/logs', 'method': 'GET', 'description': '获取操作日志(分页)', 'auth': '需要'},
|
||
{'module': '日志接口', 'endpoint': '/api/log/logs/files', 'method': 'GET', 'description': '日志文件列表', 'auth': '需要'},
|
||
{'module': '日志接口', 'endpoint': '/api/log/logs/file', 'method': 'GET', 'description': '文件日志内容', 'auth': '需要'},
|
||
])
|
||
|
||
# ============================================================================
|
||
# 模块7: 文章接口测试 (原脚本测试数: 10个)
|
||
# ============================================================================
|
||
def test_article_apis(token):
|
||
"""测试文章接口 - 列表、详情、发布"""
|
||
if not TEST_MODULES['article']:
|
||
return
|
||
|
||
print_module_header("文章接口测试", 7, 10)
|
||
|
||
headers = {"Authorization": f"Bearer {token}"}
|
||
|
||
# 1-3. 文章列表 (不同参数)
|
||
print("\n[1-3/10] 测试文章列表...")
|
||
url = f"{API_BASE_URL}/api/articles/list"
|
||
test_cases = [
|
||
{"page": 1, "pageSize": 20, "desc": "基本查询"},
|
||
{"page": 1, "pageSize": 10, "keyword": "文章", "desc": "关键词搜索"},
|
||
{"page": 2, "pageSize": 20, "desc": "第2页"},
|
||
]
|
||
|
||
for i, params in enumerate(test_cases, 1):
|
||
desc = params.pop('desc')
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': f'/api/articles/list ({desc})',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': f'/api/articles/list ({desc})',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 4. 文章仪表盘
|
||
print("[4/10] 测试文章仪表盘...")
|
||
url = f"{API_BASE_URL}/api/articles/list_dashboard"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': '/api/articles/list_dashboard',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': '/api/articles/list_dashboard',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 5. 生成文章
|
||
print("[5/10] 测试生成文章...")
|
||
url = f"{API_BASE_URL}/api/articles/generate"
|
||
data = {"product_id": 1, "prompt_workflow_id": 1, "topics": ["测试主题1", "测试主题2"], "count": 2}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, headers=headers, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': '/api/articles/generate',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 400],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': '/api/articles/generate',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 6-10. 文章详情和更新(如果有文章)
|
||
print("[6-10/10] 测试文章详情和操作...")
|
||
list_response = requests.get(f"{API_BASE_URL}/api/articles/list", headers=headers,
|
||
params={"page": 1, "pageSize": 1}, timeout=30)
|
||
|
||
if list_response.status_code == 200:
|
||
articles = list_response.json().get('data', {}).get('list', [])
|
||
if articles:
|
||
article_id = articles[0]['id']
|
||
|
||
# 文章详情
|
||
url = f"{API_BASE_URL}/api/articles/{article_id}"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': f'/api/articles/{article_id}',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': f'/api/articles/{article_id}',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 更新文章
|
||
data = {"title": f"测试更新标题_{int(time.time())}", "status": "draft"}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.put(url, headers=headers, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': f'/api/articles/{article_id} (PUT)',
|
||
'method': 'PUT',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': f'/api/articles/{article_id} (PUT)',
|
||
'method': 'PUT',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 批量发布测试
|
||
print(" 测试批量发布接口...")
|
||
endpoints = [
|
||
('/api/articles/batch-published-account-cycle', {"article_ids": [1, 2], "author_ids": [1, 2]}, '账号循环'),
|
||
('/api/articles/batch-published-review', {"article_ids": [1, 2], "author_id": 1}, '指定作者'),
|
||
]
|
||
|
||
for endpoint, data, desc in endpoints:
|
||
url = f"{API_BASE_URL}{endpoint}"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, headers=headers, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': f'{endpoint} ({desc})',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 400],
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '文章接口',
|
||
'endpoint': f'{endpoint} ({desc})',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
api_docs.extend([
|
||
{'module': '文章接口', 'endpoint': '/api/articles/list', 'method': 'GET', 'description': '文章列表', 'auth': '需要'},
|
||
{'module': '文章接口', 'endpoint': '/api/articles/list_dashboard', 'method': 'GET', 'description': '文章仪表盘', 'auth': '需要'},
|
||
{'module': '文章接口', 'endpoint': '/api/articles/generate', 'method': 'POST', 'description': '生成文章', 'auth': '需要'},
|
||
{'module': '文章接口', 'endpoint': '/api/articles/<id>', 'method': 'GET', 'description': '文章详情', 'auth': '需要'},
|
||
{'module': '文章接口', 'endpoint': '/api/articles/<id>', 'method': 'PUT', 'description': '更新文章', 'auth': '需要'},
|
||
{'module': '文章接口', 'endpoint': '/api/articles/batch-published-account-cycle', 'method': 'POST', 'description': '批量发布(账号循环)', 'auth': '需要'},
|
||
{'module': '文章接口', 'endpoint': '/api/articles/batch-published-review', 'method': 'POST', 'description': '批量发布(指定作者)', 'auth': '需要'},
|
||
])
|
||
|
||
# ============================================================================
|
||
# 模块8: 员工接口测试 (原脚本测试数: 8个)
|
||
# ============================================================================
|
||
def test_employee_apis(token):
|
||
"""测试员工接口 - 列表、统计、CRUD"""
|
||
if not TEST_MODULES['employee']:
|
||
return
|
||
|
||
print_module_header("员工接口测试", 8, 10)
|
||
|
||
headers = {"Authorization": f"Bearer {token}"}
|
||
|
||
# 1-5. 员工列表 (不同参数)
|
||
print("\n[1-5/8] 测试员工列表(不同参数)...")
|
||
url = f"{API_BASE_URL}/api/employees/list"
|
||
test_cases = [
|
||
{"page": 1, "pageSize": 20, "desc": "基本查询"},
|
||
{"page": 1, "pageSize": 10, "desc": "每页10条"},
|
||
{"page": 1, "pageSize": 20, "keyword": "员工", "desc": "关键词搜索"},
|
||
{"page": 1, "pageSize": 20, "status": "active", "desc": "状态筛选"},
|
||
{"page": 2, "pageSize": 20, "desc": "第2页"},
|
||
]
|
||
|
||
for i, params in enumerate(test_cases, 1):
|
||
desc = params.pop('desc')
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '员工接口',
|
||
'endpoint': f'/api/employees/list ({desc})',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '员工接口',
|
||
'endpoint': f'/api/employees/list ({desc})',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 6. 员工统计
|
||
print("[6/8] 测试员工统计...")
|
||
url = f"{API_BASE_URL}/api/employees/stats"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '员工接口',
|
||
'endpoint': '/api/employees/stats',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '员工接口',
|
||
'endpoint': '/api/employees/stats',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 7. 添加员工
|
||
print("[7/8] 测试添加员工...")
|
||
url = f"{API_BASE_URL}/api/employees/add"
|
||
timestamp = int(time.time())
|
||
data = {
|
||
"name": f"测试员工{timestamp}",
|
||
"phone": f"1382{timestamp % 10000000:07d}",
|
||
"password": "test123456",
|
||
"role": "employee",
|
||
"department": "测试部门"
|
||
}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, headers=headers, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '员工接口',
|
||
'endpoint': '/api/employees/add',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 400],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '员工接口',
|
||
'endpoint': '/api/employees/add',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 8. 删除员工
|
||
print("[8/8] 测试删除员工...")
|
||
url = f"{API_BASE_URL}/api/employees/999999"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.delete(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '员工接口',
|
||
'endpoint': '/api/employees/<id>',
|
||
'method': 'DELETE',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 404],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '员工接口',
|
||
'endpoint': '/api/employees/<id>',
|
||
'method': 'DELETE',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
api_docs.extend([
|
||
{'module': '员工接口', 'endpoint': '/api/employees/list', 'method': 'GET', 'description': '员工列表(分页、搜索)', 'auth': '需要'},
|
||
{'module': '员工接口', 'endpoint': '/api/employees/stats', 'method': 'GET', 'description': '员工统计', 'auth': '需要'},
|
||
{'module': '员工接口', 'endpoint': '/api/employees/add', 'method': 'POST', 'description': '添加员工', 'auth': '需要'},
|
||
{'module': '员工接口', 'endpoint': '/api/employees/<id>', 'method': 'DELETE', 'description': '删除员工', 'auth': '需要'},
|
||
])
|
||
|
||
# ============================================================================
|
||
# 模块9: 作者接口测试 (原脚本测试数: 20个)
|
||
# ============================================================================
|
||
def test_author_apis(token):
|
||
"""测试作者接口 - 列表、搜索、CRUD"""
|
||
if not TEST_MODULES['author']:
|
||
return
|
||
|
||
print_module_header("作者接口测试", 9, 10)
|
||
|
||
headers = {"Authorization": f"Bearer {token}"}
|
||
|
||
# 1-3. 作者列表 (不同参数)
|
||
print("\n[1-3/12] 测试作者列表...")
|
||
url = f"{API_BASE_URL}/api/authors"
|
||
test_cases = [
|
||
{"page": 1, "size": 10, "desc": "第1页"},
|
||
{"page": 1, "size": 5, "desc": "每页5条"},
|
||
{"page": 1, "size": 10, "search": "医生", "desc": "搜索关键词"},
|
||
]
|
||
|
||
for i, params in enumerate(test_cases, 1):
|
||
desc = params.pop('desc')
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': f'/api/authors ({desc})',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': f'/api/authors ({desc})',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 4. 作者列表(用户关联)
|
||
print("[4/12] 测试作者列表(用户关联)...")
|
||
url = f"{API_BASE_URL}/api/authors/list"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params={"page": 1, "size": 10}, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': '/api/authors/list',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': '/api/authors/list',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 5-6. 作者详细列表
|
||
print("[5-6/12] 测试作者详细列表...")
|
||
url = f"{API_BASE_URL}/api/authors/detail_list"
|
||
test_cases = [
|
||
{"page": 1, "size": 10, "desc": "基本查询"},
|
||
{"page": 1, "size": 20, "search": "医生", "desc": "搜索查询"},
|
||
]
|
||
|
||
for i, params in enumerate(test_cases, 1):
|
||
desc = params.pop('desc')
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': f'/api/authors/detail_list ({desc})',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': f'/api/authors/detail_list ({desc})',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 7. 科室列表
|
||
print("[7/12] 测试科室列表...")
|
||
url = f"{API_BASE_URL}/api/authors/departments"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': '/api/authors/departments',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': '/api/authors/departments',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 8-9. 搜索作者
|
||
print("[8-9/12] 测试搜索作者...")
|
||
url = f"{API_BASE_URL}/api/authors/search"
|
||
test_cases = [
|
||
{"keyword": "医生", "limit": 10, "desc": "搜索医生"},
|
||
{"keyword": "科室", "limit": 5, "desc": "搜索科室"},
|
||
]
|
||
|
||
for i, params in enumerate(test_cases, 1):
|
||
desc = params.pop('desc')
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params=params, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': f'/api/authors/search ({desc})',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': f'/api/authors/search ({desc})',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 10-12. 作者CRUD操作
|
||
print("[10-12/12] 测试作者CRUD...")
|
||
# 创建作者
|
||
url = f"{API_BASE_URL}/api/authors"
|
||
timestamp = int(time.time())
|
||
data = {
|
||
"author_name": f"测试医生{timestamp}",
|
||
"app_id": f"test_app_{timestamp}",
|
||
"app_token": f"test_token_{timestamp}",
|
||
"department_name": "测试科室",
|
||
"title": "主任医师",
|
||
"hospital": "测试医院",
|
||
"channel": 1
|
||
}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, headers=headers, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': '/api/authors (POST)',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 201, 400],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': '/api/authors (POST)',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 更新和删除
|
||
for endpoint, method, desc in [
|
||
('/api/authors/999999', 'PUT', '更新作者'),
|
||
('/api/authors/999999', 'DELETE', '删除作者'),
|
||
]:
|
||
url = f"{API_BASE_URL}{endpoint}"
|
||
try:
|
||
start_time = time.time()
|
||
if method == 'PUT':
|
||
response = requests.put(url, headers=headers, json={"author_name": "更新测试"}, timeout=30)
|
||
else:
|
||
response = requests.delete(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': f'{endpoint} ({desc})',
|
||
'method': method,
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 404],
|
||
'error': ''
|
||
})
|
||
time.sleep(0.1)
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '作者接口',
|
||
'endpoint': f'{endpoint} ({desc})',
|
||
'method': method,
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
api_docs.extend([
|
||
{'module': '作者接口', 'endpoint': '/api/authors', 'method': 'GET', 'description': '作者列表(按科室分组)', 'auth': '需要'},
|
||
{'module': '作者接口', 'endpoint': '/api/authors', 'method': 'POST', 'description': '创建作者', 'auth': '需要'},
|
||
{'module': '作者接口', 'endpoint': '/api/authors/<id>', 'method': 'PUT', 'description': '更新作者', 'auth': '需要'},
|
||
{'module': '作者接口', 'endpoint': '/api/authors/<id>', 'method': 'DELETE', 'description': '删除作者', 'auth': '需要'},
|
||
{'module': '作者接口', 'endpoint': '/api/authors/list', 'method': 'GET', 'description': '作者列表(用户关联)', 'auth': '需要'},
|
||
{'module': '作者接口', 'endpoint': '/api/authors/detail_list', 'method': 'GET', 'description': '作者详细列表', 'auth': '需要'},
|
||
{'module': '作者接口', 'endpoint': '/api/authors/departments', 'method': 'GET', 'description': '科室列表', 'auth': '需要'},
|
||
{'module': '作者接口', 'endpoint': '/api/authors/search', 'method': 'GET', 'description': '搜索作者', 'auth': '需要'},
|
||
])
|
||
|
||
# ============================================================================
|
||
# 模块10: 搜索服务接口测试 (原脚本测试数: 6个)
|
||
# ============================================================================
|
||
def test_search_apis(token):
|
||
"""测试搜索服务接口 - 独立服务(8321端口)"""
|
||
if not TEST_MODULES['search']:
|
||
return
|
||
|
||
print_module_header("搜索服务接口测试", 10, 10)
|
||
print("注意: 搜索服务使用独立端口 8321")
|
||
|
||
headers = {"Authorization": f"Bearer {token}"}
|
||
|
||
# 1. 健康检查
|
||
print("\n[1/6] 测试健康检查...")
|
||
url = f"{SEARCH_API_BASE_URL}/health"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, timeout=5)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/health',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/health',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 2. 查询日志
|
||
print("[2/6] 测试查询日志...")
|
||
url = f"{SEARCH_API_BASE_URL}/api/search-service/logs"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params={"page": 1, "size": 10}, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/api/search-service/logs',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 500],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/api/search-service/logs',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 3. 日志文件列表
|
||
print("[3/6] 测试日志文件列表...")
|
||
url = f"{SEARCH_API_BASE_URL}/api/search-service/logs/files"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/api/search-service/logs/files',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code == 200,
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/api/search-service/logs/files',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 4. 文件日志内容
|
||
print("[4/6] 测试文件日志内容...")
|
||
url = f"{SEARCH_API_BASE_URL}/api/search-service/logs/file"
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.get(url, headers=headers, params={"type": "article", "lines": 50}, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/api/search-service/logs/file',
|
||
'method': 'GET',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 404],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/api/search-service/logs/file',
|
||
'method': 'GET',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 5. 生成文章
|
||
print("[5/6] 测试生成文章...")
|
||
url = f"{SEARCH_API_BASE_URL}/api/search-service/generate_article"
|
||
data = {"query": "测试查询", "max_results": 5}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, headers=headers, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/api/search-service/generate_article',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 400],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/api/search-service/generate_article',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
# 6. 关键词搜索
|
||
print("[6/6] 测试关键词搜索...")
|
||
url = f"{SEARCH_API_BASE_URL}/api/search-service/search"
|
||
data = {"keyword": "测试", "limit": 10}
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(url, headers=headers, json=data, timeout=30)
|
||
elapsed_time = (time.time() - start_time) * 1000
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/api/search-service/search',
|
||
'method': 'POST',
|
||
'status_code': response.status_code,
|
||
'elapsed_time_ms': f"{elapsed_time:.2f}",
|
||
'success': response.status_code in [200, 400],
|
||
'error': ''
|
||
})
|
||
except Exception as e:
|
||
test_results.append({
|
||
'module': '搜索服务',
|
||
'endpoint': '/api/search-service/search',
|
||
'method': 'POST',
|
||
'status_code': 0,
|
||
'elapsed_time_ms': '0',
|
||
'success': False,
|
||
'error': str(e)
|
||
})
|
||
|
||
api_docs.extend([
|
||
{'module': '搜索服务', 'endpoint': '/health', 'method': 'GET', 'description': '健康检查', 'auth': '不需要'},
|
||
{'module': '搜索服务', 'endpoint': '/api/search-service/logs', 'method': 'GET', 'description': '查询日志', 'auth': '需要'},
|
||
{'module': '搜索服务', 'endpoint': '/api/search-service/logs/files', 'method': 'GET', 'description': '日志文件列表', 'auth': '需要'},
|
||
{'module': '搜索服务', 'endpoint': '/api/search-service/logs/file', 'method': 'GET', 'description': '文件日志内容', 'auth': '需要'},
|
||
{'module': '搜索服务', 'endpoint': '/api/search-service/generate_article', 'method': 'POST', 'description': '生成文章', 'auth': '需要'},
|
||
{'module': '搜索服务', 'endpoint': '/api/search-service/search', 'method': 'POST', 'description': '关键词搜索', 'auth': '需要'},
|
||
])
|
||
|
||
# ============================================================================
|
||
# 结果保存和输出
|
||
# ============================================================================
|
||
def save_to_csv():
|
||
"""保存测试结果到CSV文件"""
|
||
timestamp = datetime.now().strftime('%Y%m%d')
|
||
filename = f'整体后端接口性能测试_{timestamp}.csv'
|
||
|
||
with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
|
||
writer = csv.DictWriter(f, fieldnames=[
|
||
'module', 'endpoint', 'method', 'status_code', 'elapsed_time_ms', 'success', 'error'
|
||
])
|
||
writer.writeheader()
|
||
writer.writerows(test_results)
|
||
|
||
print(f"\n✓ 性能测试结果已保存到: {filename}")
|
||
return filename
|
||
|
||
def save_api_docs():
|
||
"""保存接口文档到CSV文件"""
|
||
timestamp = datetime.now().strftime('%Y%m%d')
|
||
filename = f'整体后端接口文档_{timestamp}.csv'
|
||
|
||
with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
|
||
writer = csv.DictWriter(f, fieldnames=[
|
||
'module', 'endpoint', 'method', 'description', 'auth'
|
||
])
|
||
writer.writeheader()
|
||
writer.writerows(api_docs)
|
||
|
||
print(f"✓ 接口文档已保存到: {filename}")
|
||
return filename
|
||
|
||
def generate_markdown_report():
|
||
"""生成Markdown格式的综合数据报表"""
|
||
from collections import defaultdict
|
||
|
||
timestamp = datetime.now().strftime('%Y%m%d')
|
||
filename = f'整体后端接口测试报表_{timestamp}.md'
|
||
|
||
# 总体统计
|
||
total = len(test_results)
|
||
success = sum(1 for r in test_results if r['success'])
|
||
failed = total - success
|
||
|
||
times = [float(r['elapsed_time_ms']) for r in test_results if r['elapsed_time_ms'] != '0']
|
||
avg_time = sum(times) / len(times) if times else 0
|
||
max_time = max(times) if times else 0
|
||
min_time = min(times) if times else 0
|
||
|
||
# 找出最快和最慢的接口
|
||
max_item = max(test_results, key=lambda x: float(x['elapsed_time_ms']) if x['elapsed_time_ms'] != '0' else 0)
|
||
min_item = min([r for r in test_results if r['elapsed_time_ms'] != '0'],
|
||
key=lambda x: float(x['elapsed_time_ms'])) if times else None
|
||
|
||
# 按模块统计
|
||
modules = {}
|
||
for r in test_results:
|
||
module = r['module']
|
||
if module not in modules:
|
||
modules[module] = {
|
||
'total': 0,
|
||
'success': 0,
|
||
'times': [],
|
||
'endpoints': [],
|
||
'errors': []
|
||
}
|
||
modules[module]['total'] += 1
|
||
if r['success']:
|
||
modules[module]['success'] += 1
|
||
else:
|
||
modules[module]['errors'].append(r['endpoint'])
|
||
if r['elapsed_time_ms'] != '0':
|
||
modules[module]['times'].append(float(r['elapsed_time_ms']))
|
||
modules[module]['endpoints'].append((r['endpoint'], float(r['elapsed_time_ms'])))
|
||
|
||
# HTTP方法统计
|
||
methods = defaultdict(lambda: {'total': 0, 'success': 0})
|
||
for r in test_results:
|
||
method = r['method']
|
||
methods[method]['total'] += 1
|
||
if r['success']:
|
||
methods[method]['success'] += 1
|
||
|
||
# 性能分布
|
||
fast = sum(1 for t in times if t < 100)
|
||
normal = sum(1 for t in times if 100 <= t < 500)
|
||
slow = sum(1 for t in times if 500 <= t < 1000)
|
||
very_slow = sum(1 for t in times if t >= 1000)
|
||
|
||
# 状态码分布
|
||
status_codes = defaultdict(int)
|
||
for r in test_results:
|
||
status_codes[r['status_code']] += 1
|
||
|
||
# 接口文档统计
|
||
doc_modules = defaultdict(int)
|
||
doc_methods = defaultdict(int)
|
||
auth_required = sum(1 for d in api_docs if d['auth'] == '需要')
|
||
for doc in api_docs:
|
||
doc_modules[doc['module']] += 1
|
||
doc_methods[doc['method']] += 1
|
||
|
||
# 开始写入Markdown文件
|
||
with open(filename, 'w', encoding='utf-8') as f:
|
||
# 标题和概览
|
||
f.write("# 整体后端接口性能测试报表\n\n")
|
||
f.write(f"**生成时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
||
f.write("---\n\n")
|
||
|
||
# 总体统计
|
||
f.write("## 📈 总体测试统计\n\n")
|
||
f.write("| 指标 | 数值 |\n")
|
||
f.write("|------|------|\n")
|
||
f.write(f"| 测试总数 | {total} 个 |\n")
|
||
f.write(f"| 成功数量 | {success} 个 ✓ |\n")
|
||
f.write(f"| 失败数量 | {failed} 个 ✗ |\n")
|
||
f.write(f"| **成功率** | **{success/total*100:.2f}%** |\n")
|
||
f.write(f"| 平均耗时 | {avg_time:.2f}ms |\n")
|
||
f.write(f"| 最大耗时 | {max_time:.2f}ms |\n")
|
||
f.write(f"| 最小耗时 | {min_time:.2f}ms |\n\n")
|
||
|
||
if max_item:
|
||
f.write("**最慢接口**:\n")
|
||
f.write(f"- `{max_item['endpoint']}` ({max_item['module']}) - {max_item['elapsed_time_ms']}ms\n\n")
|
||
|
||
if min_item:
|
||
f.write("**最快接口**:\n")
|
||
f.write(f"- `{min_item['endpoint']}` ({min_item['module']}) - {min_item['elapsed_time_ms']}ms\n\n")
|
||
|
||
f.write("---\n\n")
|
||
|
||
# 各模块性能分析
|
||
f.write("## 📊 各模块性能分析\n\n")
|
||
|
||
sorted_modules = sorted(modules.items(),
|
||
key=lambda x: (x[1]['success']/x[1]['total'] if x[1]['total'] > 0 else 0),
|
||
reverse=True)
|
||
|
||
f.write("| 模块 | 测试数 | 成功 | 失败 | 成功率 | 平均耗时 | 最大耗时 | 最小耗时 | 状态 |\n")
|
||
f.write("|------|--------|------|------|--------|----------|----------|----------|------|\n")
|
||
|
||
for module, stats in sorted_modules:
|
||
success_rate = (stats['success'] / stats['total'] * 100) if stats['total'] > 0 else 0
|
||
avg_time = (sum(stats['times']) / len(stats['times'])) if stats['times'] else 0
|
||
max_time = max(stats['times']) if stats['times'] else 0
|
||
min_time = min(stats['times']) if stats['times'] else 0
|
||
|
||
if success_rate >= 90:
|
||
status = "🟢 优秀"
|
||
elif success_rate >= 70:
|
||
status = "🟡 良好"
|
||
elif success_rate >= 50:
|
||
status = "🟠 一般"
|
||
else:
|
||
status = "🔴 需优化"
|
||
|
||
f.write(f"| {module} | {stats['total']} | {stats['success']} | {stats['total']-stats['success']} | "
|
||
f"{success_rate:.1f}% | {avg_time:.2f}ms | {max_time:.2f}ms | {min_time:.2f}ms | {status} |\n")
|
||
|
||
f.write("\n")
|
||
|
||
# 失败接口详情
|
||
f.write("### ⚠️ 失败接口详情\n\n")
|
||
has_errors = False
|
||
for module, stats in sorted_modules:
|
||
if stats['errors']:
|
||
has_errors = True
|
||
f.write(f"**{module}** ({len(stats['errors'])}个失败): \n")
|
||
for err_endpoint in stats['errors']:
|
||
f.write(f"- `{err_endpoint}`\n")
|
||
f.write("\n")
|
||
|
||
if not has_errors:
|
||
f.write("✅ 所有接口测试通过!\n\n")
|
||
|
||
f.write("---\n\n")
|
||
|
||
# HTTP方法分布
|
||
f.write("## 🔧 HTTP方法分布\n\n")
|
||
f.write("| 方法 | 总数 | 成功 | 失败 | 成功率 |\n")
|
||
f.write("|------|------|------|------|--------|\n")
|
||
|
||
for method, stats in sorted(methods.items()):
|
||
success_rate = (stats['success'] / stats['total'] * 100) if stats['total'] > 0 else 0
|
||
f.write(f"| {method} | {stats['total']} | {stats['success']} | "
|
||
f"{stats['total']-stats['success']} | {success_rate:.1f}% |\n")
|
||
|
||
f.write("\n---\n\n")
|
||
|
||
# 性能分布
|
||
f.write("## ⚡ 性能分布分析\n\n")
|
||
f.write("| 性能等级 | 数量 | 占比 |\n")
|
||
f.write("|----------|------|------|\n")
|
||
f.write(f"| 🚀 极快 (<100ms) | {fast} | {fast/len(times)*100:.1f}% |\n")
|
||
f.write(f"| ✅ 正常 (100-500ms) | {normal} | {normal/len(times)*100:.1f}% |\n")
|
||
f.write(f"| ⚠️ 较慢 (500-1000ms) | {slow} | {slow/len(times)*100:.1f}% |\n")
|
||
f.write(f"| 🐌 很慢 (>1000ms) | {very_slow} | {very_slow/len(times)*100:.1f}% |\n")
|
||
f.write("\n")
|
||
|
||
# 性能分布图(文本版)
|
||
f.write("### 性能分布可视化\n\n")
|
||
f.write("```\n")
|
||
max_bar_len = 50
|
||
if times:
|
||
fast_bar = '█' * int((fast/len(times)) * max_bar_len)
|
||
normal_bar = '█' * int((normal/len(times)) * max_bar_len)
|
||
slow_bar = '█' * int((slow/len(times)) * max_bar_len)
|
||
very_slow_bar = '█' * int((very_slow/len(times)) * max_bar_len)
|
||
|
||
f.write(f"极快 (<100ms): {fast_bar} {fast}个\n")
|
||
f.write(f"正常 (100-500ms): {normal_bar} {normal}个\n")
|
||
f.write(f"较慢 (500-1000ms):{slow_bar} {slow}个\n")
|
||
f.write(f"很慢 (>1000ms): {very_slow_bar} {very_slow}个\n")
|
||
f.write("```\n\n")
|
||
|
||
f.write("---\n\n")
|
||
|
||
# HTTP状态码分布
|
||
f.write("## 📋 HTTP状态码分布\n\n")
|
||
f.write("| 状态码 | 数量 | 占比 | 说明 |\n")
|
||
f.write("|--------|------|------|------|\n")
|
||
|
||
for code, count in sorted(status_codes.items()):
|
||
percentage = (count / total * 100)
|
||
if code == 200:
|
||
desc = "✅ 成功"
|
||
elif code >= 500:
|
||
desc = "❌ 服务器错误"
|
||
elif code >= 400:
|
||
desc = "⚠️ 客户端错误"
|
||
elif code == 0:
|
||
desc = "❌ 请求失败"
|
||
else:
|
||
desc = "ℹ️ 其他"
|
||
|
||
f.write(f"| {code} | {count} | {percentage:.1f}% | {desc} |\n")
|
||
|
||
f.write("\n---\n\n")
|
||
|
||
# 接口文档统计
|
||
f.write("## 📚 接口文档统计\n\n")
|
||
f.write(f"**已记录接口总数**: {len(api_docs)} 个 \n")
|
||
f.write(f"**需要认证**: {auth_required} 个 ({auth_required/len(api_docs)*100:.1f}%)\n\n")
|
||
|
||
f.write("### 按模块分类\n\n")
|
||
f.write("| 模块 | 接口数 |\n")
|
||
f.write("|------|--------|\n")
|
||
for module, count in sorted(doc_modules.items(), key=lambda x: x[1], reverse=True):
|
||
f.write(f"| {module} | {count} |\n")
|
||
f.write("\n")
|
||
|
||
f.write("### 按方法分类\n\n")
|
||
f.write("| 方法 | 接口数 |\n")
|
||
f.write("|------|--------|\n")
|
||
for method, count in sorted(doc_methods.items()):
|
||
f.write(f"| {method} | {count} |\n")
|
||
f.write("\n")
|
||
|
||
f.write("---\n\n")
|
||
|
||
# 优化建议
|
||
f.write("## 💡 优化建议\n\n")
|
||
|
||
suggestions = []
|
||
|
||
# 检查失败率高的模块
|
||
for module, stats in modules.items():
|
||
success_rate = (stats['success'] / stats['total'] * 100) if stats['total'] > 0 else 0
|
||
if success_rate < 70:
|
||
suggestions.append(f"🔴 **{module}** 模块成功率仅 {success_rate:.1f}%,建议优先排查认证、参数和数据库连接问题")
|
||
|
||
# 检查慢接口
|
||
slow_endpoints = [(r['module'], r['endpoint'], float(r['elapsed_time_ms']))
|
||
for r in test_results if r['elapsed_time_ms'] != '0' and float(r['elapsed_time_ms']) > 500]
|
||
if slow_endpoints:
|
||
slow_endpoints.sort(key=lambda x: x[2], reverse=True)
|
||
suggestions.append(f"⚠️ 发现 {len(slow_endpoints)} 个耗时超过500ms的接口,建议添加缓存或优化查询")
|
||
suggestions.append(" 慢接口TOP 3:")
|
||
for module, endpoint, time in slow_endpoints[:3]:
|
||
suggestions.append(f" - `{endpoint}` ({module}) - {time:.2f}ms")
|
||
|
||
# 检查错误率
|
||
if failed > 0:
|
||
error_rate = failed / total * 100
|
||
if error_rate > 20:
|
||
suggestions.append(f"❌ 总体错误率达到 {error_rate:.1f}%,建议进行全面的接口测试和修复")
|
||
|
||
if suggestions:
|
||
for suggestion in suggestions:
|
||
f.write(f"{suggestion}\n")
|
||
else:
|
||
f.write("✅ 所有接口性能良好,无需特别优化\n")
|
||
|
||
f.write("\n---\n\n")
|
||
|
||
# 测试配置信息
|
||
f.write("## ⚙️ 测试配置\n\n")
|
||
f.write(f"- **主服务地址**: `{API_BASE_URL}`\n")
|
||
f.write(f"- **搜索服务地址**: `{SEARCH_API_BASE_URL}`\n")
|
||
f.write(f"- **测试账号**: `{USERNAME}`\n")
|
||
|
||
enabled_modules = [name for name, enabled in TEST_MODULES.items() if enabled]
|
||
disabled_modules = [name for name, enabled in TEST_MODULES.items() if not enabled]
|
||
|
||
f.write(f"- **启用模块** ({len(enabled_modules)}个): {', '.join(enabled_modules)}\n")
|
||
if disabled_modules:
|
||
f.write(f"- **禁用模块** ({len(disabled_modules)}个): {', '.join(disabled_modules)}\n")
|
||
|
||
f.write("\n---\n\n")
|
||
f.write("*报表由整体后端接口性能测试工具自动生成*\n")
|
||
|
||
print(f"\n✅ Markdown报表已生成: {filename}")
|
||
return filename
|
||
|
||
def generate_data_report():
|
||
"""生成综合数据报表"""
|
||
from collections import defaultdict
|
||
|
||
print("\n" + "█"*80)
|
||
print("📊 综合数据报表")
|
||
print("█"*80)
|
||
|
||
# 总体统计
|
||
total = len(test_results)
|
||
success = sum(1 for r in test_results if r['success'])
|
||
failed = total - success
|
||
|
||
times = [float(r['elapsed_time_ms']) for r in test_results if r['elapsed_time_ms'] != '0']
|
||
avg_time = sum(times) / len(times) if times else 0
|
||
max_time = max(times) if times else 0
|
||
min_time = min(times) if times else 0
|
||
|
||
# 找出最快和最慢的接口
|
||
max_item = min(test_results, key=lambda x: float(x['elapsed_time_ms']) if x['elapsed_time_ms'] != '0' else float('inf'))
|
||
min_item = max(test_results, key=lambda x: float(x['elapsed_time_ms']) if x['elapsed_time_ms'] != '0' else 0)
|
||
|
||
print("\n" + "="*80)
|
||
print("📈 总体测试统计")
|
||
print("="*80)
|
||
print(f" 测试总数: {total} 个")
|
||
print(f" 成功数量: {success} 个 ✓")
|
||
print(f" 失败数量: {failed} 个 ✗")
|
||
print(f" 成功率: {success/total*100:.2f}%")
|
||
print(f" 平均耗时: {avg_time:.2f}ms")
|
||
print(f" 最大耗时: {max_time:.2f}ms")
|
||
print(f" └─ 接口: {max_item['endpoint']} ({max_item['module']})")
|
||
print(f" 最小耗时: {min_time:.2f}ms")
|
||
print(f" └─ 接口: {min_item['endpoint']} ({min_item['module']})")
|
||
|
||
# 按模块统计
|
||
print("\n" + "="*80)
|
||
print("📊 各模块性能分析")
|
||
print("="*80)
|
||
|
||
modules = {}
|
||
for r in test_results:
|
||
module = r['module']
|
||
if module not in modules:
|
||
modules[module] = {
|
||
'total': 0,
|
||
'success': 0,
|
||
'times': [],
|
||
'endpoints': [],
|
||
'errors': []
|
||
}
|
||
modules[module]['total'] += 1
|
||
if r['success']:
|
||
modules[module]['success'] += 1
|
||
else:
|
||
modules[module]['errors'].append(r['endpoint'])
|
||
if r['elapsed_time_ms'] != '0':
|
||
modules[module]['times'].append(float(r['elapsed_time_ms']))
|
||
modules[module]['endpoints'].append((r['endpoint'], float(r['elapsed_time_ms'])))
|
||
|
||
# 按成功率排序
|
||
sorted_modules = sorted(modules.items(),
|
||
key=lambda x: (x[1]['success']/x[1]['total'] if x[1]['total'] > 0 else 0),
|
||
reverse=True)
|
||
|
||
for module, stats in sorted_modules:
|
||
success_rate = (stats['success'] / stats['total'] * 100) if stats['total'] > 0 else 0
|
||
avg_time = (sum(stats['times']) / len(stats['times'])) if stats['times'] else 0
|
||
max_time = max(stats['times']) if stats['times'] else 0
|
||
min_time = min(stats['times']) if stats['times'] else 0
|
||
|
||
# 确定性能等级
|
||
if success_rate >= 90:
|
||
status = "🟢 优秀"
|
||
elif success_rate >= 70:
|
||
status = "🟡 良好"
|
||
elif success_rate >= 50:
|
||
status = "🟠 一般"
|
||
else:
|
||
status = "🔴 需优化"
|
||
|
||
print(f"\n{status} {module}")
|
||
print(f" 测试数: {stats['total']} | 成功: {stats['success']} | 失败: {stats['total']-stats['success']}")
|
||
print(f" 成功率: {success_rate:.2f}%")
|
||
print(f" 平均耗时: {avg_time:.2f}ms | 最大: {max_time:.2f}ms | 最小: {min_time:.2f}ms")
|
||
|
||
# 显示失败的接口
|
||
if stats['errors']:
|
||
print(f" ⚠️ 失败接口 ({len(stats['errors'])}个):")
|
||
for err_endpoint in stats['errors'][:3]: # 最多显示3个
|
||
print(f" • {err_endpoint}")
|
||
if len(stats['errors']) > 3:
|
||
print(f" • ... 还有 {len(stats['errors'])-3} 个")
|
||
|
||
# HTTP方法统计
|
||
print("\n" + "="*80)
|
||
print("🔧 HTTP方法分布")
|
||
print("="*80)
|
||
|
||
methods = defaultdict(lambda: {'total': 0, 'success': 0})
|
||
for r in test_results:
|
||
method = r['method']
|
||
methods[method]['total'] += 1
|
||
if r['success']:
|
||
methods[method]['success'] += 1
|
||
|
||
for method, stats in sorted(methods.items()):
|
||
success_rate = (stats['success'] / stats['total'] * 100) if stats['total'] > 0 else 0
|
||
print(f" {method:6} | 总数: {stats['total']:3} | 成功: {stats['success']:3} | 成功率: {success_rate:5.1f}%")
|
||
|
||
# 性能分布
|
||
print("\n" + "="*80)
|
||
print("⚡ 性能分布分析")
|
||
print("="*80)
|
||
|
||
fast = sum(1 for t in times if t < 100)
|
||
normal = sum(1 for t in times if 100 <= t < 500)
|
||
slow = sum(1 for t in times if 500 <= t < 1000)
|
||
very_slow = sum(1 for t in times if t >= 1000)
|
||
|
||
print(f" 🚀 极快 (<100ms): {fast:3} 个 ({fast/len(times)*100:.1f}%)")
|
||
print(f" ✅ 正常 (100-500ms): {normal:3} 个 ({normal/len(times)*100:.1f}%)")
|
||
print(f" ⚠️ 较慢 (500-1000ms): {slow:3} 个 ({slow/len(times)*100:.1f}%)")
|
||
print(f" 🐌 很慢 (>1000ms): {very_slow:3} 个 ({very_slow/len(times)*100:.1f}%)")
|
||
|
||
# 状态码分布
|
||
print("\n" + "="*80)
|
||
print("📋 HTTP状态码分布")
|
||
print("="*80)
|
||
|
||
status_codes = defaultdict(int)
|
||
for r in test_results:
|
||
status_codes[r['status_code']] += 1
|
||
|
||
for code, count in sorted(status_codes.items()):
|
||
percentage = (count / total * 100)
|
||
if code == 200:
|
||
print(f" ✅ {code}: {count:3} 个 ({percentage:.1f}%)")
|
||
elif code >= 400:
|
||
print(f" ❌ {code}: {count:3} 个 ({percentage:.1f}%)")
|
||
else:
|
||
print(f" ℹ️ {code}: {count:3} 个 ({percentage:.1f}%)")
|
||
|
||
# 接口文档统计
|
||
print("\n" + "="*80)
|
||
print("📚 接口文档统计")
|
||
print("="*80)
|
||
print(f" 已记录接口: {len(api_docs)} 个")
|
||
|
||
doc_modules = defaultdict(int)
|
||
doc_methods = defaultdict(int)
|
||
auth_required = sum(1 for d in api_docs if d['auth'] == '需要')
|
||
|
||
for doc in api_docs:
|
||
doc_modules[doc['module']] += 1
|
||
doc_methods[doc['method']] += 1
|
||
|
||
print(f" 需要认证: {auth_required} 个")
|
||
print(f"\n 按模块分类:")
|
||
for module, count in sorted(doc_modules.items(), key=lambda x: x[1], reverse=True):
|
||
print(f" • {module}: {count} 个")
|
||
|
||
print(f"\n 按方法分类:")
|
||
for method, count in sorted(doc_methods.items()):
|
||
print(f" • {method}: {count} 个")
|
||
|
||
print("\n" + "█"*80)
|
||
|
||
def print_summary():
|
||
"""打印测试摘要(兼容旧版)"""
|
||
generate_data_report()
|
||
|
||
def print_config():
|
||
"""打印测试配置"""
|
||
print("测试配置:")
|
||
print("-"*80)
|
||
enabled_modules = [name for name, enabled in TEST_MODULES.items() if enabled]
|
||
disabled_modules = [name for name, enabled in TEST_MODULES.items() if not enabled]
|
||
|
||
print(f"启用的模块 ({len(enabled_modules)}个): {', '.join(enabled_modules)}")
|
||
if disabled_modules:
|
||
print(f"禁用的模块 ({len(disabled_modules)}个): {', '.join(disabled_modules)}")
|
||
print("-"*80)
|
||
|
||
def main():
|
||
"""主函数"""
|
||
print("="*80)
|
||
print("整合后端接口性能测试工具")
|
||
print(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||
print(f"主服务地址: {API_BASE_URL}")
|
||
print(f"搜索服务地址: {SEARCH_API_BASE_URL}")
|
||
print("="*80)
|
||
|
||
# 打印配置
|
||
print_config()
|
||
|
||
# 登录
|
||
token = login()
|
||
if not token:
|
||
print("\n✗ 登录失败,无法继续测试")
|
||
return 1
|
||
|
||
# 执行所有测试
|
||
try:
|
||
test_basic_apis()
|
||
test_auth_apis(token)
|
||
test_dashboard_apis(token)
|
||
test_enterprise_apis(token)
|
||
test_image_apis(token)
|
||
test_log_apis(token)
|
||
test_article_apis(token)
|
||
test_employee_apis(token)
|
||
test_author_apis(token)
|
||
test_search_apis(token)
|
||
except Exception as e:
|
||
print(f"\n✗ 测试过程中出现异常: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
# 生成综合数据报表(控制台输出)
|
||
generate_data_report()
|
||
|
||
# 保存结果
|
||
csv_file = save_to_csv()
|
||
doc_file = save_api_docs()
|
||
md_report_file = generate_markdown_report() # 生成Markdown报表
|
||
|
||
print("\n" + "="*80)
|
||
print("✅ 测试完成!")
|
||
print("="*80)
|
||
print(f"📁 输出文件:")
|
||
print(f" • 性能数据: {csv_file}")
|
||
print(f" • 接口文档: {doc_file}")
|
||
print(f" • 测试报表: {md_report_file}")
|
||
print(f"\n💡 提示:")
|
||
print(f" • 查看Markdown报表: {md_report_file}")
|
||
print(f" • 使用 'python 查看综合测试报表.py' 查看控制台版详细报表")
|
||
print(f" • 成功率低于70%的模块需要优化")
|
||
print(f" • 耗时超过500ms的接口建议添加缓存或优化查询")
|
||
print("="*80)
|
||
|
||
return 0
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
sys.exit(main())
|
||
except KeyboardInterrupt:
|
||
print("\n\n✗ 测试被用户中断")
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
print(f"\n✗ 测试异常: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
sys.exit(1)
|