from __future__ import annotations from unittest.mock import AsyncMock, MagicMock, patch import httpx import pytest from yuxi.channels.adapters.wechat.mp.client import MPClient from yuxi.channels.adapters.wechat.mp.send import ( send_mp_custom_message, send_mp_template_message, send_mp_image, send_mp_voice, send_mp_video, send_mp_news, build_mp_news_payload, ) from yuxi.channels.adapters.wechat.wecom.client import WeComClient from yuxi.channels.adapters.wechat.wecom.send import ( send_wecom_message, send_wecom_voice, send_wecom_video, build_wecom_text_payload, ) from yuxi.channels.models import DeliveryResult class TestSendWeComMessage: def setup_method(self): self.http_client = AsyncMock(spec=httpx.AsyncClient) self.config = {"corp_id": "test_corp", "agent_id": "100001", "corp_secret": "test_secret"} self.client = WeComClient(self.http_client, self.config) self.client.set_token("test_token", 9999999999.0) @pytest.mark.asyncio async def test_send_success(self): mock_resp = MagicMock() mock_resp.json.return_value = {"errcode": 0, "msgid": "msg_001"} self.http_client.post.return_value = mock_resp payload = build_wecom_text_payload("100001", "user1", "hello") result = await send_wecom_message(self.client, self.http_client, payload) assert result.success is True assert result.message_id == "msg_001" @pytest.mark.asyncio async def test_send_token_expired_retries(self): mock_resp_expired = MagicMock() mock_resp_expired.json.return_value = {"errcode": 42001, "errmsg": "token expired"} mock_resp_success = MagicMock() mock_resp_success.json.return_value = {"errcode": 0, "msgid": "retry_msg"} mock_resp_token = MagicMock() mock_resp_token.json.return_value = {"access_token": "new_token", "expires_in": 7200} self.http_client.post.side_effect = [mock_resp_expired, mock_resp_success] self.http_client.get.return_value = mock_resp_token payload = build_wecom_text_payload("100001", "user1", "hello") result = await send_wecom_message(self.client, self.http_client, payload) assert result.success is True @pytest.mark.asyncio async def test_send_http_error(self): self.http_client.post.side_effect = httpx.NetworkError("connection refused") payload = build_wecom_text_payload("100001", "user1", "hello") result = await send_wecom_message(self.client, self.http_client, payload) assert result.success is False assert result.error is not None @pytest.mark.asyncio async def test_send_api_error(self): mock_resp = MagicMock() mock_resp.json.return_value = {"errcode": 40003, "errmsg": "invalid userid"} self.http_client.post.return_value = mock_resp payload = build_wecom_text_payload("100001", "invalid_user", "hello") result = await send_wecom_message(self.client, self.http_client, payload) assert result.success is False class TestSendMpMessage: def setup_method(self): self.http_client = AsyncMock(spec=httpx.AsyncClient) self.config = {"app_id": "wx_test", "app_secret": "test_secret"} self.client = MPClient(self.http_client, self.config) self.client.set_token("mp_token", 9999999999.0) @pytest.mark.asyncio async def test_send_custom_message_success(self): mock_resp = MagicMock() mock_resp.json.return_value = {"errcode": 0, "msgid": "mp_msg_001"} self.http_client.post.return_value = mock_resp result = await send_mp_custom_message(self.client, self.http_client, "user1", "hello") assert result.success is True assert result.message_id == "mp_msg_001" @pytest.mark.asyncio async def test_send_custom_message_with_reply(self): mock_resp = MagicMock() mock_resp.json.return_value = {"errcode": 0, "msgid": "mp_reply_001"} self.http_client.post.return_value = mock_resp result = await send_mp_custom_message( self.client, self.http_client, "user1", "reply content", reply_to_msg_id="orig_123", reply_to_user="sender", ) assert result.success is True @pytest.mark.asyncio async def test_send_custom_message_http_error(self): self.http_client.post.side_effect = httpx.NetworkError("connection error") result = await send_mp_custom_message(self.client, self.http_client, "user1", "hello") assert result.success is False @pytest.mark.asyncio async def test_send_template_message_success(self): mock_resp = MagicMock() mock_resp.json.return_value = {"errcode": 0, "msgid": "tpl_msg_001"} self.http_client.post.return_value = mock_resp result = await send_mp_template_message( self.client, self.http_client, "user1", "template_001", {"first": {"value": "hello"}}, url="http://example.com", ) assert result.success is True assert result.message_id == "tpl_msg_001" @pytest.mark.asyncio async def test_send_mp_image_upload_failure(self): self.client.upload_temp_media = AsyncMock(side_effect=RuntimeError("upload failed")) result = await send_mp_image(self.client, self.http_client, "user1", b"fake_image") assert result.success is False assert "upload failed" in (result.error or "") @pytest.mark.asyncio async def test_send_mp_voice_upload_failure(self): self.client.upload_temp_media = AsyncMock(side_effect=RuntimeError("voice upload failed")) result = await send_mp_voice(self.client, self.http_client, "user1", b"fake_voice") assert result.success is False @pytest.mark.asyncio async def test_send_mp_video_upload_failure(self): self.client.upload_temp_media = AsyncMock(side_effect=RuntimeError("video upload failed")) result = await send_mp_video(self.client, self.http_client, "user1", b"fake_video") assert result.success is False @pytest.mark.asyncio async def test_send_mp_news_success(self): mock_resp = MagicMock() mock_resp.json.return_value = {"errcode": 0, "msgid": "news_001"} self.http_client.post.return_value = mock_resp payload = build_mp_news_payload("user1", [{"title": "News1", "url": "http://example.com"}]) result = await send_mp_news(self.client, self.http_client, payload) assert result.success is True @pytest.mark.asyncio async def test_send_wecom_voice_upload_failure(self): wecom_client = WeComClient(AsyncMock(spec=httpx.AsyncClient), { "corp_id": "test", "agent_id": "100001", "corp_secret": "secret", }) wecom_client.set_token("token", 9999999999.0) wecom_client.upload_media = AsyncMock(side_effect=RuntimeError("upload failed")) result = await send_wecom_voice(wecom_client, AsyncMock(), "100001", "user1", b"data") assert result.success is False @pytest.mark.asyncio async def test_send_wecom_video_upload_failure(self): wecom_client = WeComClient(AsyncMock(spec=httpx.AsyncClient), { "corp_id": "test", "agent_id": "100001", "corp_secret": "secret", }) wecom_client.set_token("token", 9999999999.0) wecom_client.upload_media = AsyncMock(side_effect=RuntimeError("upload failed")) result = await send_wecom_video(wecom_client, AsyncMock(), "100001", "user1", b"data") assert result.success is False