ForcePilot/backend/test/unit/channels/test_models.py

343 lines
11 KiB
Python
Raw Normal View History

from __future__ import annotations
from datetime import datetime
import pytest
from pydantic import ValidationError
from yuxi.channels.models import (
AgentRequest,
AgentResult,
Attachment,
ChannelAccountSnapshot,
ChannelIdentity,
ChannelMessage,
ChannelResponse,
ChannelStatus,
ChannelType,
ChatType,
DeliveryResult,
EventType,
HealthStatus,
MentionsInfo,
MessageType,
build_snapshot_from_adapter,
)
class TestMessageType:
def test_text_value(self):
assert MessageType.TEXT == "text"
def test_all_values_are_strings(self):
for member in MessageType:
assert isinstance(member.value, str)
class TestChannelType:
def test_webchat(self):
assert ChannelType.WEBCHAT == "webchat"
def test_has_wechat(self):
assert ChannelType.WECHAT == "wechat"
class TestChatType:
def test_direct(self):
assert ChatType.DIRECT == "direct"
def test_group(self):
assert ChatType.GROUP == "group"
class TestEventType:
def test_message_received(self):
assert EventType.MESSAGE_RECEIVED == "message.received"
class TestChannelIdentity:
def test_minimal_fields(self):
identity = ChannelIdentity(
channel_id="test_chan",
channel_type=ChannelType.WEBCHAT,
channel_user_id="user_123",
channel_chat_id="chat_456",
)
assert identity.channel_id == "test_chan"
assert identity.channel_type == ChannelType.WEBCHAT
assert identity.channel_message_id is None
def test_missing_required_field_raises(self):
with pytest.raises(ValidationError):
ChannelIdentity(channel_id="test_chan", channel_type=ChannelType.WEBCHAT, channel_user_id="u1")
def test_all_fields(self):
identity = ChannelIdentity(
channel_id="telegram",
channel_type=ChannelType.TELEGRAM,
channel_user_id="@user",
channel_chat_id="12345",
channel_message_id="msg_001",
)
assert identity.channel_message_id == "msg_001"
class TestChannelMessage:
def _make_identity(self) -> ChannelIdentity:
return ChannelIdentity(
channel_id="test",
channel_type=ChannelType.WEBCHAT,
channel_user_id="u1",
channel_chat_id="c1",
)
def test_minimal_fields(self):
msg = ChannelMessage(identity=self._make_identity(), content="hello")
assert msg.content == "hello"
assert msg.message_type == MessageType.TEXT
assert msg.event_type == EventType.MESSAGE_RECEIVED
assert msg.chat_type == ChatType.DIRECT
assert msg.attachments == []
assert msg.mentions is None
assert msg.extracted_urls == []
assert msg.metadata == {}
assert isinstance(msg.timestamp, datetime)
def test_with_mentions(self):
mentions = MentionsInfo(mentioned_user_ids=["u2"], is_bot_mentioned=True)
msg = ChannelMessage(identity=self._make_identity(), content="@bot hi", mentions=mentions)
assert msg.mentions.is_bot_mentioned is True
def test_with_attachments(self):
attachment = Attachment(type="image", url="https://example.com/img.png", filename="img.png")
msg = ChannelMessage(identity=self._make_identity(), content="image", attachments=[attachment])
assert len(msg.attachments) == 1
assert msg.attachments[0].type == "image"
def test_content_required(self):
with pytest.raises(ValidationError):
ChannelMessage(identity=self._make_identity())
def test_identity_required(self):
with pytest.raises(ValidationError):
ChannelMessage(content="hello")
def test_metadata_default(self):
msg = ChannelMessage(identity=self._make_identity(), content="test")
assert msg.metadata == {}
def test_metadata_custom(self):
msg = ChannelMessage(identity=self._make_identity(), content="test", metadata={"key": "val"})
assert msg.metadata["key"] == "val"
def test_timestamp_auto_generated(self):
msg = ChannelMessage(identity=self._make_identity(), content="test")
assert isinstance(msg.timestamp, datetime)
def test_default_message_type(self):
msg = ChannelMessage(identity=self._make_identity(), content="test")
assert msg.message_type == MessageType.TEXT
def test_custom_message_type(self):
msg = ChannelMessage(identity=self._make_identity(), content="test", message_type=MessageType.IMAGE)
assert msg.message_type == MessageType.IMAGE
def test_reply_to_message_id(self):
msg = ChannelMessage(identity=self._make_identity(), content="test", reply_to_message_id="reply_123")
assert msg.reply_to_message_id == "reply_123"
class TestChannelResponse:
def _make_identity(self) -> ChannelIdentity:
return ChannelIdentity(
channel_id="test",
channel_type=ChannelType.WEBCHAT,
channel_user_id="u1",
channel_chat_id="c1",
)
def test_minimal_fields(self):
resp = ChannelResponse(identity=self._make_identity(), content="reply")
assert resp.content == "reply"
assert resp.message_type == MessageType.TEXT
assert isinstance(resp.timestamp, datetime)
def test_with_attachments(self):
attachment = Attachment(type="file", url="https://example.com/doc.pdf", filename="doc.pdf")
resp = ChannelResponse(identity=self._make_identity(), content="here", attachments=[attachment])
assert len(resp.attachments) == 1
assert resp.attachments[0].filename == "doc.pdf"
class TestDeliveryResult:
def test_success(self):
result = DeliveryResult(success=True, message_id="msg_123")
assert result.success is True
assert result.message_id == "msg_123"
assert result.error is None
def test_failure(self):
result = DeliveryResult(success=False, error="timeout")
assert result.success is False
assert result.error == "timeout"
def test_defaults(self):
result = DeliveryResult(success=True)
assert result.message_id is None
assert result.error is None
assert result.metadata == {}
class TestAgentRequest:
def test_minimal_fields(self):
identity = ChannelIdentity(
channel_id="test", channel_type=ChannelType.WEBCHAT, channel_user_id="u1", channel_chat_id="c1"
)
message = ChannelMessage(identity=identity, content="hello")
req = AgentRequest(user_id="user_1", thread_id="thread_1", agent_config_id=1, message=message)
assert req.user_id == "user_1"
assert req.thread_id == "thread_1"
assert req.agent_config_id == 1
assert req.agent_id is None
assert req.context == {}
class TestAgentResult:
def test_text_response(self):
result = AgentResult(response_text="hello world")
assert result.response_text == "hello world"
assert result.response_type == "text"
assert result.attachments == []
assert result.agent_state is None
def test_error_response(self):
result = AgentResult(response_text="error occurred", response_type="error")
assert result.response_type == "error"
def test_with_attachments(self):
attachment = Attachment(type="file", url="https://example.com/f.pdf")
result = AgentResult(response_text="here is file", attachments=[attachment])
assert len(result.attachments) == 1
class TestHealthStatus:
def test_healthy(self):
hs = HealthStatus(status="healthy", latency_ms=50.0)
assert hs.status == "healthy"
assert hs.latency_ms == 50.0
def test_degraded(self):
hs = HealthStatus(status="degraded", last_error="slow response")
assert hs.status == "degraded"
assert hs.last_error == "slow response"
def test_unhealthy(self):
hs = HealthStatus(status="unhealthy")
assert hs.status == "unhealthy"
def test_invalid_status_raises(self):
with pytest.raises(ValidationError):
HealthStatus(status="unknown")
def test_default_metadata(self):
hs = HealthStatus(status="healthy")
assert hs.metadata == {}
class TestChannelStatus:
def test_disconnected(self):
assert ChannelStatus.DISCONNECTED == "disconnected"
def test_connected(self):
assert ChannelStatus.CONNECTED == "connected"
class TestMentionsInfo:
def test_defaults(self):
mentions = MentionsInfo()
assert mentions.mentioned_user_ids == []
assert mentions.is_bot_mentioned is False
assert mentions.raw_text is None
def test_bot_mentioned(self):
mentions = MentionsInfo(is_bot_mentioned=True)
assert mentions.is_bot_mentioned is True
class TestAttachment:
def test_defaults(self):
attachment = Attachment()
assert attachment.type == "file"
assert attachment.url is None
assert attachment.filename is None
def test_full_fields(self):
attachment = Attachment(
type="image",
url="https://example.com/photo.jpg",
filename="photo.jpg",
size_bytes=1024,
mime_type="image/jpeg",
file_id="file_001",
)
assert attachment.size_bytes == 1024
assert attachment.mime_type == "image/jpeg"
class TestChannelAccountSnapshot:
def test_default_values(self):
snap = ChannelAccountSnapshot()
assert snap.account_id == ""
assert snap.configured is False
assert snap.enabled is True
assert snap.linked is False
assert snap.running is False
assert snap.connected is False
assert snap.status_state == "not-configured"
assert snap.health_state == "stopped"
assert snap.dm_policy == "pairing"
assert snap.group_policy == "allowlist"
assert snap.reconnect_attempts == 0
def test_custom_values(self):
snap = ChannelAccountSnapshot(account_id="acc_1", name="test", configured=True, linked=True)
assert snap.account_id == "acc_1"
assert snap.name == "test"
assert snap.configured is True
assert snap.linked is True
def test_extra_fields_allowed(self):
snap = ChannelAccountSnapshot(account_id="acc_1", custom_field="extra_value")
assert snap.custom_field == "extra_value"
class TestBuildSnapshotFromAdapter:
def test_build_from_minimal_adapter(self):
class MinimalAdapter:
pass
adapter = MinimalAdapter()
snap = build_snapshot_from_adapter(adapter)
assert isinstance(snap, ChannelAccountSnapshot)
assert snap.status_state == "not-configured"
def test_build_from_full_adapter(self):
class FullAdapter:
account_id = "acc_full"
account_name = "Full Adapter"
_token_mgr = object()
_enabled = True
_linked = True
_running = True
_connected = True
_status_state = "active"
_status = None
adapter = FullAdapter()
snap = build_snapshot_from_adapter(adapter)
assert snap.account_id == "acc_full"
assert snap.name == "Full Adapter"
assert snap.configured is True
assert snap.linked is True
assert snap.running is True
assert snap.connected is True