489 lines
18 KiB
Python
489 lines
18 KiB
Python
|
#!/usr/bin/env python
|
|||
|
# -*- coding: utf-8 -*-
|
|||
|
|
|||
|
"""
|
|||
|
脚本集成测试
|
|||
|
测试从创建项目、编辑脚本到VWED任务调度的完整流程
|
|||
|
"""
|
|||
|
|
|||
|
import asyncio
|
|||
|
import json
|
|||
|
import sys
|
|||
|
import os
|
|||
|
from pathlib import Path
|
|||
|
|
|||
|
# 添加项目根目录到 Python 路径
|
|||
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|||
|
|
|||
|
from services.script_file_service import get_file_service
|
|||
|
from services.script_engine_service import get_script_engine
|
|||
|
from services.script_task_integration import get_task_integration
|
|||
|
from services.script_registry_service import get_global_registry
|
|||
|
from utils.logger import get_logger
|
|||
|
|
|||
|
logger = get_logger("test.script_integration")
|
|||
|
|
|||
|
|
|||
|
class ScriptIntegrationTest:
|
|||
|
"""脚本集成测试类"""
|
|||
|
|
|||
|
def __init__(self):
|
|||
|
self.file_service = get_file_service()
|
|||
|
self.script_engine = get_script_engine()
|
|||
|
self.task_integration = get_task_integration()
|
|||
|
self.registry = get_global_registry()
|
|||
|
|
|||
|
# 测试数据
|
|||
|
self.test_project_id = None
|
|||
|
self.test_file_id = None
|
|||
|
self.test_script_id = None
|
|||
|
|
|||
|
async def run_full_test(self):
|
|||
|
"""运行完整的集成测试"""
|
|||
|
try:
|
|||
|
logger.info("=== 开始脚本集成测试 ===")
|
|||
|
|
|||
|
# 第1步:创建测试项目
|
|||
|
await self.test_create_project()
|
|||
|
|
|||
|
# 第2步:创建测试脚本文件
|
|||
|
await self.test_create_script_file()
|
|||
|
|
|||
|
# 第3步:编辑脚本内容
|
|||
|
await self.test_update_script_content()
|
|||
|
|
|||
|
# 第4步:启动脚本服务
|
|||
|
await self.test_start_script_service()
|
|||
|
|
|||
|
# 第5步:测试脚本函数调用
|
|||
|
await self.test_execute_script_function()
|
|||
|
|
|||
|
# 第6步:测试VWED任务集成
|
|||
|
await self.test_vwed_task_integration()
|
|||
|
|
|||
|
# 第7步:测试API接口调用
|
|||
|
await self.test_dynamic_api_routes()
|
|||
|
|
|||
|
# 第8步:获取注册中心状态
|
|||
|
await self.test_registry_status()
|
|||
|
|
|||
|
# 第9步:停止脚本服务
|
|||
|
await self.test_stop_script_service()
|
|||
|
|
|||
|
logger.info("=== 脚本集成测试完成 ===")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"集成测试失败: {e}", exc_info=True)
|
|||
|
raise
|
|||
|
|
|||
|
async def test_create_project(self):
|
|||
|
"""测试创建项目"""
|
|||
|
logger.info("测试1: 创建脚本项目")
|
|||
|
|
|||
|
result = await self.file_service.create_project(
|
|||
|
project_name="测试项目_集成测试",
|
|||
|
description="用于集成测试的项目",
|
|||
|
created_by="test_user"
|
|||
|
)
|
|||
|
|
|||
|
assert result["success"], f"创建项目失败: {result.get('error')}"
|
|||
|
self.test_project_id = result["project"]["id"]
|
|||
|
|
|||
|
logger.info(f"✓ 项目创建成功,ID: {self.test_project_id}")
|
|||
|
|
|||
|
async def test_create_script_file(self):
|
|||
|
"""测试创建脚本文件"""
|
|||
|
logger.info("测试2: 创建脚本文件")
|
|||
|
|
|||
|
# 初始的简单脚本内容
|
|||
|
initial_content = '''def boot():
|
|||
|
"""脚本启动函数"""
|
|||
|
VWED.log.sync_info("测试脚本启动中...")
|
|||
|
|
|||
|
# 这里会在后续步骤中添加更多功能
|
|||
|
VWED.log.sync_info("测试脚本启动完成")
|
|||
|
'''
|
|||
|
|
|||
|
result = await self.file_service.create_file(
|
|||
|
project_id=self.test_project_id,
|
|||
|
file_name="test_integration.py",
|
|||
|
file_path="test_integration.py",
|
|||
|
content=initial_content,
|
|||
|
file_type="python",
|
|||
|
created_by="test_user"
|
|||
|
)
|
|||
|
|
|||
|
assert result["success"], f"创建文件失败: {result.get('error')}"
|
|||
|
self.test_file_id = result["file"]["id"]
|
|||
|
|
|||
|
logger.info(f"✓ 脚本文件创建成功,ID: {self.test_file_id}")
|
|||
|
|
|||
|
async def test_update_script_content(self):
|
|||
|
"""测试更新脚本内容"""
|
|||
|
logger.info("测试3: 编辑脚本内容")
|
|||
|
|
|||
|
# 完整的测试脚本内容
|
|||
|
updated_content = '''def boot():
|
|||
|
"""脚本启动函数 - 注册各种服务"""
|
|||
|
VWED.log.sync_info("集成测试脚本启动中...")
|
|||
|
|
|||
|
# 注册API接口
|
|||
|
setup_api_routes()
|
|||
|
|
|||
|
# 注册自定义函数
|
|||
|
setup_functions()
|
|||
|
|
|||
|
# 注册事件监听器
|
|||
|
setup_events()
|
|||
|
|
|||
|
# 注册定时任务
|
|||
|
setup_timers()
|
|||
|
|
|||
|
VWED.log.sync_info("集成测试脚本服务注册完成")
|
|||
|
|
|||
|
def setup_api_routes():
|
|||
|
"""设置API接口"""
|
|||
|
|
|||
|
@VWED.api.get("/test/hello", description="测试问候接口")
|
|||
|
def hello_api(request_data):
|
|||
|
name = request_data["query_params"].get("name", "World")
|
|||
|
VWED.log.sync_info(f"API调用: hello_api, name={name}")
|
|||
|
return {
|
|||
|
"message": f"Hello, {name}!",
|
|||
|
"timestamp": VWED.util.now(),
|
|||
|
"script_id": VWED.get_script_id()
|
|||
|
}
|
|||
|
|
|||
|
@VWED.api.post("/test/calculate", description="测试计算接口")
|
|||
|
def calculate_api(request_data):
|
|||
|
data = request_data["body"]
|
|||
|
a = data.get("a", 0)
|
|||
|
b = data.get("b", 0)
|
|||
|
operation = data.get("operation", "add")
|
|||
|
|
|||
|
if operation == "add":
|
|||
|
result = a + b
|
|||
|
elif operation == "subtract":
|
|||
|
result = a - b
|
|||
|
elif operation == "multiply":
|
|||
|
result = a * b
|
|||
|
elif operation == "divide":
|
|||
|
result = a / b if b != 0 else "Error: Division by zero"
|
|||
|
else:
|
|||
|
result = "Error: Unknown operation"
|
|||
|
|
|||
|
VWED.log.sync_info(f"API计算: {a} {operation} {b} = {result}")
|
|||
|
return {
|
|||
|
"operation": operation,
|
|||
|
"operand_a": a,
|
|||
|
"operand_b": b,
|
|||
|
"result": result,
|
|||
|
"timestamp": VWED.util.now()
|
|||
|
}
|
|||
|
|
|||
|
def setup_functions():
|
|||
|
"""设置自定义函数"""
|
|||
|
|
|||
|
@VWED.function.register("test_add", description="测试加法函数")
|
|||
|
def test_add(args):
|
|||
|
"""供VWED任务调用的加法函数"""
|
|||
|
a = args.get("a", 0)
|
|||
|
b = args.get("b", 0)
|
|||
|
result = a + b
|
|||
|
|
|||
|
VWED.log.sync_info(f"函数调用: test_add({a}, {b}) = {result}")
|
|||
|
return {
|
|||
|
"result": result,
|
|||
|
"operation": "addition",
|
|||
|
"inputs": {"a": a, "b": b}
|
|||
|
}
|
|||
|
|
|||
|
@VWED.function.register("test_process_data", description="测试数据处理函数")
|
|||
|
def test_process_data(args):
|
|||
|
"""数据处理测试函数"""
|
|||
|
data_list = args.get("data", [])
|
|||
|
process_type = args.get("type", "count")
|
|||
|
|
|||
|
if process_type == "count":
|
|||
|
result = len(data_list)
|
|||
|
elif process_type == "sum":
|
|||
|
result = sum(data_list) if all(isinstance(x, (int, float)) for x in data_list) else "Error: Non-numeric data"
|
|||
|
elif process_type == "filter_positive":
|
|||
|
result = [x for x in data_list if isinstance(x, (int, float)) and x > 0]
|
|||
|
else:
|
|||
|
result = data_list
|
|||
|
|
|||
|
VWED.log.sync_info(f"数据处理: type={process_type}, input_size={len(data_list)}, result={result}")
|
|||
|
return {
|
|||
|
"processed_data": result,
|
|||
|
"process_type": process_type,
|
|||
|
"input_size": len(data_list)
|
|||
|
}
|
|||
|
|
|||
|
@VWED.function.register("test_async_task", description="测试异步任务函数")
|
|||
|
async def test_async_task(args):
|
|||
|
"""异步任务测试函数"""
|
|||
|
task_name = args.get("task_name", "default_task")
|
|||
|
delay = args.get("delay", 1)
|
|||
|
|
|||
|
await VWED.log.info(f"异步任务开始: {task_name}, 延迟: {delay}秒")
|
|||
|
await VWED.util.async_sleep(delay)
|
|||
|
await VWED.log.info(f"异步任务完成: {task_name}")
|
|||
|
|
|||
|
return {
|
|||
|
"task_name": task_name,
|
|||
|
"delay_seconds": delay,
|
|||
|
"completed": True,
|
|||
|
"completion_time": VWED.util.now()
|
|||
|
}
|
|||
|
|
|||
|
def setup_events():
|
|||
|
"""设置事件监听器"""
|
|||
|
|
|||
|
@VWED.event.listen("test_event")
|
|||
|
def on_test_event(event_data):
|
|||
|
VWED.log.sync_info(f"接收到测试事件: {event_data}")
|
|||
|
|
|||
|
# 存储事件计数
|
|||
|
event_count = VWED.data.get("event_count", 0)
|
|||
|
VWED.data.set("event_count", event_count + 1)
|
|||
|
|
|||
|
@VWED.event.listen("task_completed", priority=1)
|
|||
|
def on_task_completed(event_data):
|
|||
|
task_id = event_data.get("task_id", "unknown")
|
|||
|
VWED.log.sync_info(f"任务完成通知: {task_id}")
|
|||
|
|
|||
|
# 记录完成的任务
|
|||
|
completed_tasks = VWED.data.get("completed_tasks", [])
|
|||
|
completed_tasks.append({
|
|||
|
"task_id": task_id,
|
|||
|
"completed_at": VWED.util.now()
|
|||
|
})
|
|||
|
VWED.data.set("completed_tasks", completed_tasks)
|
|||
|
|
|||
|
def setup_timers():
|
|||
|
"""设置定时任务"""
|
|||
|
|
|||
|
@VWED.timer.interval(30) # 每30秒执行一次
|
|||
|
def heartbeat_timer():
|
|||
|
heartbeat_count = VWED.data.get("heartbeat_count", 0)
|
|||
|
VWED.data.set("heartbeat_count", heartbeat_count + 1)
|
|||
|
VWED.log.sync_info(f"心跳检测 #{heartbeat_count + 1}")
|
|||
|
|
|||
|
@VWED.timer.once(delay=5) # 5秒后执行一次
|
|||
|
def initialization_timer():
|
|||
|
VWED.log.sync_info("初始化任务执行")
|
|||
|
VWED.data.set("initialized", True)
|
|||
|
VWED.data.set("init_time", VWED.util.now())
|
|||
|
|
|||
|
# 辅助函数
|
|||
|
def get_test_statistics():
|
|||
|
"""获取测试统计信息"""
|
|||
|
return {
|
|||
|
"event_count": VWED.data.get("event_count", 0),
|
|||
|
"heartbeat_count": VWED.data.get("heartbeat_count", 0),
|
|||
|
"completed_tasks": len(VWED.data.get("completed_tasks", [])),
|
|||
|
"initialized": VWED.data.get("initialized", False),
|
|||
|
"init_time": VWED.data.get("init_time", None)
|
|||
|
}
|
|||
|
'''
|
|||
|
|
|||
|
result = await self.file_service.update_file_content(
|
|||
|
file_id=self.test_file_id,
|
|||
|
content=updated_content,
|
|||
|
updated_by="test_user"
|
|||
|
)
|
|||
|
|
|||
|
assert result["success"], f"更新文件内容失败: {result.get('error')}"
|
|||
|
|
|||
|
logger.info("✓ 脚本内容更新成功,已添加完整的测试功能")
|
|||
|
|
|||
|
async def test_start_script_service(self):
|
|||
|
"""测试启动脚本服务"""
|
|||
|
logger.info("测试4: 启动脚本服务")
|
|||
|
|
|||
|
# 构建脚本路径
|
|||
|
script_path = f"projects/测试项目_集成测试/test_integration.py"
|
|||
|
|
|||
|
result = await self.script_engine.start_script_service(
|
|||
|
script_path=script_path,
|
|||
|
start_params={"test_mode": True}
|
|||
|
)
|
|||
|
|
|||
|
assert result["success"], f"启动脚本服务失败: {result.get('error')}"
|
|||
|
self.test_script_id = result["script_id"]
|
|||
|
|
|||
|
logger.info(f"✓ 脚本服务启动成功,Script ID: {self.test_script_id}")
|
|||
|
logger.info(f" - 注册的接口数: {result['registrations']['apis']}")
|
|||
|
logger.info(f" - 注册的函数数: {result['registrations']['functions']}")
|
|||
|
logger.info(f" - 注册的事件数: {result['registrations']['events']}")
|
|||
|
logger.info(f" - 注册的定时器数: {result['registrations']['timers']}")
|
|||
|
|
|||
|
async def test_execute_script_function(self):
|
|||
|
"""测试执行脚本函数"""
|
|||
|
logger.info("测试5: 执行脚本函数")
|
|||
|
|
|||
|
# 测试同步函数
|
|||
|
result1 = await self.script_engine.execute_script_function(
|
|||
|
script_id=self.test_script_id,
|
|||
|
function_name="test_add",
|
|||
|
function_args={"a": 15, "b": 25}
|
|||
|
)
|
|||
|
|
|||
|
assert result1["success"], f"执行test_add函数失败: {result1.get('error')}"
|
|||
|
assert result1["result"]["result"] == 40, "加法函数计算结果错误"
|
|||
|
logger.info(f"✓ 同步函数test_add执行成功: 15 + 25 = {result1['result']['result']}")
|
|||
|
|
|||
|
# 测试数据处理函数
|
|||
|
result2 = await self.script_engine.execute_script_function(
|
|||
|
script_id=self.test_script_id,
|
|||
|
function_name="test_process_data",
|
|||
|
function_args={"data": [1, 2, 3, -1, 4, -2, 5], "type": "filter_positive"}
|
|||
|
)
|
|||
|
|
|||
|
assert result2["success"], f"执行test_process_data函数失败: {result2.get('error')}"
|
|||
|
expected_result = [1, 2, 3, 4, 5]
|
|||
|
assert result2["result"]["processed_data"] == expected_result, "数据过滤结果错误"
|
|||
|
logger.info(f"✓ 数据处理函数执行成功: 过滤出正数 {expected_result}")
|
|||
|
|
|||
|
# 测试异步函数
|
|||
|
result3 = await self.script_engine.execute_script_function(
|
|||
|
script_id=self.test_script_id,
|
|||
|
function_name="test_async_task",
|
|||
|
function_args={"task_name": "integration_test", "delay": 0.5}
|
|||
|
)
|
|||
|
|
|||
|
assert result3["success"], f"执行test_async_task函数失败: {result3.get('error')}"
|
|||
|
assert result3["result"]["completed"] == True, "异步任务未正确完成"
|
|||
|
logger.info(f"✓ 异步函数test_async_task执行成功")
|
|||
|
|
|||
|
async def test_vwed_task_integration(self):
|
|||
|
"""测试VWED任务系统集成"""
|
|||
|
logger.info("测试6: VWED任务系统集成")
|
|||
|
|
|||
|
# 获取可用的脚本函数列表
|
|||
|
available_functions = await self.task_integration.get_available_functions()
|
|||
|
|
|||
|
assert len(available_functions) > 0, "没有可用的脚本函数"
|
|||
|
|
|||
|
function_names = [func["function_name"] for func in available_functions]
|
|||
|
assert "test_add" in function_names, "test_add函数未在可用函数列表中"
|
|||
|
assert "test_process_data" in function_names, "test_process_data函数未在可用函数列表中"
|
|||
|
|
|||
|
logger.info(f"✓ 可用脚本函数: {function_names}")
|
|||
|
|
|||
|
# 模拟从VWED任务中调用脚本函数
|
|||
|
task_context = {
|
|||
|
"task_id": "test_task_001",
|
|||
|
"instance_id": "test_instance_001",
|
|||
|
"block_id": "script_block_001"
|
|||
|
}
|
|||
|
|
|||
|
result = await self.task_integration.execute_script_function_from_task(
|
|||
|
function_name="test_add",
|
|||
|
function_args={"a": 100, "b": 200},
|
|||
|
task_context=task_context
|
|||
|
)
|
|||
|
|
|||
|
assert result["success"], f"从任务调用脚本函数失败: {result.get('error')}"
|
|||
|
assert result["result"]["result"] == 300, "任务调用脚本函数计算结果错误"
|
|||
|
|
|||
|
logger.info(f"✓ VWED任务调用脚本函数成功: 100 + 200 = {result['result']['result']}")
|
|||
|
|
|||
|
async def test_dynamic_api_routes(self):
|
|||
|
"""测试动态API路由(模拟)"""
|
|||
|
logger.info("测试7: 动态API路由测试")
|
|||
|
|
|||
|
# 获取注册的API路由信息
|
|||
|
registrations = self.registry.get_all_registrations()
|
|||
|
api_routes = registrations.get("api_routes", {})
|
|||
|
|
|||
|
# 检查是否注册了预期的API路由
|
|||
|
expected_routes = ["GET:/test/hello", "POST:/test/calculate"]
|
|||
|
|
|||
|
for route_key in expected_routes:
|
|||
|
assert route_key in api_routes, f"API路由 {route_key} 未注册"
|
|||
|
route_info = api_routes[route_key]
|
|||
|
assert route_info["script_id"] == self.test_script_id, f"路由 {route_key} 的script_id不匹配"
|
|||
|
|
|||
|
logger.info(f"✓ 动态API路由注册成功: {list(api_routes.keys())}")
|
|||
|
|
|||
|
# 这里可以进一步测试实际的HTTP请求(需要启动FastAPI服务器)
|
|||
|
# 由于是集成测试,我们主要验证注册是否正确
|
|||
|
|
|||
|
async def test_registry_status(self):
|
|||
|
"""测试获取注册中心状态"""
|
|||
|
logger.info("测试8: 获取注册中心状态")
|
|||
|
|
|||
|
registrations = self.registry.get_all_registrations()
|
|||
|
|
|||
|
# 验证注册信息
|
|||
|
assert len(registrations["api_routes"]) >= 2, "API路由注册数量不足"
|
|||
|
assert len(registrations["registered_functions"]) >= 3, "函数注册数量不足"
|
|||
|
assert len(registrations["event_listeners"]) >= 1, "事件监听器注册数量不足"
|
|||
|
assert len(registrations["active_scripts"]) >= 1, "活跃脚本数量不足"
|
|||
|
|
|||
|
logger.info("✓ 注册中心状态检查通过")
|
|||
|
logger.info(f" - API路由数: {len(registrations['api_routes'])}")
|
|||
|
logger.info(f" - 注册函数数: {len(registrations['registered_functions'])}")
|
|||
|
logger.info(f" - 事件监听器数: {len(registrations['event_listeners'])}")
|
|||
|
logger.info(f" - 活跃脚本数: {len(registrations['active_scripts'])}")
|
|||
|
|
|||
|
async def test_stop_script_service(self):
|
|||
|
"""测试停止脚本服务"""
|
|||
|
logger.info("测试9: 停止脚本服务")
|
|||
|
|
|||
|
result = await self.script_engine.stop_script_service(self.test_script_id)
|
|||
|
|
|||
|
assert result["success"], f"停止脚本服务失败: {result.get('error')}"
|
|||
|
|
|||
|
logger.info("✓ 脚本服务停止成功")
|
|||
|
|
|||
|
# 验证注册项已被清理
|
|||
|
running_scripts = self.script_engine.get_running_scripts()
|
|||
|
script_ids = [s["script_id"] for s in running_scripts]
|
|||
|
assert self.test_script_id not in script_ids, "脚本服务未正确停止"
|
|||
|
|
|||
|
logger.info("✓ 脚本注册项清理完成")
|
|||
|
|
|||
|
|
|||
|
async def main():
|
|||
|
"""主测试函数"""
|
|||
|
try:
|
|||
|
# 创建测试实例
|
|||
|
test = ScriptIntegrationTest()
|
|||
|
|
|||
|
# 运行完整的集成测试
|
|||
|
await test.run_full_test()
|
|||
|
|
|||
|
print("\n" + "="*60)
|
|||
|
print("🎉 脚本集成测试全部通过!")
|
|||
|
print("="*60)
|
|||
|
print("测试覆盖的功能:")
|
|||
|
print("✓ 项目和文件管理")
|
|||
|
print("✓ 脚本内容编辑")
|
|||
|
print("✓ 脚本服务启动和停止")
|
|||
|
print("✓ API接口动态注册")
|
|||
|
print("✓ 自定义函数注册和调用")
|
|||
|
print("✓ 事件系统和定时任务")
|
|||
|
print("✓ VWED任务系统集成")
|
|||
|
print("✓ 注册中心状态管理")
|
|||
|
print("="*60)
|
|||
|
|
|||
|
return True
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
print("\n" + "="*60)
|
|||
|
print("❌ 脚本集成测试失败!")
|
|||
|
print("="*60)
|
|||
|
print(f"错误信息: {e}")
|
|||
|
logger.error(f"集成测试失败: {e}", exc_info=True)
|
|||
|
print("="*60)
|
|||
|
|
|||
|
return False
|
|||
|
|
|||
|
|
|||
|
if __name__ == "__main__":
|
|||
|
# 运行测试
|
|||
|
success = asyncio.run(main())
|
|||
|
sys.exit(0 if success else 1)
|