From 7c932c5aa445666eeeec0ba923e0da0938353c5b Mon Sep 17 00:00:00 2001 From: Asunfly Date: Wed, 15 Apr 2026 12:30:41 +0800 Subject: [PATCH] fix(dingtalk): close websocket on disconnect --- gateway/platforms/dingtalk.py | 13 ++++++++++--- tests/gateway/test_dingtalk.py | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/gateway/platforms/dingtalk.py b/gateway/platforms/dingtalk.py index 424c7af52..2e2e25131 100644 --- a/gateway/platforms/dingtalk.py +++ b/gateway/platforms/dingtalk.py @@ -154,12 +154,19 @@ class DingTalkAdapter(BasePlatformAdapter): self._running = False self._mark_disconnected() + websocket = getattr(self._stream_client, "websocket", None) + if websocket is not None: + try: + await websocket.close() + except Exception as e: + logger.debug("[%s] websocket close during disconnect failed: %s", self.name, e) + if self._stream_task: self._stream_task.cancel() try: - await self._stream_task - except asyncio.CancelledError: - pass + await asyncio.wait_for(self._stream_task, timeout=2.0) + except (asyncio.CancelledError, asyncio.TimeoutError): + logger.debug("[%s] stream task did not exit cleanly during disconnect", self.name) self._stream_task = None if self._http_client: diff --git a/tests/gateway/test_dingtalk.py b/tests/gateway/test_dingtalk.py index 0894c0187..453835ec9 100644 --- a/tests/gateway/test_dingtalk.py +++ b/tests/gateway/test_dingtalk.py @@ -2,6 +2,7 @@ import asyncio import json from datetime import datetime, timezone +from types import SimpleNamespace from unittest.mock import AsyncMock, MagicMock, patch, PropertyMock import pytest @@ -230,6 +231,29 @@ class TestSend: class TestConnect: + @pytest.mark.asyncio + async def test_disconnect_closes_session_websocket(self): + from gateway.platforms.dingtalk import DingTalkAdapter + + adapter = DingTalkAdapter(PlatformConfig(enabled=True)) + websocket = AsyncMock() + blocker = asyncio.Event() + + async def _run_forever(): + try: + await blocker.wait() + except asyncio.CancelledError: + return + + adapter._stream_client = SimpleNamespace(websocket=websocket) + adapter._stream_task = asyncio.create_task(_run_forever()) + adapter._running = True + + await adapter.disconnect() + + websocket.close.assert_awaited_once() + assert adapter._stream_task is None + @pytest.mark.asyncio async def test_connect_fails_without_sdk(self, monkeypatch): monkeypatch.setattr(