|
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
"""
|
|
|
|
|
|
金融数据中台 v2.1 - 测试执行脚本
|
|
|
|
|
|
执行所有测试用例并生成报告
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import sys
|
|
|
|
|
|
import time
|
|
|
|
|
|
import json
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
# 添加项目路径
|
|
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / 'backend'))
|
|
|
|
|
|
|
|
|
|
|
|
class TestRunner:
|
|
|
|
|
|
"""测试执行器"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
self.results = []
|
|
|
|
|
|
self.start_time = datetime.now()
|
|
|
|
|
|
|
|
|
|
|
|
def run_test(self, name, test_func):
|
|
|
|
|
|
"""执行单个测试"""
|
|
|
|
|
|
print(f"Running: {name}...", end=" ")
|
|
|
|
|
|
try:
|
|
|
|
|
|
test_func()
|
|
|
|
|
|
print("✅ PASS")
|
|
|
|
|
|
self.results.append({"name": name, "status": "pass", "time": time.time()})
|
|
|
|
|
|
return True
|
|
|
|
|
|
except AssertionError as e:
|
|
|
|
|
|
print(f"❌ FAIL: {e}")
|
|
|
|
|
|
self.results.append({"name": name, "status": "fail", "error": str(e), "time": time.time()})
|
|
|
|
|
|
return False
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"❌ ERROR: {e}")
|
|
|
|
|
|
self.results.append({"name": name, "status": "error", "error": str(e), "time": time.time()})
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def generate_report(self):
|
|
|
|
|
|
"""生成测试报告"""
|
|
|
|
|
|
total = len(self.results)
|
|
|
|
|
|
passed = sum(1 for r in self.results if r["status"] == "pass")
|
|
|
|
|
|
failed = sum(1 for r in self.results if r["status"] == "fail")
|
|
|
|
|
|
errors = sum(1 for r in self.results if r["status"] == "error")
|
|
|
|
|
|
|
|
|
|
|
|
report = {
|
|
|
|
|
|
"project": "20260330_kline_system",
|
|
|
|
|
|
"version": "v2.1.0",
|
|
|
|
|
|
"start_time": self.start_time.isoformat(),
|
|
|
|
|
|
"end_time": datetime.now().isoformat(),
|
|
|
|
|
|
"duration": (datetime.now() - self.start_time).total_seconds(),
|
|
|
|
|
|
"total": total,
|
|
|
|
|
|
"passed": passed,
|
|
|
|
|
|
"failed": failed,
|
|
|
|
|
|
"errors": errors,
|
|
|
|
|
|
"pass_rate": f"{passed/total*100:.2f}%" if total > 0 else "0%",
|
|
|
|
|
|
"results": self.results
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return report
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============== WebSocket 测试 ==============
|
|
|
|
|
|
|
|
|
|
|
|
def test_ws_connect_valid_token():
|
|
|
|
|
|
"""TC-WS-001: 正常连接(有效 Token)"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_ws_connect_invalid_token():
|
|
|
|
|
|
"""TC-WS-002: 连接失败(无效 Token)"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_ws_subscribe_single():
|
|
|
|
|
|
"""TC-WS-011: 订阅单个品种"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_ws_quote_push():
|
|
|
|
|
|
"""TC-WS-021: 行情推送接收"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_ws_1000_connections():
|
|
|
|
|
|
"""TC-WS-043: 1000 并发连接"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============== 告警系统测试 ==============
|
|
|
|
|
|
|
|
|
|
|
|
def test_alert_create_price():
|
|
|
|
|
|
"""TC-AL-001: 创建告警规则(价格)"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_alert_price_above_trigger():
|
|
|
|
|
|
"""TC-AL-011: 价格大于阈值触发"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_alert_notification_parallel():
|
|
|
|
|
|
"""TC-AL-036: 多渠道并行通知"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_alert_100_rules():
|
|
|
|
|
|
"""TC-AL-041: 100 规则/用户"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============== 质量监控测试 ==============
|
|
|
|
|
|
|
|
|
|
|
|
def test_quality_completeness_100():
|
|
|
|
|
|
"""TC-QM-001: 数据完整(100 分)"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_quality_accuracy_anomaly():
|
|
|
|
|
|
"""TC-QM-012: 价格异常检测"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_quality_overall_score():
|
|
|
|
|
|
"""TC-QM-041: 总体评分计算"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============== 前端功能测试 ==============
|
|
|
|
|
|
|
|
|
|
|
|
def test_frontend_alert_list():
|
|
|
|
|
|
"""TC-FE-001: 告警列表加载"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_frontend_quality_cards():
|
|
|
|
|
|
"""TC-FE-011: 质量概览卡片显示"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_frontend_ws_connect():
|
|
|
|
|
|
"""TC-FE-021: WebSocket 连接"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============== 集成测试 ==============
|
|
|
|
|
|
|
|
|
|
|
|
def test_e2e_alert_workflow():
|
|
|
|
|
|
"""TC-IN-001: 完整告警工作流"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_e2e_websocket_workflow():
|
|
|
|
|
|
"""TC-IN-002: 完整 WebSocket 工作流"""
|
|
|
|
|
|
# TODO: 实现实际测试
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
"""主函数"""
|
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
print("金融数据中台 v2.1 - 测试执行")
|
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
|
|
runner = TestRunner()
|
|
|
|
|
|
|
|
|
|
|
|
# WebSocket 测试
|
|
|
|
|
|
print("\n📡 WebSocket 测试")
|
|
|
|
|
|
print("-" * 40)
|
|
|
|
|
|
runner.run_test("TC-WS-001: 正常连接", test_ws_connect_valid_token)
|
|
|
|
|
|
runner.run_test("TC-WS-002: 无效 Token", test_ws_connect_invalid_token)
|
|
|
|
|
|
runner.run_test("TC-WS-011: 订阅单个品种", test_ws_subscribe_single)
|
|
|
|
|
|
runner.run_test("TC-WS-021: 行情推送", test_ws_quote_push)
|
|
|
|
|
|
runner.run_test("TC-WS-043: 1000 并发连接", test_ws_1000_connections)
|
|
|
|
|
|
|
|
|
|
|
|
# 告警系统测试
|
|
|
|
|
|
print("\n🔔 告警系统测试")
|
|
|
|
|
|
print("-" * 40)
|
|
|
|
|
|
runner.run_test("TC-AL-001: 创建价格告警", test_alert_create_price)
|
|
|
|
|
|
runner.run_test("TC-AL-011: 价格触发告警", test_alert_price_above_trigger)
|
|
|
|
|
|
runner.run_test("TC-AL-036: 多渠道通知", test_alert_notification_parallel)
|
|
|
|
|
|
runner.run_test("TC-AL-041: 100 规则/用户", test_alert_100_rules)
|
|
|
|
|
|
|
|
|
|
|
|
# 质量监控测试
|
|
|
|
|
|
print("\n📊 质量监控测试")
|
|
|
|
|
|
print("-" * 40)
|
|
|
|
|
|
runner.run_test("TC-QM-001: 完整性 100 分", test_quality_completeness_100)
|
|
|
|
|
|
runner.run_test("TC-QM-012: 准确性异常检测", test_quality_accuracy_anomaly)
|
|
|
|
|
|
runner.run_test("TC-QM-041: 总体评分", test_quality_overall_score)
|
|
|
|
|
|
|
|
|
|
|
|
# 前端功能测试
|
|
|
|
|
|
print("\n🖥️ 前端功能测试")
|
|
|
|
|
|
print("-" * 40)
|
|
|
|
|
|
runner.run_test("TC-FE-001: 告警列表", test_frontend_alert_list)
|
|
|
|
|
|
runner.run_test("TC-FE-011: 质量卡片", test_frontend_quality_cards)
|
|
|
|
|
|
runner.run_test("TC-FE-021: WebSocket 连接", test_frontend_ws_connect)
|
|
|
|
|
|
|
|
|
|
|
|
# 集成测试
|
|
|
|
|
|
print("\n🔗 集成测试")
|
|
|
|
|
|
print("-" * 40)
|
|
|
|
|
|
runner.run_test("TC-IN-001: 告警工作流", test_e2e_alert_workflow)
|
|
|
|
|
|
runner.run_test("TC-IN-002: WebSocket 工作流", test_e2e_websocket_workflow)
|
|
|
|
|
|
|
|
|
|
|
|
# 生成报告
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
|
print("测试报告")
|
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
report = runner.generate_report()
|
|
|
|
|
|
|
|
|
|
|
|
print(f"开始时间:{report['start_time']}")
|
|
|
|
|
|
print(f"结束时间:{report['end_time']}")
|
|
|
|
|
|
print(f"总耗时:{report['duration']:.2f}秒")
|
|
|
|
|
|
print(f"总用例:{report['total']}")
|
|
|
|
|
|
print(f"通过:{report['passed']} ✅")
|
|
|
|
|
|
print(f"失败:{report['failed']} ❌")
|
|
|
|
|
|
print(f"错误:{report['errors']} ⚠️")
|
|
|
|
|
|
print(f"通过率:{report['pass_rate']}")
|
|
|
|
|
|
|
|
|
|
|
|
# 保存报告
|
|
|
|
|
|
report_path = Path(__file__).parent.parent / 'test_report_v2_1.json'
|
|
|
|
|
|
with open(report_path, 'w', encoding='utf-8') as f:
|
|
|
|
|
|
json.dump(report, f, ensure_ascii=False, indent=2)
|
|
|
|
|
|
|
|
|
|
|
|
print(f"\n报告已保存:{report_path}")
|
|
|
|
|
|
|
|
|
|
|
|
# 返回退出码
|
|
|
|
|
|
if report['failed'] > 0 or report['errors'] > 0:
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
main()
|