mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-06-29 19:51:54 +08:00
fix: clean up feishu websocket shutdown
This commit is contained in:
@@ -1205,9 +1205,14 @@ class TestFeishu(unittest.TestCase):
|
||||
future = MagicMock()
|
||||
future.result.return_value = None
|
||||
|
||||
def _run_threadsafe(coro, loop):
|
||||
"""模拟线程安全调度并关闭测试协程,避免产生未等待告警。"""
|
||||
coro.close()
|
||||
return future
|
||||
|
||||
with patch(
|
||||
"app.modules.feishu.feishu.asyncio.run_coroutine_threadsafe",
|
||||
return_value=future,
|
||||
side_effect=_run_threadsafe,
|
||||
) as runner:
|
||||
client.stop()
|
||||
|
||||
|
||||
94
tests/test_feishu_ws_lifecycle.py
Normal file
94
tests/test_feishu_ws_lifecycle.py
Normal file
@@ -0,0 +1,94 @@
|
||||
import asyncio
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from app.testing.bootstrap import ensure_optional_stub
|
||||
|
||||
# 可选三方依赖在 CI / 全新环境可能未安装,补占位避免 app.modules.feishu 导入失败
|
||||
ensure_optional_stub("psutil")
|
||||
ensure_optional_stub("dateparser")
|
||||
ensure_optional_stub("Pinyin2Hanzi", is_pinyin=lambda value: False)
|
||||
|
||||
from app.modules.feishu.feishu import Feishu
|
||||
|
||||
|
||||
def _build_feishu_client() -> Feishu:
|
||||
"""构造不会启动真实飞书长连接的测试客户端。"""
|
||||
with (
|
||||
patch.object(Feishu, "_build_api_client", return_value=MagicMock()),
|
||||
patch.object(Feishu, "_start_ws_client"),
|
||||
):
|
||||
return Feishu(
|
||||
FEISHU_APP_ID="cli_test_app_id",
|
||||
FEISHU_APP_SECRET="cli_test_app_secret",
|
||||
name="feishu-test",
|
||||
)
|
||||
|
||||
|
||||
async def _wait_forever() -> None:
|
||||
"""模拟飞书 SDK 创建的长生命周期后台任务。"""
|
||||
await asyncio.Future()
|
||||
|
||||
|
||||
def test_shutdown_ws_client_cancels_sdk_tasks_before_quiet_disconnect():
|
||||
"""飞书关机清理应先消费后台任务,再静默关闭 WebSocket 连接。"""
|
||||
client = _build_feishu_client()
|
||||
loop = asyncio.new_event_loop()
|
||||
closed = False
|
||||
|
||||
async def _close_conn() -> None:
|
||||
"""记录测试连接已被关闭。"""
|
||||
nonlocal closed
|
||||
closed = True
|
||||
|
||||
try:
|
||||
asyncio.set_event_loop(loop)
|
||||
task = loop.create_task(_wait_forever())
|
||||
task.add_done_callback(client._consume_ws_task_result)
|
||||
client._ws_tasks.add(task)
|
||||
ws_client = SimpleNamespace(
|
||||
_auto_reconnect=True,
|
||||
_conn=SimpleNamespace(close=_close_conn),
|
||||
_conn_url="wss://msg-frontier.feishu.cn/ws/v2?access_key=secret&ticket=secret",
|
||||
_conn_id="conn_test",
|
||||
_service_id="service_test",
|
||||
_lock=asyncio.Lock(),
|
||||
)
|
||||
client._ws_client = ws_client
|
||||
|
||||
loop.run_until_complete(client._shutdown_ws_client())
|
||||
finally:
|
||||
loop.close()
|
||||
asyncio.set_event_loop(None)
|
||||
|
||||
assert task.cancelled()
|
||||
assert closed
|
||||
assert not ws_client._auto_reconnect
|
||||
assert ws_client._conn is None
|
||||
assert ws_client._conn_url == ""
|
||||
assert ws_client._conn_id == ""
|
||||
assert ws_client._service_id == ""
|
||||
assert client._ws_tasks == set()
|
||||
|
||||
|
||||
def test_consume_ws_task_result_suppresses_stop_exception():
|
||||
"""停止过程中飞书 SDK 后台任务的异常应被取回并降为调试日志。"""
|
||||
client = _build_feishu_client()
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
future = loop.create_future()
|
||||
future.set_exception(RuntimeError("normal shutdown"))
|
||||
client._ws_tasks.add(future)
|
||||
client._stop_event.set()
|
||||
|
||||
with (
|
||||
patch("app.modules.feishu.feishu.logger.debug") as debug_logger,
|
||||
patch("app.modules.feishu.feishu.logger.error") as error_logger,
|
||||
):
|
||||
client._consume_ws_task_result(future)
|
||||
finally:
|
||||
loop.close()
|
||||
|
||||
debug_logger.assert_called_once()
|
||||
error_logger.assert_not_called()
|
||||
assert future not in client._ws_tasks
|
||||
Reference in New Issue
Block a user