新增了Twitch、Telegram、Discord、Slack、Mattermost、WeChat、Zalo等多渠道的单元测试用例,覆盖了令牌处理、速率限制、消息去重、会话解析、格式转换、安全策略等模块 同时在测试配置中添加了测试用的OpenAI API密钥环境变量
602 lines
24 KiB
Python
602 lines
24 KiB
Python
from __future__ import annotations
|
|
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
from yuxi.channels.adapters.imessage.adapter import IMessageAdapter
|
|
from yuxi.channels.adapters.imessage.session import resolve_chat_type, resolve_thread_key
|
|
from yuxi.channels.adapters.imessage.tapback import (
|
|
describe_tapback,
|
|
emoji_to_tapback,
|
|
is_valid_tapback,
|
|
resolve_tapback_value,
|
|
tapback_to_emoji,
|
|
)
|
|
from yuxi.channels.models import (
|
|
ChannelIdentity,
|
|
ChannelMessage,
|
|
ChannelResponse,
|
|
ChannelType,
|
|
ChatType,
|
|
DeliveryResult,
|
|
EventType,
|
|
MessageType,
|
|
)
|
|
|
|
|
|
def make_dm_event(text="Hello world", sender="+8613800138000", chat_guid="iMessage;+;chat001", msg_guid="msg_001"):
|
|
return {
|
|
"type": "new-message",
|
|
"data": {
|
|
"chatGuid": chat_guid,
|
|
"guid": msg_guid,
|
|
"text": text,
|
|
"sender": sender,
|
|
"isFromMe": False,
|
|
"attachments": [],
|
|
"dateCreated": 1700000000000,
|
|
},
|
|
}
|
|
|
|
|
|
def make_group_event(text="Hello group", sender="+8613800138000", chat_guid="iMessage;-;group001", msg_guid="msg_003"):
|
|
return {
|
|
"type": "new-message",
|
|
"data": {
|
|
"chatGuid": chat_guid,
|
|
"guid": msg_guid,
|
|
"text": text,
|
|
"sender": sender,
|
|
"isFromMe": False,
|
|
"attachments": [],
|
|
"dateCreated": 1700000000000,
|
|
},
|
|
}
|
|
|
|
|
|
def make_image_event(chat_guid="iMessage;+;chat001", msg_guid="msg_002"):
|
|
return {
|
|
"type": "new-message",
|
|
"data": {
|
|
"chatGuid": chat_guid,
|
|
"guid": msg_guid,
|
|
"text": "Check this photo",
|
|
"sender": "+8613800138000",
|
|
"isFromMe": False,
|
|
"attachments": [
|
|
{
|
|
"mimeType": "image/jpeg",
|
|
"url": "/media/img001.jpg",
|
|
"transferName": "photo.jpg",
|
|
"totalBytes": 1024000,
|
|
}
|
|
],
|
|
"dateCreated": 1700000000000,
|
|
},
|
|
}
|
|
|
|
|
|
def make_response(chat_guid="iMessage;+;chat001", content="Hello from ForcePilot"):
|
|
return ChannelResponse(
|
|
identity=ChannelIdentity(
|
|
channel_id="imessage",
|
|
channel_type=ChannelType.IMESSAGE,
|
|
channel_user_id="+8613800138000",
|
|
channel_chat_id=chat_guid,
|
|
channel_message_id="msg_001",
|
|
),
|
|
content=content,
|
|
)
|
|
|
|
|
|
class TestIMessageSession:
|
|
def test_resolve_direct_chat(self):
|
|
assert resolve_chat_type("iMessage;+;chat001") == ChatType.DIRECT
|
|
|
|
def test_resolve_group_chat_separator(self):
|
|
assert resolve_chat_type("iMessage;-;group001") == ChatType.GROUP
|
|
|
|
def test_resolve_group_chat_suffix(self):
|
|
assert resolve_chat_type("chat123@chat") == ChatType.GROUP
|
|
|
|
def test_resolve_thread_key_direct(self):
|
|
identity = ChannelIdentity(
|
|
channel_id="imessage",
|
|
channel_type=ChannelType.IMESSAGE,
|
|
channel_user_id="+8613800138000",
|
|
channel_chat_id="iMessage;+;chat001",
|
|
)
|
|
key = resolve_thread_key(identity)
|
|
assert "dm" in key
|
|
assert "8613800138000" in key
|
|
|
|
def test_resolve_thread_key_group(self):
|
|
identity = ChannelIdentity(
|
|
channel_id="imessage",
|
|
channel_type=ChannelType.IMESSAGE,
|
|
channel_user_id="+8613800138000",
|
|
channel_chat_id="iMessage;-;group001",
|
|
)
|
|
key = resolve_thread_key(identity)
|
|
assert "group" in key
|
|
assert "group001" in key
|
|
|
|
|
|
class TestIMessageTapback:
|
|
def test_emoji_to_tapback_known(self):
|
|
assert emoji_to_tapback("❤️") == "love"
|
|
assert emoji_to_tapback("👍") == "like"
|
|
assert emoji_to_tapback("👎") == "dislike"
|
|
assert emoji_to_tapback("😂") == "laugh"
|
|
assert emoji_to_tapback("❗") == "exclaim"
|
|
assert emoji_to_tapback("❓") == "question"
|
|
|
|
def test_emoji_to_tapback_unknown(self):
|
|
assert emoji_to_tapback("🚀") is None
|
|
|
|
def test_resolve_tapback_value(self):
|
|
assert resolve_tapback_value("love") == 0
|
|
assert resolve_tapback_value("heart") == 0
|
|
assert resolve_tapback_value("like") == 1
|
|
assert resolve_tapback_value("thumbs_up") == 1
|
|
assert resolve_tapback_value("dislike") == 2
|
|
assert resolve_tapback_value("thumbs_down") == 2
|
|
assert resolve_tapback_value("laugh") == 3
|
|
assert resolve_tapback_value("exclaim") == 4
|
|
assert resolve_tapback_value("question") == 5
|
|
|
|
def test_resolve_tapback_value_case_insensitive(self):
|
|
assert resolve_tapback_value("LOVE") == 0
|
|
assert resolve_tapback_value("Love") == 0
|
|
|
|
def test_resolve_tapback_value_unknown(self):
|
|
assert resolve_tapback_value("rocket") is None
|
|
assert resolve_tapback_value("fire") is None
|
|
|
|
def test_tapback_to_emoji(self):
|
|
assert tapback_to_emoji(0) == "❤️"
|
|
assert tapback_to_emoji(1) == "👍"
|
|
assert tapback_to_emoji(2) == "👎"
|
|
assert tapback_to_emoji(5) == "❓"
|
|
|
|
def test_tapback_to_emoji_invalid(self):
|
|
assert tapback_to_emoji(99) is None
|
|
|
|
def test_describe_tapback(self):
|
|
assert "Loved" in describe_tapback(0)
|
|
assert "Liked" in describe_tapback(1)
|
|
assert "Laughed" in describe_tapback(3)
|
|
|
|
def test_describe_tapback_unknown(self):
|
|
assert describe_tapback(99) == "Reacted"
|
|
|
|
def test_is_valid_tapback(self):
|
|
assert is_valid_tapback("❤️") is True
|
|
assert is_valid_tapback("👍") is True
|
|
assert is_valid_tapback("🚀") is False
|
|
|
|
|
|
class TestIMessageAdapter:
|
|
@pytest.fixture
|
|
def adapter(self):
|
|
config = {
|
|
"server_url": "http://localhost:1234",
|
|
"password": "test-password",
|
|
"name": "test-imessage",
|
|
"group_allow_from": ["iMessage;-;group001"],
|
|
}
|
|
return IMessageAdapter(config)
|
|
|
|
def test_channel_id(self, adapter):
|
|
assert adapter.channel_id == "imessage"
|
|
|
|
def test_channel_type(self, adapter):
|
|
assert adapter.channel_type == ChannelType.IMESSAGE
|
|
|
|
def test_capabilities(self, adapter):
|
|
assert adapter.supports_streaming is True
|
|
assert adapter.supports_markdown is True
|
|
assert "off" in adapter.streaming_modes
|
|
assert "partial" in adapter.streaming_modes
|
|
assert "block" in adapter.streaming_modes
|
|
assert adapter.text_chunk_limit == 4096
|
|
assert adapter.max_media_size_mb == 100
|
|
|
|
async def test_normalize_dm_message(self, adapter):
|
|
event = make_dm_event()
|
|
result = await adapter.normalize_inbound(event)
|
|
|
|
assert isinstance(result, ChannelMessage)
|
|
assert result.message_type == MessageType.TEXT
|
|
assert result.content == "Hello world"
|
|
assert result.identity.channel_id == "imessage"
|
|
assert result.identity.channel_type == ChannelType.IMESSAGE
|
|
assert result.identity.channel_user_id == "8613800138000"
|
|
assert result.identity.channel_chat_id == "iMessage;+;chat001"
|
|
assert result.identity.channel_message_id == "msg_001"
|
|
assert result.chat_type == ChatType.DIRECT
|
|
assert result.event_type == EventType.MESSAGE_RECEIVED
|
|
assert result.metadata["is_from_me"] is False
|
|
assert result.metadata["sender_handle"] == "+8613800138000"
|
|
|
|
async def test_normalize_image_message(self, adapter):
|
|
event = make_image_event()
|
|
adapter._include_attachments = True
|
|
result = await adapter.normalize_inbound(event)
|
|
|
|
assert result.message_type == MessageType.IMAGE
|
|
assert len(result.attachments) == 1
|
|
assert result.attachments[0].mime_type == "image/jpeg"
|
|
assert result.attachments[0].filename == "photo.jpg"
|
|
assert result.attachments[0].size_bytes == 1024000
|
|
|
|
async def test_normalize_group_message(self, adapter):
|
|
event = make_group_event()
|
|
result = await adapter.normalize_inbound(event)
|
|
|
|
assert result.chat_type == ChatType.GROUP
|
|
assert result.content == "Hello group"
|
|
assert result.identity.channel_user_id == "8613800138000"
|
|
|
|
async def test_normalize_self_sent_message(self, adapter):
|
|
event = make_dm_event()
|
|
event["data"]["isFromMe"] = True
|
|
|
|
result = await adapter.normalize_inbound(event)
|
|
assert result.content == "(self-sent message)"
|
|
assert result.metadata["is_from_me"] is True
|
|
|
|
async def test_normalize_typing_event(self, adapter):
|
|
event = {
|
|
"type": "typing-indicator",
|
|
"data": {"chatGuid": "iMessage;+;chat001", "senderGuid": "+8613800138000", "display": True},
|
|
}
|
|
result = await adapter.normalize_inbound(event)
|
|
assert result.event_type == EventType.TYPING
|
|
assert result.metadata["display"] is True
|
|
|
|
async def test_normalize_read_receipt_event(self, adapter):
|
|
event = {
|
|
"type": "chat-read-receipt",
|
|
"data": {"chatGuid": "iMessage;+;chat001", "senderGuid": "+8613800138000"},
|
|
}
|
|
result = await adapter.normalize_inbound(event)
|
|
assert result.event_type == EventType.READ_RECEIPT
|
|
|
|
async def test_normalize_chat_name_change(self, adapter):
|
|
event = {
|
|
"type": "chat-name-change",
|
|
"data": {"chatGuid": "iMessage;-;group001", "newName": "New Group Name"},
|
|
}
|
|
result = await adapter.normalize_inbound(event)
|
|
assert result.metadata["event"] == "chat_name_change"
|
|
assert result.metadata["new_name"] == "New Group Name"
|
|
|
|
async def test_normalize_message_without_text_has_attachments(self, adapter):
|
|
event = make_dm_event(text="")
|
|
event["data"]["attachments"] = [
|
|
{"mimeType": "image/png", "url": "/img/test.png", "transferName": "test.png", "totalBytes": 500}
|
|
]
|
|
adapter._include_attachments = True
|
|
result = await adapter.normalize_inbound(event)
|
|
assert result.message_type == MessageType.IMAGE
|
|
assert result.content == "<media:image>"
|
|
assert len(result.attachments) == 1
|
|
|
|
def test_format_outbound_text(self, adapter):
|
|
response = make_response(content="Hello from ForcePilot")
|
|
result = adapter.format_outbound(response)
|
|
|
|
assert result["chatGuid"] == "iMessage;+;chat001"
|
|
assert result["content"] == "Hello from ForcePilot"
|
|
|
|
def test_format_outbound_with_reply(self, adapter):
|
|
response = make_response(content="Reply text")
|
|
response.reply_to_message_id = "msg_reply"
|
|
|
|
result = adapter.format_outbound(response)
|
|
assert result["replyTo"] == "msg_reply"
|
|
|
|
def test_clean_handle(self, adapter):
|
|
assert adapter._clean_handle(" +8613800138000 ") == "8613800138000"
|
|
assert adapter._clean_handle("+12345") == "12345"
|
|
assert adapter._clean_handle("test@example.com") == "test@example.com"
|
|
|
|
def test_map_attachment_type(self, adapter):
|
|
assert adapter._map_attachment_type("image/jpeg") == MessageType.IMAGE
|
|
assert adapter._map_attachment_type("video/mp4") == MessageType.VIDEO
|
|
assert adapter._map_attachment_type("audio/mp3") == MessageType.AUDIO
|
|
assert adapter._map_attachment_type("application/pdf") == MessageType.FILE
|
|
|
|
def test_map_event_type(self, adapter):
|
|
assert adapter._map_event_type("new-message") == EventType.MESSAGE_RECEIVED
|
|
assert adapter._map_event_type("message-updated") == EventType.MESSAGE_UPDATED
|
|
assert adapter._map_event_type("message-deleted") == EventType.MESSAGE_DELETED
|
|
assert adapter._map_event_type("typing-indicator") == EventType.TYPING
|
|
assert adapter._map_event_type("read-receipt") == EventType.READ_RECEIPT
|
|
assert adapter._map_event_type("unknown-type") == EventType.MESSAGE_RECEIVED
|
|
|
|
async def test_normalize_unknown_event_type(self, adapter):
|
|
event = {
|
|
"type": "server-event",
|
|
"data": {"chatGuid": "unknown", "guid": "evt_001", "sender": "system", "isFromMe": False},
|
|
}
|
|
result = await adapter.normalize_inbound(event)
|
|
assert result.event_type == EventType.MESSAGE_RECEIVED
|
|
|
|
async def test_normalize_message_deleted_event(self, adapter):
|
|
event = {
|
|
"type": "message-deleted",
|
|
"data": {"chatGuid": "iMessage;+;chat001", "guid": "msg_deleted_001"},
|
|
}
|
|
result = await adapter.normalize_inbound(event)
|
|
assert result.event_type == EventType.MESSAGE_DELETED
|
|
assert result.metadata["event"] == "message_deleted"
|
|
assert result.metadata["deleted_guid"] == "msg_deleted_001"
|
|
|
|
async def test_normalize_participant_added_event(self, adapter):
|
|
event = {
|
|
"type": "participant-added",
|
|
"data": {"chatGuid": "iMessage;-;group001", "participant": "+8613900139000"},
|
|
}
|
|
result = await adapter.normalize_inbound(event)
|
|
assert result.event_type == EventType.MEMBER_JOINED
|
|
assert result.metadata["event"] == "member_joined"
|
|
assert result.metadata["participant"] == "+8613900139000"
|
|
|
|
async def test_normalize_participant_removed_event(self, adapter):
|
|
event = {
|
|
"type": "participant-removed",
|
|
"data": {"chatGuid": "iMessage;-;group001", "participant": "+8613900139000"},
|
|
}
|
|
result = await adapter.normalize_inbound(event)
|
|
assert result.event_type == EventType.MEMBER_LEFT
|
|
assert result.metadata["event"] == "member_left"
|
|
assert result.metadata["participant"] == "+8613900139000"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_reaction_valid(self, adapter):
|
|
mock_result = DeliveryResult(success=True, message_id="msg_001")
|
|
adapter._primary_bridge = AsyncMock()
|
|
adapter._primary_bridge.send_reaction = AsyncMock(return_value=mock_result)
|
|
|
|
result = await adapter.send_reaction("iMessage;+;chat001", "msg_001", "love")
|
|
assert result.success is True
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_reaction_invalid(self, adapter):
|
|
result = await adapter.send_reaction("iMessage;+;chat001", "msg_001", "🚀")
|
|
assert result.success is False
|
|
assert "Unsupported" in result.error
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_reaction_by_emoji(self, adapter):
|
|
mock_result = DeliveryResult(success=True, message_id="msg_001")
|
|
adapter._primary_bridge = AsyncMock()
|
|
adapter._primary_bridge.send_reaction = AsyncMock(return_value=mock_result)
|
|
|
|
result = await adapter.send_reaction("iMessage;+;chat001", "msg_001", "👍")
|
|
assert result.success is True
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_health_check_healthy(self, adapter):
|
|
adapter._primary_bridge = AsyncMock()
|
|
adapter._primary_bridge.probe_server = AsyncMock(return_value={"version": "1.0"})
|
|
adapter._primary_bridge.get_handle = AsyncMock(return_value={"connected": True, "handle": "test@icloud.com"})
|
|
|
|
result = await adapter.health_check()
|
|
assert result.status == "healthy"
|
|
assert result.metadata["handle"] == "test@icloud.com"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_health_check_degraded(self, adapter):
|
|
adapter._primary_bridge = AsyncMock()
|
|
adapter._primary_bridge.probe_server = AsyncMock(return_value={"version": "1.0"})
|
|
adapter._primary_bridge.get_handle = AsyncMock(return_value={"connected": False})
|
|
|
|
result = await adapter.health_check()
|
|
assert result.status == "degraded"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_health_check_unhealthy(self, adapter):
|
|
adapter._primary_bridge = AsyncMock()
|
|
adapter._primary_bridge.probe_server = AsyncMock(side_effect=Exception("Connection refused"))
|
|
|
|
result = await adapter.health_check()
|
|
assert result.status == "unhealthy"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_text_message(self, adapter):
|
|
mock_result = DeliveryResult(success=True, message_id="sent_001")
|
|
adapter._primary_bridge = AsyncMock()
|
|
adapter._primary_bridge.send_text_message = AsyncMock(return_value=mock_result)
|
|
|
|
response = make_response()
|
|
result = await adapter.send(response)
|
|
assert result.success is True
|
|
assert result.message_id == "sent_001"
|
|
assert adapter._pending_messages["sent_001"] == "Hello from ForcePilot"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_typing_indicator(self, adapter):
|
|
mock_result = DeliveryResult(success=True)
|
|
adapter._primary_bridge = AsyncMock()
|
|
adapter._primary_bridge.send_typing_indicator = AsyncMock(return_value=mock_result)
|
|
|
|
result = await adapter.send_typing_indicator("iMessage;+;chat001", display=True)
|
|
assert result.success is True
|
|
adapter._primary_bridge.send_typing_indicator.assert_called_once_with("iMessage;+;chat001", True)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_read_receipt(self, adapter):
|
|
mock_result = DeliveryResult(success=True)
|
|
adapter._primary_bridge = AsyncMock()
|
|
adapter._primary_bridge.send_read_receipt = AsyncMock(return_value=mock_result)
|
|
|
|
result = await adapter.send_read_receipt("iMessage;+;chat001")
|
|
assert result.success is True
|
|
adapter._primary_bridge.send_read_receipt.assert_called_once_with("iMessage;+;chat001")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_edit_message(self, adapter):
|
|
mock_result = DeliveryResult(success=True, message_id="msg_001")
|
|
adapter._primary_bridge = AsyncMock()
|
|
adapter._primary_bridge.edit_message = AsyncMock(return_value=mock_result)
|
|
|
|
result = await adapter.edit_message("chat_001", "msg_001", "Edited text")
|
|
assert result.success is True
|
|
assert adapter._pending_messages["msg_001"] == "Edited text"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_stream_chunk_finished(self, adapter):
|
|
mock_result = DeliveryResult(success=True, message_id="msg_001")
|
|
adapter._primary_bridge = AsyncMock()
|
|
adapter._primary_bridge.edit_message = AsyncMock(return_value=mock_result)
|
|
adapter._pending_messages["msg_001"] = "partial text"
|
|
|
|
result = await adapter.send_stream_chunk("chat_001", "msg_001", "final text", finished=True)
|
|
assert result.success is True
|
|
assert "msg_001" not in adapter._pending_messages
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_stream_chunk_accumulating(self, adapter):
|
|
mock_result = DeliveryResult(success=True, message_id="msg_001")
|
|
adapter._primary_bridge = AsyncMock()
|
|
adapter._primary_bridge.edit_message = AsyncMock(return_value=mock_result)
|
|
|
|
result = await adapter.send_stream_chunk("chat_001", "msg_001", "hello", finished=False)
|
|
assert result.success is True
|
|
assert adapter._pending_messages["msg_001"] == "hello"
|
|
|
|
await adapter.send_stream_chunk("chat_001", "msg_001", " world", finished=False)
|
|
assert adapter._pending_messages["msg_001"] == "hello world"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_receive_returns_empty(self, adapter):
|
|
messages = []
|
|
async for msg in adapter.receive():
|
|
messages.append(msg)
|
|
assert len(messages) == 0
|
|
|
|
async def test_normalize_tapback_uses_module_function(self, adapter):
|
|
event = {
|
|
"type": "new-message",
|
|
"data": {
|
|
"chatGuid": "iMessage;+;chat001",
|
|
"guid": "msg_tapback",
|
|
"sender": "+8613800138000",
|
|
"isFromMe": False,
|
|
"isTapback": True,
|
|
"tapback": 0,
|
|
"attachments": [],
|
|
},
|
|
}
|
|
result = await adapter.normalize_inbound(event)
|
|
assert "Loved" in result.content
|
|
assert result.metadata["event"] == "tapback"
|
|
assert result.metadata["tapback_value"] == 0
|
|
|
|
|
|
class TestBlueBubblesClient:
|
|
@pytest.fixture
|
|
def client(self):
|
|
from yuxi.channels.adapters.imessage.bridge_client import BlueBubblesClient
|
|
|
|
return BlueBubblesClient({
|
|
"server_url": "http://localhost:1234",
|
|
"password": "test-password",
|
|
})
|
|
|
|
def test_base_url(self, client):
|
|
assert client._server_url == "http://localhost:1234"
|
|
|
|
def test_base_url_strips_trailing_slash(self):
|
|
from yuxi.channels.adapters.imessage.bridge_client import BlueBubblesClient
|
|
|
|
c = BlueBubblesClient({"server_url": "http://localhost:1234/", "password": "pw"})
|
|
assert c._server_url == "http://localhost:1234"
|
|
|
|
def test_ws_url_http(self, client):
|
|
assert client.ws_url == "ws://localhost:1234/ws"
|
|
|
|
def test_ws_url_https(self):
|
|
from yuxi.channels.adapters.imessage.bridge_client import BlueBubblesClient
|
|
|
|
c = BlueBubblesClient({"server_url": "https://mac.local:1234", "password": "pw"})
|
|
assert c.ws_url == "wss://mac.local:1234/ws"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_context_manager(self, client):
|
|
async with client as c:
|
|
assert c._session is not None
|
|
assert client._session is None
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.skip(reason="Requires proper async context manager mock setup")
|
|
async def test_query_contacts(self, client):
|
|
mock_resp = AsyncMock()
|
|
mock_resp.json = AsyncMock(return_value={"data": [
|
|
{"address": "+8613800138000", "displayName": "Test User"}
|
|
]})
|
|
mock_resp.status = 200
|
|
|
|
client._session = AsyncMock()
|
|
client._session.post = AsyncMock(return_value=mock_resp)
|
|
|
|
result = await client.query_contacts(["+8613800138000"])
|
|
assert len(result) == 1
|
|
assert result[0]["displayName"] == "Test User"
|
|
|
|
def test_download_attachment_url_validation_same_origin(self):
|
|
from yuxi.channels.adapters.imessage.bridge_client import BlueBubblesClient
|
|
|
|
c = BlueBubblesClient({"server_url": "http://localhost:1234", "password": "pw"})
|
|
assert c._server_url == "http://localhost:1234"
|
|
|
|
def test_download_attachment_relative_path(self):
|
|
from yuxi.channels.adapters.imessage.bridge_client import BlueBubblesClient
|
|
|
|
c = BlueBubblesClient({"server_url": "http://localhost:1234", "password": "pw"})
|
|
c._session = MagicMock()
|
|
url = c._server_url + "/" + "/media/test.jpg".lstrip("/")
|
|
assert url == "http://localhost:1234/media/test.jpg"
|
|
|
|
|
|
class TestIMessageMonitor:
|
|
def test_monitor_creation(self):
|
|
from yuxi.channels.adapters.imessage.monitor import IMessageMonitor
|
|
from yuxi.channels.adapters.imessage.bridge_client import BlueBubblesClient
|
|
|
|
bridge = BlueBubblesClient({"server_url": "http://localhost:1234", "password": "pw"})
|
|
monitor = IMessageMonitor({"name": "test"}, bridge)
|
|
assert monitor._message_handler is None
|
|
|
|
def test_monitor_on_message(self):
|
|
from yuxi.channels.adapters.imessage.monitor import IMessageMonitor
|
|
from yuxi.channels.adapters.imessage.bridge_client import BlueBubblesClient
|
|
|
|
bridge = BlueBubblesClient({"server_url": "http://localhost:1234", "password": "pw"})
|
|
|
|
async def handler(data):
|
|
pass
|
|
|
|
monitor = IMessageMonitor({"name": "test"}, bridge)
|
|
monitor.on_message(handler)
|
|
assert monitor._message_handler is not None
|
|
|
|
|
|
class TestIMessageProbe:
|
|
@pytest.mark.asyncio
|
|
async def test_probe_bridge(self):
|
|
from yuxi.channels.adapters.imessage.probe import probe_bridge
|
|
|
|
mock_bridge = AsyncMock()
|
|
mock_bridge.probe_server = AsyncMock(return_value={"version": "1.5.0", "os_version": "15.0"})
|
|
mock_bridge.get_handle = AsyncMock(return_value={"connected": True, "handle": "test@icloud.com"})
|
|
|
|
result = await probe_bridge(mock_bridge)
|
|
assert result["bridge_version"] == "1.5.0"
|
|
assert result["imessage_connected"] is True
|
|
assert result["imessage_handle"] == "test@icloud.com"
|