From c50d6f177134916ef28d799910cea3bde3fcc12d Mon Sep 17 00:00:00 2001 From: longpeng Date: Fri, 20 Jun 2025 16:28:33 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BAAPI=E5=92=8CUI=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=B4=BB=E5=8A=A8=E5=BC=95=E5=85=A5=E5=BF=83=E8=B7=B3=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=EF=BC=8C=E4=BB=A5=E5=A2=9E=E5=BC=BATemporal=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E7=9A=84=E5=81=A5=E5=A3=AE=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- workers/python/activities.py | 51 +++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/workers/python/activities.py b/workers/python/activities.py index 3e55b71..75d79b6 100644 --- a/workers/python/activities.py +++ b/workers/python/activities.py @@ -2,6 +2,7 @@ import os import sys import time +import asyncio from temporalio import activity @@ -15,15 +16,35 @@ from utils import upload_file_to_s3, scalar_map_to_dict class TestActivities: + + async def _heartbeat_task(self, interval_seconds=30): + """心跳任务,定期发送心跳信号""" + while True: + try: + await asyncio.sleep(interval_seconds) + activity.heartbeat() + activity.logger.debug("Activity heartbeat sent") + except asyncio.CancelledError: + activity.logger.debug("Heartbeat task cancelled") + break + except Exception as e: + activity.logger.warning(f"Failed to send heartbeat: {e}") + @activity.defn(name="run_api_test") - async def run_api_test(self,req: pb.ApiTestRequest) -> pb.ApiTestResult: + async def run_api_test(self, req: pb.ApiTestRequest) -> pb.ApiTestResult: """执行API测试的Temporal Activity实现""" activity.logger.info(f"Received API Test Request: {req.test_case_id}") start_time = time.time() result = pb.ApiTestResult() result.base_result.test_case_id = req.test_case_id + # 启动心跳任务 + heartbeat_task = asyncio.create_task(self._heartbeat_task()) + try: + # 发送初始心跳 + activity.heartbeat() + # 调用实际的API测试逻辑 api_test_success, actual_status, response_body, log_output = execute_api_test_case( req.test_case_id, req.endpoint, req.http_method, scalar_map_to_dict(req.headers), req.request_body, @@ -41,20 +62,32 @@ class TestActivities: result.base_result.success = False result.base_result.message = f"API Test Error: {e}" result.base_result.error_details = str(e) + finally: + # 取消心跳任务 + heartbeat_task.cancel() + try: + await heartbeat_task + except asyncio.CancelledError: + pass result.base_result.duration_seconds = time.time() - start_time return result - @activity.defn(name="run_ui_test") - async def run_ui_test(self,req: pb.UiTestRequest) -> pb.UiTestResult: + async def run_ui_test(self, req: pb.UiTestRequest) -> pb.UiTestResult: """执行UI测试的Temporal Activity实现""" activity.logger.info(f"Received UI Test Request: {req.test_case_id}") start_time = time.time() result = pb.UiTestResult() result.base_result.test_case_id = req.test_case_id + # 启动心跳任务 + heartbeat_task = asyncio.create_task(self._heartbeat_task()) + try: + # 发送初始心跳 + activity.heartbeat() + # 调用实际的UI测试逻辑,返回本地文件路径 ui_test_success, log_output, screenshot_path, html_report_path = await execute_ui_test_case( req.test_case_id, req.url_path, req.browser_type, req.headless, scalar_map_to_dict(req.user_data) @@ -66,9 +99,14 @@ class TestActivities: # 上传截图和报告到对象存储,并返回URL if screenshot_path and os.path.exists(screenshot_path): + # 在长时间操作前发送心跳 + activity.heartbeat() result.screenshot_url = await upload_file_to_s3(screenshot_path, f"screenshots/{req.test_case_id}.png") os.remove(screenshot_path) # 清理本地文件 + if html_report_path and os.path.exists(html_report_path): + # 在长时间操作前发送心跳 + activity.heartbeat() result.html_report_url = await upload_file_to_s3(html_report_path, f"reports/{req.test_case_id}.html") os.remove(html_report_path) # 清理本地文件 @@ -77,6 +115,13 @@ class TestActivities: result.base_result.success = False result.base_result.message = f"UI Test Error: {e}" result.base_result.error_details = str(e) + finally: + # 取消心跳任务 + heartbeat_task.cancel() + try: + await heartbeat_task + except asyncio.CancelledError: + pass result.base_result.duration_seconds = time.time() - start_time return result \ No newline at end of file