"""Tests for bot command handlers.""" import pytest from unittest.mock import Mock, AsyncMock, patch, MagicMock from mautrix.types import EventType, UserID, MessageEvent, StateEvent from mautrix.errors import MNotFound from community.bot import CommunityBot class TestBotCommands: """Test cases for bot command handlers.""" @pytest.fixture def bot(self): """Create a mock bot instance for testing.""" bot = Mock(spec=CommunityBot) bot.client = Mock() bot.database = Mock() bot.log = Mock() bot.config = { "parent_room": "!parent:example.com", "community_slug": "test", "track_users": True, "warn_threshold_days": 7, "kick_threshold_days": 14, "sleep": 0.1, "censor_wordlist": [r"badword"], "censor_files": False, "censor": True, "banlists": ["!banlist:example.com"], "redact_on_ban": False, "admins": [], "moderators": [] } return bot @pytest.fixture def mock_evt(self): """Create a mock MessageEvent for testing.""" evt = Mock(spec=MessageEvent) evt.sender = "@user:example.com" evt.room_id = "!room:example.com" evt.reply = AsyncMock() evt.respond = AsyncMock() return evt @pytest.mark.asyncio async def test_check_parent_room_configured(self, bot, mock_evt): """Test check_parent_room when parent room is configured.""" # Use the mock bot instance bot.config = {"parent_room": "!parent:example.com"} result = await bot.check_parent_room(mock_evt) assert result == True mock_evt.reply.assert_not_called() @pytest.mark.asyncio async def test_check_parent_room_not_configured(self, bot, mock_evt): """Test check_parent_room when parent room is not configured.""" bot.config = {"parent_room": None} result = await bot.check_parent_room(mock_evt) assert result == False mock_evt.reply.assert_called_once() @pytest.mark.asyncio async def test_check_banlists_command(self, bot, mock_evt): """Test the check_banlists command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.log = bot.log # Mock the check_if_banned method with patch.object(real_bot, 'check_if_banned', return_value=True): await real_bot.check_banlists(mock_evt, "@test:example.com") mock_evt.reply.assert_called_once_with("user on banlist: True") @pytest.mark.asyncio async def test_sync_space_members_command(self, bot, mock_evt): """Test the sync_space_members command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.database = bot.database real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'do_sync', return_value={"added": [], "dropped": []}): await real_bot.sync_space_members(mock_evt) mock_evt.respond.assert_called() @pytest.mark.asyncio async def test_sync_space_members_no_permission(self, bot, mock_evt): """Test sync_space_members command without permission.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.log = bot.log with patch.object(real_bot, 'user_permitted', return_value=False): await real_bot.sync_space_members(mock_evt) mock_evt.reply.assert_called_once_with("You don't have permission to use this command") @pytest.mark.asyncio async def test_sync_space_members_tracking_disabled(self, bot, mock_evt): """Test sync_space_members command when tracking is disabled.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = {**bot.config, "track_users": False} real_bot.client = bot.client real_bot.log = bot.log with patch.object(real_bot, 'user_permitted', return_value=True): await real_bot.sync_space_members(mock_evt) mock_evt.respond.assert_called_once_with("user tracking is disabled") @pytest.mark.asyncio async def test_ignore_command(self, bot, mock_evt): """Test the ignore command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.database = bot.database real_bot.log = bot.log # Mock database operations real_bot.database.execute = AsyncMock() with patch.object(real_bot, 'user_permitted', return_value=True): await real_bot.ignore_user(mock_evt, "@test:example.com") real_bot.database.execute.assert_called() mock_evt.reply.assert_called() @pytest.mark.asyncio async def test_unignore_command(self, bot, mock_evt): """Test the unignore command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.database = bot.database real_bot.log = bot.log # Mock database operations real_bot.database.execute = AsyncMock() with patch.object(real_bot, 'user_permitted', return_value=True): await real_bot.unignore_user(mock_evt, "@test:example.com") real_bot.database.execute.assert_called() mock_evt.reply.assert_called() @pytest.mark.asyncio async def test_kick_command(self, bot, mock_evt): """Test the kick command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'get_space_roomlist', return_value=["!room1:example.com"]), \ patch.object(real_bot, 'ban_this_user', return_value={"ban_list": {}, "error_list": {}}): await real_bot.kick_user(mock_evt, "@test:example.com") mock_evt.reply.assert_called() @pytest.mark.asyncio async def test_ban_command(self, bot, mock_evt): """Test the ban command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'get_space_roomlist', return_value=["!room1:example.com"]), \ patch.object(real_bot, 'ban_this_user', return_value={"ban_list": {}, "error_list": {}}): await real_bot.ban_user(mock_evt, "@test:example.com") mock_evt.reply.assert_called() @pytest.mark.asyncio async def test_doctor_command(self, bot, mock_evt): """Test the doctor command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.database = bot.database real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'get_space_roomlist', return_value=["!room1:example.com"]): await real_bot.doctor(mock_evt) mock_evt.respond.assert_called() @pytest.mark.asyncio async def test_doctor_room_detail_command(self, bot, mock_evt): """Test the doctor room detail command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.database = bot.database real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, '_doctor_room_detail', return_value=None): await real_bot.doctor_room_detail(mock_evt, "!room:example.com") mock_evt.respond.assert_called() @pytest.mark.asyncio async def test_initialize_command(self, bot, mock_evt): """Test the initialize command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.database = bot.database real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'create_space', return_value=("!space:example.com", "#space:example.com")): await real_bot.initialize(mock_evt, "Test Community") mock_evt.respond.assert_called() @pytest.mark.asyncio async def test_create_room_command(self, bot, mock_evt): """Test the create_room command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'validate_room_aliases', return_value=(True, [])), \ patch.object(real_bot, 'create_room', return_value=("!room:example.com", "#room:example.com")): await real_bot.create_room(mock_evt, "Test Room") mock_evt.respond.assert_called() @pytest.mark.asyncio async def test_create_room_command_alias_conflict(self, bot, mock_evt): """Test create_room command with alias conflict.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'validate_room_aliases', return_value=(False, ["#conflict:example.com"])): await real_bot.create_room(mock_evt, "Test Room") mock_evt.respond.assert_called() # Should mention the conflict @pytest.mark.asyncio async def test_archive_room_command(self, bot, mock_evt): """Test the archive_room command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'do_archive_room', return_value=None): await real_bot.archive_room(mock_evt, "!room:example.com") mock_evt.respond.assert_called() @pytest.mark.asyncio async def test_remove_room_command(self, bot, mock_evt): """Test the remove_room command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'remove_room_aliases', return_value=[]): await real_bot.remove_room(mock_evt, "!room:example.com") mock_evt.respond.assert_called() @pytest.mark.asyncio async def test_join_room_command(self, bot, mock_evt): """Test the join_room command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'join_room', return_value="!room:example.com"): await real_bot.join_room(mock_evt, "!room:example.com") mock_evt.respond.assert_called() @pytest.mark.asyncio async def test_leave_room_command(self, bot, mock_evt): """Test the leave_room command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'leave_room', return_value=None): await real_bot.leave_room(mock_evt, "!room:example.com") mock_evt.respond.assert_called() @pytest.mark.asyncio async def test_verify_command(self, bot, mock_evt): """Test the verify command.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.client = bot.client real_bot.database = bot.database real_bot.log = bot.log # Mock required methods with patch.object(real_bot, 'user_permitted', return_value=True), \ patch.object(real_bot, 'create_verification_dm', return_value="!dm:example.com"): await real_bot.verify_user(mock_evt, "@test:example.com", "!room:example.com") mock_evt.respond.assert_called() @pytest.mark.asyncio async def test_commands_require_permission(self, bot, mock_evt): """Test that commands require proper permissions.""" from community.bot import CommunityBot real_bot = CommunityBot() real_bot.config = bot.config real_bot.log = bot.log # Test various commands that require permission commands_to_test = [ ('sync_space_members', []), ('ignore_user', ['@test:example.com']), ('unignore_user', ['@test:example.com']), ('kick_user', ['@test:example.com']), ('ban_user', ['@test:example.com']), ('doctor', []), ('doctor_room_detail', ['!room:example.com']), ('initialize', ['Test Community']), ('create_room', ['Test Room']), ('archive_room', ['!room:example.com']), ('remove_room', ['!room:example.com']), ('join_room', ['!room:example.com']), ('leave_room', ['!room:example.com']), ('verify_user', ['@test:example.com', '!room:example.com']) ] for command_name, args in commands_to_test: with patch.object(real_bot, 'user_permitted', return_value=False): command_func = getattr(real_bot, command_name) await command_func(mock_evt, *args) # Should respond with permission denied message mock_evt.reply.assert_called() mock_evt.reply.reset_mock()