|
|
#!/usr/bin/env python3
|
|
|
"""
|
|
|
测试运行器 - 统一管理所有测试
|
|
|
包含问题检测、修复建议和测试报告
|
|
|
"""
|
|
|
|
|
|
import os
|
|
|
import sys
|
|
|
import subprocess
|
|
|
import json
|
|
|
import requests
|
|
|
import time
|
|
|
from pathlib import Path
|
|
|
|
|
|
# 项目路径配置
|
|
|
PROJECT_ROOT = Path(__file__).parent.parent
|
|
|
DJANGO_ROOT = PROJECT_ROOT / "信息抽取+数据检验/Django123/atc_extractor/backend"
|
|
|
BACKEND_API_URL = "http://127.0.0.1:8000/api"
|
|
|
|
|
|
class TestRunner:
|
|
|
def __init__(self):
|
|
|
self.test_results = {
|
|
|
'static_analysis': {},
|
|
|
'api_tests': {},
|
|
|
'database_tests': {},
|
|
|
'security_tests': {},
|
|
|
'integration_tests': {}
|
|
|
}
|
|
|
self.issues_found = []
|
|
|
|
|
|
def run_static_analysis(self):
|
|
|
"""静态代码分析"""
|
|
|
print("🔍 进行静态代码分析...")
|
|
|
|
|
|
issues = []
|
|
|
|
|
|
# 1. 检查数据库凭据安全性
|
|
|
settings_file = DJANGO_ROOT / "backend/settings.py"
|
|
|
if settings_file.exists():
|
|
|
with open(settings_file, 'r', encoding='utf-8') as f:
|
|
|
content = f.read()
|
|
|
if 'hzk200407140238' in content:
|
|
|
issues.append({
|
|
|
'type': 'SECURITY',
|
|
|
'severity': 'HIGH',
|
|
|
'message': '数据库密码硬编码在源代码中',
|
|
|
'file': str(settings_file),
|
|
|
'recommendation': '使用环境变量或配置文件管理敏感信息'
|
|
|
})
|
|
|
|
|
|
if 'DEBUG = True' in content:
|
|
|
issues.append({
|
|
|
'type': 'SECURITY',
|
|
|
'severity': 'MEDIUM',
|
|
|
'message': 'DEBUG模式在生产环境中应该关闭',
|
|
|
'file': str(settings_file),
|
|
|
'recommendation': '在生产环境中设置DEBUG = False'
|
|
|
})
|
|
|
|
|
|
if 'django-insecure' in content:
|
|
|
issues.append({
|
|
|
'type': 'SECURITY',
|
|
|
'severity': 'HIGH',
|
|
|
'message': '使用默认的不安全SECRET_KEY',
|
|
|
'file': str(settings_file),
|
|
|
'recommendation': '生成新的安全SECRET_KEY'
|
|
|
})
|
|
|
|
|
|
# 2. 检查表名拼写错误
|
|
|
views_file = DJANGO_ROOT / "extractor/views.py"
|
|
|
if views_file.exists():
|
|
|
with open(views_file, 'r', encoding='utf-8') as f:
|
|
|
content = f.read()
|
|
|
if 'precessed_table' in content:
|
|
|
issues.append({
|
|
|
'type': 'BUG',
|
|
|
'severity': 'HIGH',
|
|
|
'message': '表名拼写错误: precessed_table 应该是 processed_table',
|
|
|
'file': str(views_file),
|
|
|
'recommendation': '修正表名拼写或确保数据库中表名一致'
|
|
|
})
|
|
|
|
|
|
# 3. 检查模型文件是否为空
|
|
|
models_file = DJANGO_ROOT / "extractor/models.py"
|
|
|
if models_file.exists():
|
|
|
with open(models_file, 'r', encoding='utf-8') as f:
|
|
|
content = f.read().strip()
|
|
|
if len(content) < 100: # 基本上是空文件
|
|
|
issues.append({
|
|
|
'type': 'ARCHITECTURE',
|
|
|
'severity': 'MEDIUM',
|
|
|
'message': 'models.py文件为空,未使用Django ORM',
|
|
|
'file': str(models_file),
|
|
|
'recommendation': '考虑使用Django ORM管理数据模型,或添加注释说明原因'
|
|
|
})
|
|
|
|
|
|
# 4. 检查依赖管理
|
|
|
requirements_file = DJANGO_ROOT / "requirements.txt"
|
|
|
if not requirements_file.exists():
|
|
|
issues.append({
|
|
|
'type': 'DEPLOYMENT',
|
|
|
'severity': 'MEDIUM',
|
|
|
'message': '缺少requirements.txt文件',
|
|
|
'file': str(DJANGO_ROOT),
|
|
|
'recommendation': '创建requirements.txt管理项目依赖'
|
|
|
})
|
|
|
|
|
|
self.test_results['static_analysis'] = {
|
|
|
'issues_count': len(issues),
|
|
|
'issues': issues
|
|
|
}
|
|
|
self.issues_found.extend(issues)
|
|
|
|
|
|
print(f" 发现 {len(issues)} 个静态分析问题")
|
|
|
return issues
|
|
|
|
|
|
def test_api_endpoints(self):
|
|
|
"""测试API接口"""
|
|
|
print("🌐 测试API接口...")
|
|
|
|
|
|
endpoints = [
|
|
|
('GET', '/health/', '健康检查'),
|
|
|
('GET', '/original-data/', '原始数据获取'),
|
|
|
('GET', '/statistics/', '统计信息'),
|
|
|
('GET', '/processed-data/', '处理后数据'),
|
|
|
('POST', '/preprocess/', '数据预处理'),
|
|
|
('POST', '/merge/', '格式合并'),
|
|
|
('POST', '/correct/', '单词纠错'),
|
|
|
('POST', '/analyze/', '大模型分析'),
|
|
|
('POST', '/upload/', '文件上传'),
|
|
|
]
|
|
|
|
|
|
results = {}
|
|
|
|
|
|
for method, endpoint, description in endpoints:
|
|
|
try:
|
|
|
url = f"{BACKEND_API_URL}{endpoint}"
|
|
|
|
|
|
if method == 'GET':
|
|
|
response = requests.get(url, timeout=5)
|
|
|
else:
|
|
|
# POST请求发送空JSON
|
|
|
response = requests.post(url, json={}, timeout=5)
|
|
|
|
|
|
results[endpoint] = {
|
|
|
'status_code': response.status_code,
|
|
|
'success': response.status_code in [200, 400], # 400也可能是正常的业务错误
|
|
|
'description': description,
|
|
|
'response_size': len(response.content)
|
|
|
}
|
|
|
|
|
|
if response.status_code == 200:
|
|
|
print(f" ✓ {description} ({endpoint}): {response.status_code}")
|
|
|
elif response.status_code == 400:
|
|
|
print(f" ⚠ {description} ({endpoint}): {response.status_code} (业务错误)")
|
|
|
else:
|
|
|
print(f" ✗ {description} ({endpoint}): {response.status_code}")
|
|
|
|
|
|
except requests.RequestException as e:
|
|
|
results[endpoint] = {
|
|
|
'error': str(e),
|
|
|
'success': False,
|
|
|
'description': description
|
|
|
}
|
|
|
print(f" ✗ {description} ({endpoint}): 连接失败 - {e}")
|
|
|
|
|
|
self.test_results['api_tests'] = results
|
|
|
return results
|
|
|
|
|
|
def test_database_connectivity(self):
|
|
|
"""测试数据库连接"""
|
|
|
print("🗄️ 测试数据库连接...")
|
|
|
|
|
|
# 通过API测试数据库连接
|
|
|
try:
|
|
|
response = requests.get(f"{BACKEND_API_URL}/statistics/", timeout=5)
|
|
|
if response.status_code == 200:
|
|
|
data = response.json()
|
|
|
if data.get('status') == 'success':
|
|
|
print(" ✓ 数据库连接正常")
|
|
|
return True
|
|
|
print(" ✗ 数据库连接异常")
|
|
|
return False
|
|
|
except Exception as e:
|
|
|
print(f" ✗ 数据库连接测试失败: {e}")
|
|
|
return False
|
|
|
|
|
|
def check_server_status(self):
|
|
|
"""检查Django服务器状态"""
|
|
|
print("🚀 检查Django服务器状态...")
|
|
|
|
|
|
try:
|
|
|
response = requests.get(f"{BACKEND_API_URL}/health/", timeout=3)
|
|
|
if response.status_code == 200:
|
|
|
print(" ✓ Django服务器运行正常")
|
|
|
return True
|
|
|
else:
|
|
|
print(f" ✗ Django服务器响应异常: {response.status_code}")
|
|
|
return False
|
|
|
except requests.RequestException:
|
|
|
print(" ✗ Django服务器未运行或无法连接")
|
|
|
print(" 💡 请先启动Django服务器: python manage.py runserver")
|
|
|
return False
|
|
|
|
|
|
def generate_test_report(self):
|
|
|
"""生成测试报告"""
|
|
|
print("\n" + "="*60)
|
|
|
print("📊 测试报告汇总")
|
|
|
print("="*60)
|
|
|
|
|
|
# 静态分析结果
|
|
|
static_issues = self.test_results['static_analysis'].get('issues', [])
|
|
|
print(f"\n🔍 静态分析: 发现 {len(static_issues)} 个问题")
|
|
|
|
|
|
for issue in static_issues:
|
|
|
severity_emoji = {
|
|
|
'HIGH': '🔴',
|
|
|
'MEDIUM': '🟡',
|
|
|
'LOW': '🟢'
|
|
|
}.get(issue['severity'], '⚪')
|
|
|
|
|
|
print(f" {severity_emoji} [{issue['type']}] {issue['message']}")
|
|
|
print(f" 文件: {issue['file']}")
|
|
|
print(f" 建议: {issue['recommendation']}")
|
|
|
print()
|
|
|
|
|
|
# API测试结果
|
|
|
api_results = self.test_results['api_tests']
|
|
|
successful_apis = sum(1 for r in api_results.values() if r.get('success', False))
|
|
|
print(f"\n🌐 API测试: {successful_apis}/{len(api_results)} 个接口正常")
|
|
|
|
|
|
for endpoint, result in api_results.items():
|
|
|
if result.get('success'):
|
|
|
print(f" ✓ {result['description']}: {result.get('status_code', 'OK')}")
|
|
|
else:
|
|
|
print(f" ✗ {result['description']}: {result.get('error', result.get('status_code', '未知错误'))}")
|
|
|
|
|
|
# 生成修复建议
|
|
|
self.generate_fix_recommendations()
|
|
|
|
|
|
# 保存详细报告
|
|
|
report_file = PROJECT_ROOT / "test" / "test_report.json"
|
|
|
with open(report_file, 'w', encoding='utf-8') as f:
|
|
|
json.dump(self.test_results, f, indent=2, ensure_ascii=False)
|
|
|
print(f"\n📄 详细报告已保存到: {report_file}")
|
|
|
|
|
|
def generate_fix_recommendations(self):
|
|
|
"""生成修复建议"""
|
|
|
print("\n" + "="*60)
|
|
|
print("🔧 修复建议")
|
|
|
print("="*60)
|
|
|
|
|
|
high_priority_issues = [
|
|
|
issue for issue in self.issues_found
|
|
|
if issue.get('severity') == 'HIGH'
|
|
|
]
|
|
|
|
|
|
if high_priority_issues:
|
|
|
print("\n🔴 高优先级问题(需要立即修复):")
|
|
|
for i, issue in enumerate(high_priority_issues, 1):
|
|
|
print(f"\n{i}. {issue['message']}")
|
|
|
print(f" 类型: {issue['type']}")
|
|
|
print(f" 文件: {issue['file']}")
|
|
|
print(f" 建议: {issue['recommendation']}")
|
|
|
|
|
|
# 通用修复建议
|
|
|
print("\n💡 通用优化建议:")
|
|
|
print("1. 创建环境变量配置文件 (.env)")
|
|
|
print("2. 添加单元测试覆盖所有API接口")
|
|
|
print("3. 实现日志记录和监控")
|
|
|
print("4. 添加数据验证和错误处理")
|
|
|
print("5. 考虑使用Django ORM替代原生SQL")
|
|
|
print("6. 添加API文档(Swagger/OpenAPI)")
|
|
|
print("7. 实现缓存机制提高性能")
|
|
|
print("8. 添加数据库迁移管理")
|
|
|
|
|
|
def run_all_tests(self):
|
|
|
"""运行所有测试"""
|
|
|
print("🚀 开始综合测试...")
|
|
|
|
|
|
# 1. 检查服务器状态
|
|
|
if not self.check_server_status():
|
|
|
print("\n❌ 服务器未运行,跳过API测试")
|
|
|
print("请先启动Django服务器:")
|
|
|
print(f"cd {DJANGO_ROOT}")
|
|
|
print("python manage.py runserver")
|
|
|
|
|
|
# 只运行静态分析
|
|
|
self.run_static_analysis()
|
|
|
else:
|
|
|
# 2. 静态分析
|
|
|
self.run_static_analysis()
|
|
|
|
|
|
# 3. API测试
|
|
|
self.test_api_endpoints()
|
|
|
|
|
|
# 4. 数据库测试
|
|
|
self.test_database_connectivity()
|
|
|
|
|
|
# 5. 生成报告
|
|
|
self.generate_test_report()
|
|
|
|
|
|
|
|
|
def main():
|
|
|
"""主函数"""
|
|
|
print("🎯 Django后端测试系统")
|
|
|
print("=" * 60)
|
|
|
|
|
|
runner = TestRunner()
|
|
|
runner.run_all_tests()
|
|
|
|
|
|
print("\n✅ 测试完成!")
|
|
|
|
|
|
# 检查是否有严重问题
|
|
|
high_issues = sum(1 for issue in runner.issues_found if issue.get('severity') == 'HIGH')
|
|
|
if high_issues > 0:
|
|
|
print(f"⚠️ 发现 {high_issues} 个高优先级问题需要修复")
|
|
|
return 1
|
|
|
else:
|
|
|
print("🎉 未发现严重问题")
|
|
|
return 0
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
sys.exit(main()) |