from __future__ import annotations import asyncio from unittest.mock import AsyncMock, MagicMock import pytest from yuxi.channels.adapters.wechat.wecom.monitor import WeComMonitor class TestWeComMonitor: def setup_method(self): self.http_client = AsyncMock() self.config = {"heartbeat_interval": 0.2} self.message_handler = AsyncMock() self.monitor = WeComMonitor(self.http_client, self.config, self.message_handler) @pytest.mark.asyncio async def test_start_creates_heartbeat_task(self): token_refresh = AsyncMock() await self.monitor.start(token_refresh) assert self.monitor._running is True assert self.monitor._heartbeat_task is not None await self.monitor.stop() @pytest.mark.asyncio async def test_stop_cancels_task(self): token_refresh = AsyncMock() await self.monitor.start(token_refresh) await self.monitor.stop() assert self.monitor._running is False assert self.monitor._heartbeat_task is None @pytest.mark.asyncio async def test_heartbeat_calls_token_refresh(self): token_refresh = AsyncMock() await self.monitor.start(token_refresh) await asyncio.sleep(0.4) await self.monitor.stop() assert token_refresh.call_count >= 1 @pytest.mark.asyncio async def test_heartbeat_error_does_not_crash(self): token_refresh = AsyncMock(side_effect=RuntimeError("refresh error")) await self.monitor.start(token_refresh) await asyncio.sleep(0.4) await self.monitor.stop() assert self.monitor._running is False @pytest.mark.asyncio async def test_double_stop_no_error(self): token_refresh = AsyncMock() await self.monitor.start(token_refresh) await self.monitor.stop() await self.monitor.stop() @pytest.mark.asyncio async def test_stop_when_not_running(self): await self.monitor.stop() def test_process_webhook_message(self): normalize_func = MagicMock(return_value="normalized_message") raw = {"Content": "hello"} result = self.monitor.process_webhook_message(raw, normalize_func) normalize_func.assert_called_once_with(raw) assert result == "normalized_message"