blob: 8ed452a7ff6eed1f635ebd3e9eda42efd7f0ce7c [file] [log] [blame] [raw]
"""
Webhook模块单元测试
"""
import pytest
from unittest.mock import Mock, AsyncMock, patch, MagicMock
import asyncio
from datetime import datetime
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
from claude_agent.webhook.models import (
BotMessage, BotRegistration, MessageBroadcast, WebhookConfig, WebhookResponse
)
from claude_agent.webhook.client import WebhookClient
from claude_agent.webhook.server import WebhookServer
from claude_agent.webhook.telegram_integration import (
TelegramWebhookIntegration, create_webhook_integration_from_config
)
class TestWebhookModels:
"""Webhook模型测试"""
def test_bot_message_creation(self):
"""测试BotMessage创建"""
message = BotMessage(
message_id="test_123",
group_id=12345,
content="Test message",
sender_info={
"user_id": 999,
"username": "testbot",
"is_bot": True
},
timestamp=datetime.now().isoformat(),
message_type="text"
)
assert message.message_id == "test_123"
assert message.group_id == 12345
assert message.content == "Test message"
assert message.sender_info["user_id"] == 999
assert message.message_type == "text"
def test_bot_registration_creation(self):
"""测试BotRegistration创建"""
registration = BotRegistration(
bot_username="testbot",
auth_token="test_token",
subscribed_groups=[12345, 67890]
)
assert registration.bot_username == "testbot"
assert registration.auth_token == "test_token"
assert 12345 in registration.subscribed_groups
assert 67890 in registration.subscribed_groups
def test_webhook_config_creation(self):
"""测试WebhookConfig创建"""
config = WebhookConfig(
webhook_url="http://localhost:8080",
auth_token="test_token",
enabled_groups=[12345],
connection_timeout=10.0,
request_timeout=5.0
)
assert config.webhook_url == "http://localhost:8080"
assert config.auth_token == "test_token"
assert config.enabled_groups == [12345]
assert config.connection_timeout == 10.0
def test_message_broadcast_creation(self):
"""测试MessageBroadcast创建"""
message = BotMessage(
message_id="test_123",
group_id=12345,
content="Test message",
sender_info={"user_id": 999},
timestamp=datetime.now().isoformat()
)
broadcast = MessageBroadcast(
bot_message=message,
target_groups=[12345],
exclude_bots=["excludebot"]
)
assert broadcast.bot_message == message
assert broadcast.target_groups == [12345]
assert broadcast.exclude_bots == ["excludebot"]
class TestWebhookClient:
"""Webhook客户端测试"""
@pytest.fixture
def webhook_config(self):
"""创建测试配置"""
return WebhookConfig(
webhook_url="http://localhost:8080",
auth_token="test_token",
enabled_groups=[12345],
connection_timeout=10.0,
request_timeout=5.0
)
@pytest.fixture
def webhook_client(self, webhook_config):
"""创建测试客户端"""
return WebhookClient(
config=webhook_config,
bot_username="testbot",
subscribed_groups=[12345]
)
def test_client_initialization(self, webhook_client, webhook_config):
"""测试客户端初始化"""
assert webhook_client.config == webhook_config
assert webhook_client.bot_username == "testbot"
assert webhook_client.subscribed_groups == [12345]
assert webhook_client.session is None
assert webhook_client.is_registered is False
@pytest.mark.asyncio
async def test_start_client(self, webhook_client):
"""测试客户端启动"""
with patch('aiohttp.ClientSession') as mock_session_class:
mock_session = Mock()
mock_session_class.return_value = mock_session
with patch.object(webhook_client, '_register', new_callable=AsyncMock) as mock_register:
await webhook_client.start()
assert webhook_client.session == mock_session
mock_register.assert_called_once()
@pytest.mark.asyncio
async def test_stop_client(self, webhook_client):
"""测试客户端停止"""
# 设置已注册状态
webhook_client.is_registered = True
mock_session = Mock()
mock_session.close = AsyncMock()
webhook_client.session = mock_session
with patch.object(webhook_client, '_unregister', new_callable=AsyncMock) as mock_unregister:
await webhook_client.stop()
mock_unregister.assert_called_once()
mock_session.close.assert_called_once()
assert webhook_client.session is None
@pytest.mark.asyncio
async def test_broadcast_message(self, webhook_client):
"""测试消息广播"""
# 设置已注册状态
webhook_client.is_registered = True
mock_session = Mock()
mock_response = Mock()
mock_response.status = 200
mock_session.post.return_value.__aenter__.return_value = mock_response
webhook_client.session = mock_session
message = BotMessage(
message_id="test_123",
group_id=12345,
content="Test message",
sender_info={"user_id": 999},
timestamp=datetime.now().isoformat()
)
result = await webhook_client.broadcast_message(message)
assert result is True
mock_session.post.assert_called_once()
@pytest.mark.asyncio
async def test_broadcast_message_not_registered(self, webhook_client):
"""测试未注册时广播消息"""
webhook_client.is_registered = False
message = BotMessage(
message_id="test_123",
group_id=12345,
content="Test message",
sender_info={"user_id": 999},
timestamp=datetime.now().isoformat()
)
result = await webhook_client.broadcast_message(message)
assert result is False
@pytest.mark.asyncio
async def test_check_server_status(self, webhook_client):
"""测试服务器状态检查"""
mock_session = Mock()
mock_response = Mock()
mock_response.status = 200
mock_session.get.return_value.__aenter__.return_value = mock_response
webhook_client.session = mock_session
result = await webhook_client.check_server_status()
assert result is True
@pytest.mark.asyncio
async def test_check_server_status_failed(self, webhook_client):
"""测试服务器状态检查失败"""
mock_session = Mock()
mock_response = Mock()
mock_response.status = 500
mock_session.get.return_value.__aenter__.return_value = mock_response
webhook_client.session = mock_session
result = await webhook_client.check_server_status()
assert result is False
class TestWebhookServer:
"""Webhook服务器测试"""
@pytest.fixture
def webhook_config(self):
"""创建测试配置"""
return WebhookConfig(
webhook_url="http://localhost:8080",
auth_token="test_token",
enabled_groups=[12345],
connection_timeout=10.0,
request_timeout=5.0
)
@pytest.fixture
def webhook_server(self, webhook_config):
"""创建测试服务器"""
return WebhookServer(webhook_config)
def test_server_initialization(self, webhook_server, webhook_config):
"""测试服务器初始化"""
assert webhook_server.config == webhook_config
assert webhook_server.registered_bots == {}
assert webhook_server.app is not None
def test_register_bot(self, webhook_server):
"""测试Bot注册"""
registration = BotRegistration(
bot_username="testbot",
auth_token="test_token",
subscribed_groups=[12345]
)
webhook_server.register_bot(registration)
assert "testbot" in webhook_server.registered_bots
assert webhook_server.registered_bots["testbot"] == registration
def test_unregister_bot(self, webhook_server):
"""测试Bot注销"""
registration = BotRegistration(
bot_username="testbot",
auth_token="test_token",
subscribed_groups=[12345]
)
webhook_server.register_bot(registration)
webhook_server.unregister_bot("testbot")
assert "testbot" not in webhook_server.registered_bots
def test_get_subscribed_bots(self, webhook_server):
"""测试获取订阅指定群组的Bot"""
registration1 = BotRegistration(
bot_username="bot1",
auth_token="test_token",
subscribed_groups=[12345]
)
registration2 = BotRegistration(
bot_username="bot2",
auth_token="test_token",
subscribed_groups=[67890]
)
registration3 = BotRegistration(
bot_username="bot3",
auth_token="test_token",
subscribed_groups=[12345, 67890]
)
webhook_server.register_bot(registration1)
webhook_server.register_bot(registration2)
webhook_server.register_bot(registration3)
subscribed = webhook_server.get_subscribed_bots(12345)
assert len(subscribed) == 2
assert "bot1" in subscribed
assert "bot3" in subscribed
@pytest.mark.asyncio
async def test_broadcast_to_bots(self, webhook_server):
"""测试向Bot广播消息"""
registration = BotRegistration(
bot_username="testbot",
auth_token="test_token",
subscribed_groups=[12345],
webhook_endpoint="http://localhost:8081/webhook"
)
webhook_server.register_bot(registration)
message = BotMessage(
message_id="test_123",
group_id=12345,
content="Test message",
sender_info={"user_id": 999},
timestamp=datetime.now().isoformat()
)
with patch('aiohttp.ClientSession') as mock_session_class:
mock_session = Mock()
mock_response = Mock()
mock_response.status = 200
mock_session.post.return_value.__aenter__.return_value = mock_response
mock_session_class.return_value.__aenter__.return_value = mock_session
mock_session_class.return_value.__aexit__.return_value = AsyncMock()
result = await webhook_server.broadcast_to_bots(message)
assert result is True
class TestTelegramWebhookIntegration:
"""Telegram Webhook集成测试"""
@pytest.fixture
def mock_telegram_client(self):
"""创建模拟Telegram客户端"""
return Mock()
@pytest.fixture
def webhook_config(self):
"""创建测试配置"""
return WebhookConfig(
webhook_url="http://localhost:8080",
auth_token="test_token",
enabled_groups=[12345]
)
@pytest.fixture
def message_saver(self):
"""创建消息保存回调"""
return AsyncMock()
@pytest.fixture
def telegram_integration(self, mock_telegram_client, webhook_config, message_saver):
"""创建Telegram集成"""
return TelegramWebhookIntegration(
telegram_client=mock_telegram_client,
webhook_config=webhook_config,
bot_username="testbot",
subscribed_groups=[12345],
message_saver=message_saver
)
def test_integration_initialization(self, telegram_integration, message_saver):
"""测试集成初始化"""
assert telegram_integration.message_saver == message_saver
def test_set_message_saver(self, telegram_integration):
"""测试设置消息保存回调"""
new_saver = AsyncMock()
telegram_integration.set_message_saver(new_saver)
assert telegram_integration.message_saver == new_saver
@pytest.mark.asyncio
async def test_save_webhook_message_to_context(self, telegram_integration, message_saver):
"""测试保存Webhook消息到上下文"""
await telegram_integration._save_webhook_message_to_context(
chat_id="12345",
user_id=999,
message="Test message",
user_info={"username": "testuser"}
)
message_saver.assert_called_once_with("12345", 999, "Test message", False, {"username": "testuser"})
def test_create_integration_from_config_enabled(self):
"""测试从配置创建集成(启用状态)"""
mock_client = Mock()
config_dict = {
'enabled': True,
'server_url': 'http://localhost:8080',
'auth_token': 'test_token',
'client': {
'subscribed_groups': [12345]
}
}
integration = create_webhook_integration_from_config(
telegram_client=mock_client,
webhook_config_dict=config_dict,
bot_username="testbot"
)
assert integration is not None
assert isinstance(integration, TelegramWebhookIntegration)
def test_create_integration_from_config_disabled(self):
"""测试从配置创建集成(禁用状态)"""
mock_client = Mock()
config_dict = {
'enabled': False,
'server_url': 'http://localhost:8080',
'auth_token': 'test_token'
}
integration = create_webhook_integration_from_config(
telegram_client=mock_client,
webhook_config_dict=config_dict,
bot_username="testbot"
)
assert integration is None
def test_create_integration_from_config_missing_required(self):
"""测试从配置创建集成(缺少必需配置)"""
mock_client = Mock()
config_dict = {
'enabled': True,
'server_url': 'http://localhost:8080'
# 缺少 auth_token
}
integration = create_webhook_integration_from_config(
telegram_client=mock_client,
webhook_config_dict=config_dict,
bot_username="testbot"
)
assert integration is None
def test_create_integration_from_config_no_subscribed_groups(self):
"""测试从配置创建集成(无订阅群组)"""
mock_client = Mock()
config_dict = {
'enabled': True,
'server_url': 'http://localhost:8080',
'auth_token': 'test_token',
'client': {
'subscribed_groups': []
}
}
integration = create_webhook_integration_from_config(
telegram_client=mock_client,
webhook_config_dict=config_dict,
bot_username="testbot"
)
assert integration is None