From f29c56d2b095bf2508639e4d91e8893546eb1839 Mon Sep 17 00:00:00 2001 From: icn5381 <1655464034@qq.com> Date: Tue, 22 Sep 2026 21:53:36 +0800 Subject: [PATCH] test(wecom): cover SDK compatibility fallback in shutdown (#4788) References #4762 Co-authored-by: icn5381 <255778606+icn5381@users.noreply.github.com> --- backend/tests/test_channels.py | 66 ++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/backend/tests/test_channels.py b/backend/tests/test_channels.py index 5f64c63b3..6755cd10d 100644 --- a/backend/tests/test_channels.py +++ b/backend/tests/test_channels.py @@ -7176,6 +7176,72 @@ class TestWeComChannel: _run(go()) + def test_stop_uses_sync_disconnect_fallback_without_sdk_async_helpers(self): + """A client that exposes neither ``_ws_manager`` (and therefore no + ``_async_disconnect`` / ``_stop_heartbeat`` / ``_clear_pending_messages``) + nor an awaitable ``disconnect()`` takes the compatibility fallback: + ``disconnect()`` is invoked once and ``stop()`` still clears its + lifecycle references.""" + from app.channels.wecom import WeComChannel + + async def go(): + channel = WeComChannel(MessageBus(), config={}) + disconnect_called = asyncio.Event() + ws_task = asyncio.create_task(asyncio.sleep(0)) + await ws_task + + class LegacySyncSDKClient: + def disconnect(self) -> None: + disconnect_called.set() + + client = LegacySyncSDKClient() + channel._running = True + channel._ws_client = client + channel._ws_task = ws_task + + await channel.stop() + + assert disconnect_called.is_set() + assert channel._ws_client is None + assert channel._ws_task is None + assert channel._ws_shutdown_task is None + + _run(go()) + + def test_stop_awaits_awaitable_disconnect_fallback(self): + """A client whose ``disconnect()`` returns an awaitable (but has no + ``_ws_manager``) takes the fallback path and ``stop()`` waits for that + awaitable to finish before clearing its lifecycle references.""" + from app.channels.wecom import WeComChannel + + async def go(): + channel = WeComChannel(MessageBus(), config={}) + disconnect_called = asyncio.Event() + disconnect_finished = asyncio.Event() + ws_task = asyncio.create_task(asyncio.sleep(0)) + await ws_task + + class AwaitableSDKClient: + async def disconnect(self): + disconnect_called.set() + await asyncio.sleep(0) + disconnect_finished.set() + + client = AwaitableSDKClient() + channel._running = True + channel._ws_client = client + channel._ws_task = ws_task + + await channel.stop() + + assert disconnect_called.is_set() + assert disconnect_finished.is_set() + assert channel._ws_client is None + assert channel._ws_task is None + assert channel._ws_shutdown_task is None + + _run(go()) + def test_concurrent_start_waits_for_stop_before_installing_new_client(self, monkeypatch): from app.channels.wecom import WeComChannel