more refactoring
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# Test package for community bot
|
||||
@@ -0,0 +1,430 @@
|
||||
"""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()
|
||||
@@ -0,0 +1,452 @@
|
||||
"""Tests for bot event handlers."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, patch, MagicMock
|
||||
from mautrix.types import EventType, UserID, MessageEvent, StateEvent, ReactionEvent
|
||||
from mautrix.errors import MNotFound
|
||||
|
||||
from community.bot import CommunityBot
|
||||
|
||||
|
||||
class TestBotEvents:
|
||||
"""Test cases for bot event 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,
|
||||
"track_messages": True,
|
||||
"track_reactions": 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,
|
||||
"proactive_banning": True,
|
||||
"check_if_human": True,
|
||||
"verification_phrases": ["test phrase"],
|
||||
"verification_attempts": 3,
|
||||
"verification_message": "Please verify",
|
||||
"invite_power_level": 50,
|
||||
"uncensor_pl": 50
|
||||
}
|
||||
return bot
|
||||
|
||||
@pytest.fixture
|
||||
def mock_message_evt(self):
|
||||
"""Create a mock MessageEvent for testing."""
|
||||
evt = Mock(spec=MessageEvent)
|
||||
evt.sender = "@user:example.com"
|
||||
evt.room_id = "!room:example.com"
|
||||
evt.timestamp = 1234567890
|
||||
evt.content = Mock()
|
||||
evt.content.body = "test message"
|
||||
evt.content.msgtype = "m.text"
|
||||
evt.reply = AsyncMock()
|
||||
evt.respond = AsyncMock()
|
||||
evt.react = AsyncMock()
|
||||
return evt
|
||||
|
||||
@pytest.fixture
|
||||
def mock_state_evt(self):
|
||||
"""Create a mock StateEvent for testing."""
|
||||
evt = Mock(spec=StateEvent)
|
||||
evt.sender = "@user:example.com"
|
||||
evt.room_id = "!room:example.com"
|
||||
evt.state_key = "@user:example.com"
|
||||
evt.content = {
|
||||
"entity": "@banned:example.com",
|
||||
"recommendation": "ban"
|
||||
}
|
||||
evt.prev_content = {}
|
||||
return evt
|
||||
|
||||
@pytest.fixture
|
||||
def mock_reaction_evt(self):
|
||||
"""Create a mock ReactionEvent for testing."""
|
||||
evt = Mock(spec=ReactionEvent)
|
||||
evt.sender = "@user:example.com"
|
||||
evt.room_id = "!room:example.com"
|
||||
evt.content = Mock()
|
||||
evt.content.relates_to = Mock()
|
||||
evt.content.relates_to.event_id = "!msg:example.com"
|
||||
evt.content.relates_to.key = "👍"
|
||||
return evt
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_ban_event_proactive_banning_enabled(self, bot, mock_state_evt):
|
||||
"""Test ban event handler with proactive banning enabled."""
|
||||
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, 'get_banlist_roomids', return_value=["!banlist:example.com"]), \
|
||||
patch.object(real_bot, 'ban_this_user', return_value={"ban_list": {}, "error_list": {}}):
|
||||
|
||||
await real_bot.check_ban_event(mock_state_evt)
|
||||
|
||||
# Should call ban_this_user
|
||||
real_bot.ban_this_user.assert_called_once_with("@banned:example.com")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_ban_event_proactive_banning_disabled(self, bot, mock_state_evt):
|
||||
"""Test ban event handler with proactive banning disabled."""
|
||||
from community.bot import CommunityBot
|
||||
|
||||
real_bot = CommunityBot()
|
||||
real_bot.config = {**bot.config, "proactive_banning": False}
|
||||
real_bot.client = bot.client
|
||||
real_bot.log = bot.log
|
||||
|
||||
with patch.object(real_bot, 'get_banlist_roomids', return_value=["!banlist:example.com"]):
|
||||
await real_bot.check_ban_event(mock_state_evt)
|
||||
|
||||
# Should not call ban_this_user
|
||||
real_bot.ban_this_user.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_ban_event_wrong_room(self, bot, mock_state_evt):
|
||||
"""Test ban event handler with wrong room."""
|
||||
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, 'get_banlist_roomids', return_value=["!other:example.com"]):
|
||||
await real_bot.check_ban_event(mock_state_evt)
|
||||
|
||||
# Should not call ban_this_user
|
||||
real_bot.ban_this_user.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_power_levels(self, bot, mock_state_evt):
|
||||
"""Test power levels sync event handler."""
|
||||
from community.bot import CommunityBot
|
||||
|
||||
real_bot = CommunityBot()
|
||||
real_bot.config = bot.config
|
||||
real_bot.client = bot.client
|
||||
real_bot.log = bot.log
|
||||
|
||||
# Mock power level changes
|
||||
mock_state_evt.prev_content = {"users": {"@user:example.com": 25}}
|
||||
mock_state_evt.content = {"users": {"@user:example.com": 50}}
|
||||
|
||||
with patch.object(real_bot, 'get_space_roomlist', return_value=["!room1:example.com", "!room2:example.com"]), \
|
||||
patch.object(real_bot, 'sync_power_levels_to_room', return_value=None):
|
||||
|
||||
await real_bot.sync_power_levels(mock_state_evt)
|
||||
|
||||
# Should sync to all rooms
|
||||
assert real_bot.sync_power_levels_to_room.call_count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_power_levels_wrong_room(self, bot, mock_state_evt):
|
||||
"""Test power levels sync with wrong room."""
|
||||
from community.bot import CommunityBot
|
||||
|
||||
real_bot = CommunityBot()
|
||||
real_bot.config = bot.config
|
||||
real_bot.client = bot.client
|
||||
real_bot.log = bot.log
|
||||
|
||||
mock_state_evt.room_id = "!other:example.com"
|
||||
|
||||
with patch.object(real_bot, 'get_space_roomlist', return_value=["!room1:example.com"]):
|
||||
await real_bot.sync_power_levels(mock_state_evt)
|
||||
|
||||
# Should not sync to any rooms
|
||||
real_bot.sync_power_levels_to_room.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_leave_events(self, bot, mock_state_evt):
|
||||
"""Test leave events handler."""
|
||||
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 database operations
|
||||
real_bot.database.execute = AsyncMock()
|
||||
|
||||
with patch.object(real_bot, 'get_space_roomlist', return_value=["!room1:example.com", "!room2:example.com"]):
|
||||
await real_bot.handle_leave_events(mock_state_evt)
|
||||
|
||||
# Should delete user from database
|
||||
real_bot.database.execute.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_leave(self, bot, mock_state_evt):
|
||||
"""Test leave event handler."""
|
||||
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
|
||||
|
||||
with patch.object(real_bot, 'handle_leave_events', return_value=None):
|
||||
await real_bot.handle_leave(mock_state_evt)
|
||||
|
||||
real_bot.handle_leave_events.assert_called_once_with(mock_state_evt)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_kick(self, bot, mock_state_evt):
|
||||
"""Test kick event handler."""
|
||||
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
|
||||
|
||||
with patch.object(real_bot, 'handle_leave_events', return_value=None):
|
||||
await real_bot.handle_kick(mock_state_evt)
|
||||
|
||||
real_bot.handle_leave_events.assert_called_once_with(mock_state_evt)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_ban(self, bot, mock_state_evt):
|
||||
"""Test ban event handler."""
|
||||
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
|
||||
|
||||
with patch.object(real_bot, 'handle_leave_events', return_value=None):
|
||||
await real_bot.handle_ban(mock_state_evt)
|
||||
|
||||
real_bot.handle_leave_events.assert_called_once_with(mock_state_evt)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_newjoin_event(self, bot, mock_state_evt):
|
||||
"""Test new join event handler."""
|
||||
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 database operations
|
||||
real_bot.database.execute = AsyncMock()
|
||||
|
||||
with patch.object(real_bot, 'get_space_roomlist', return_value=["!room1:example.com", "!room2:example.com"]), \
|
||||
patch.object(real_bot, 'upsert_user_timestamp', return_value=None):
|
||||
|
||||
await real_bot.newjoin(mock_state_evt)
|
||||
|
||||
# Should update user timestamp
|
||||
real_bot.upsert_user_timestamp.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_message_timestamp_tracking_enabled(self, bot, mock_message_evt):
|
||||
"""Test message timestamp update with tracking enabled."""
|
||||
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 power levels
|
||||
power_levels = Mock()
|
||||
power_levels.get_user_level.return_value = 25
|
||||
|
||||
real_bot.client.get_state_event = AsyncMock(return_value=power_levels)
|
||||
real_bot.database.execute = AsyncMock()
|
||||
|
||||
with patch.object(real_bot, 'get_space_roomlist', return_value=["!room1:example.com"]), \
|
||||
patch.object(real_bot, 'upsert_user_timestamp', return_value=None):
|
||||
|
||||
await real_bot.update_message_timestamp(mock_message_evt)
|
||||
|
||||
# Should update user timestamp
|
||||
real_bot.upsert_user_timestamp.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_message_timestamp_tracking_disabled(self, bot, mock_message_evt):
|
||||
"""Test message timestamp update with tracking disabled."""
|
||||
from community.bot import CommunityBot
|
||||
|
||||
real_bot = CommunityBot()
|
||||
real_bot.config = {**bot.config, "track_messages": False}
|
||||
real_bot.client = bot.client
|
||||
real_bot.database = bot.database
|
||||
real_bot.log = bot.log
|
||||
|
||||
with patch.object(real_bot, 'get_space_roomlist', return_value=["!room1:example.com"]):
|
||||
await real_bot.update_message_timestamp(mock_message_evt)
|
||||
|
||||
# Should not update user timestamp
|
||||
real_bot.upsert_user_timestamp.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_verification(self, bot, mock_message_evt):
|
||||
"""Test verification message handler."""
|
||||
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 verification state
|
||||
verification_state = {
|
||||
"user_id": "@user:example.com",
|
||||
"target_room_id": "!room:example.com",
|
||||
"verification_phrase": "test phrase",
|
||||
"attempts_remaining": 3,
|
||||
"required_power_level": 50
|
||||
}
|
||||
|
||||
real_bot.database.fetchrow = AsyncMock(return_value=verification_state)
|
||||
real_bot.database.execute = AsyncMock()
|
||||
|
||||
# Mock message content
|
||||
mock_message_evt.content.body = "test phrase"
|
||||
|
||||
with patch.object(real_bot, 'user_permitted', return_value=True), \
|
||||
patch.object(real_bot, 'join_room', return_value="!room:example.com"):
|
||||
|
||||
await real_bot.handle_verification(mock_message_evt)
|
||||
|
||||
# Should process verification
|
||||
real_bot.database.execute.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_verification_wrong_phrase(self, bot, mock_message_evt):
|
||||
"""Test verification with wrong phrase."""
|
||||
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 verification state
|
||||
verification_state = {
|
||||
"user_id": "@user:example.com",
|
||||
"target_room_id": "!room:example.com",
|
||||
"verification_phrase": "correct phrase",
|
||||
"attempts_remaining": 3,
|
||||
"required_power_level": 50
|
||||
}
|
||||
|
||||
real_bot.database.fetchrow = AsyncMock(return_value=verification_state)
|
||||
real_bot.database.execute = AsyncMock()
|
||||
|
||||
# Mock message content with wrong phrase
|
||||
mock_message_evt.content.body = "wrong phrase"
|
||||
|
||||
await real_bot.handle_verification(mock_message_evt)
|
||||
|
||||
# Should decrement attempts
|
||||
real_bot.database.execute.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_verification_no_state(self, bot, mock_message_evt):
|
||||
"""Test verification with no verification state."""
|
||||
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 no verification state
|
||||
real_bot.database.fetchrow = AsyncMock(return_value=None)
|
||||
|
||||
await real_bot.handle_verification(mock_message_evt)
|
||||
|
||||
# Should not process verification
|
||||
real_bot.database.execute.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_reaction_tracking_enabled(self, bot, mock_reaction_evt):
|
||||
"""Test reaction event handler with tracking enabled."""
|
||||
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 power levels
|
||||
power_levels = Mock()
|
||||
power_levels.get_user_level.return_value = 25
|
||||
|
||||
real_bot.client.get_state_event = AsyncMock(return_value=power_levels)
|
||||
real_bot.database.execute = AsyncMock()
|
||||
|
||||
with patch.object(real_bot, 'get_space_roomlist', return_value=["!room1:example.com"]), \
|
||||
patch.object(real_bot, 'upsert_user_timestamp', return_value=None):
|
||||
|
||||
await real_bot.handle_reaction(mock_reaction_evt)
|
||||
|
||||
# Should update user timestamp
|
||||
real_bot.upsert_user_timestamp.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_reaction_tracking_disabled(self, bot, mock_reaction_evt):
|
||||
"""Test reaction event handler with tracking disabled."""
|
||||
from community.bot import CommunityBot
|
||||
|
||||
real_bot = CommunityBot()
|
||||
real_bot.config = {**bot.config, "track_reactions": False}
|
||||
real_bot.client = bot.client
|
||||
real_bot.database = bot.database
|
||||
real_bot.log = bot.log
|
||||
|
||||
with patch.object(real_bot, 'get_space_roomlist', return_value=["!room1:example.com"]):
|
||||
await real_bot.handle_reaction(mock_reaction_evt)
|
||||
|
||||
# Should not update user timestamp
|
||||
real_bot.upsert_user_timestamp.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_reaction_wrong_room(self, bot, mock_reaction_evt):
|
||||
"""Test reaction event handler with wrong room."""
|
||||
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
|
||||
|
||||
with patch.object(real_bot, 'get_space_roomlist', return_value=["!other:example.com"]):
|
||||
await real_bot.handle_reaction(mock_reaction_evt)
|
||||
|
||||
# Should not update user timestamp
|
||||
real_bot.upsert_user_timestamp.assert_not_called()
|
||||
@@ -0,0 +1,253 @@
|
||||
"""Tests for database utility functions."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, patch
|
||||
import asyncio
|
||||
|
||||
from community.helpers.database_utils import (
|
||||
get_messages_to_redact, redact_messages, upsert_user_timestamp,
|
||||
get_inactive_users, cleanup_stale_verification_states,
|
||||
get_verification_state, create_verification_state,
|
||||
update_verification_attempts, delete_verification_state
|
||||
)
|
||||
|
||||
|
||||
class TestDatabaseUtils:
|
||||
"""Test cases for database utility functions."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_messages_to_redact_success(self):
|
||||
"""Test getting messages to redact successfully."""
|
||||
client = Mock()
|
||||
|
||||
# Mock message events
|
||||
msg1 = Mock()
|
||||
msg1.content = Mock()
|
||||
msg1.content.serialize.return_value = {"body": "test"}
|
||||
|
||||
msg2 = Mock()
|
||||
msg2.content = None
|
||||
|
||||
msg3 = Mock()
|
||||
msg3.content = Mock()
|
||||
msg3.content.serialize.return_value = {"body": "test2"}
|
||||
|
||||
messages = Mock()
|
||||
messages.events = [msg1, msg2, msg3]
|
||||
|
||||
client.get_messages = AsyncMock(return_value=messages)
|
||||
|
||||
logger = Mock()
|
||||
result = await get_messages_to_redact(client, "!room:example.com", "@user:example.com", logger)
|
||||
|
||||
assert len(result) == 2 # Only msg1 and msg3 have content
|
||||
assert msg1 in result
|
||||
assert msg3 in result
|
||||
assert msg2 not in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_messages_to_redact_error(self):
|
||||
"""Test getting messages to redact with error."""
|
||||
client = Mock()
|
||||
client.get_messages = AsyncMock(side_effect=Exception("Network error"))
|
||||
|
||||
logger = Mock()
|
||||
result = await get_messages_to_redact(client, "!room:example.com", "@user:example.com", logger)
|
||||
|
||||
assert result == []
|
||||
logger.error.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_redact_messages_success(self):
|
||||
"""Test redacting messages successfully."""
|
||||
client = Mock()
|
||||
client.redact = AsyncMock()
|
||||
|
||||
database = Mock()
|
||||
database.fetch = AsyncMock(return_value=[
|
||||
{"event_id": "!msg1:example.com"},
|
||||
{"event_id": "!msg2:example.com"}
|
||||
])
|
||||
database.execute = AsyncMock()
|
||||
|
||||
logger = Mock()
|
||||
|
||||
with patch('asyncio.sleep', new_callable=AsyncMock):
|
||||
result = await redact_messages(client, database, "!room:example.com", 0.1, logger)
|
||||
|
||||
assert result["success"] == 2
|
||||
assert result["failure"] == 0
|
||||
assert client.redact.call_count == 2
|
||||
assert database.execute.call_count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_redact_messages_rate_limited(self):
|
||||
"""Test redacting messages with rate limiting."""
|
||||
client = Mock()
|
||||
client.redact = AsyncMock(side_effect=Exception("Too Many Requests"))
|
||||
|
||||
database = Mock()
|
||||
database.fetch = AsyncMock(return_value=[
|
||||
{"event_id": "!msg1:example.com"}
|
||||
])
|
||||
|
||||
logger = Mock()
|
||||
|
||||
with patch('asyncio.sleep', new_callable=AsyncMock):
|
||||
result = await redact_messages(client, database, "!room:example.com", 0.1, logger)
|
||||
|
||||
assert result["success"] == 0
|
||||
assert result["failure"] == 0 # Rate limited, so no failure count
|
||||
logger.warning.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upsert_user_timestamp_success(self):
|
||||
"""Test upserting user timestamp successfully."""
|
||||
database = Mock()
|
||||
database.execute = AsyncMock()
|
||||
|
||||
logger = Mock()
|
||||
|
||||
await upsert_user_timestamp(database, "@user:example.com", 1234567890, logger)
|
||||
|
||||
database.execute.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_inactive_users_success(self):
|
||||
"""Test getting inactive users successfully."""
|
||||
database = Mock()
|
||||
database.fetch = AsyncMock(side_effect=[
|
||||
[{"mxid": "@user1:example.com"}, {"mxid": "@user2:example.com"}], # warn results
|
||||
[{"mxid": "@user3:example.com"}] # kick results
|
||||
])
|
||||
|
||||
logger = Mock()
|
||||
|
||||
with patch('time.time', return_value=1234567890):
|
||||
result = await get_inactive_users(database, 7, 14, logger)
|
||||
|
||||
assert len(result["warn"]) == 2
|
||||
assert len(result["kick"]) == 1
|
||||
assert "@user1:example.com" in result["warn"]
|
||||
assert "@user3:example.com" in result["kick"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_inactive_users_error(self):
|
||||
"""Test getting inactive users with error."""
|
||||
database = Mock()
|
||||
database.fetch = AsyncMock(side_effect=Exception("Database error"))
|
||||
|
||||
logger = Mock()
|
||||
|
||||
result = await get_inactive_users(database, 7, 14, logger)
|
||||
|
||||
assert result == {"warn": [], "kick": []}
|
||||
logger.error.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cleanup_stale_verification_states_success(self):
|
||||
"""Test cleaning up stale verification states successfully."""
|
||||
database = Mock()
|
||||
database.execute = AsyncMock()
|
||||
|
||||
logger = Mock()
|
||||
|
||||
await cleanup_stale_verification_states(database, logger)
|
||||
|
||||
database.execute.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cleanup_stale_verification_states_error(self):
|
||||
"""Test cleaning up stale verification states with error."""
|
||||
database = Mock()
|
||||
database.execute = AsyncMock(side_effect=Exception("Database error"))
|
||||
|
||||
logger = Mock()
|
||||
|
||||
await cleanup_stale_verification_states(database, logger)
|
||||
|
||||
logger.error.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_verification_state_success(self):
|
||||
"""Test getting verification state successfully."""
|
||||
database = Mock()
|
||||
database.fetchrow = AsyncMock(return_value={
|
||||
"dm_room_id": "!dm:example.com",
|
||||
"user_id": "@user:example.com",
|
||||
"target_room_id": "!room:example.com",
|
||||
"verification_phrase": "test phrase",
|
||||
"attempts_remaining": 3,
|
||||
"required_power_level": 50
|
||||
})
|
||||
|
||||
result = await get_verification_state(database, "!dm:example.com")
|
||||
|
||||
assert result is not None
|
||||
assert result["dm_room_id"] == "!dm:example.com"
|
||||
assert result["user_id"] == "@user:example.com"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_verification_state_not_found(self):
|
||||
"""Test getting verification state when not found."""
|
||||
database = Mock()
|
||||
database.fetchrow = AsyncMock(return_value=None)
|
||||
|
||||
result = await get_verification_state(database, "!dm:example.com")
|
||||
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_verification_state_error(self):
|
||||
"""Test getting verification state with error."""
|
||||
database = Mock()
|
||||
database.fetchrow = AsyncMock(side_effect=Exception("Database error"))
|
||||
|
||||
result = await get_verification_state(database, "!dm:example.com")
|
||||
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_verification_state_success(self):
|
||||
"""Test creating verification state successfully."""
|
||||
database = Mock()
|
||||
database.execute = AsyncMock()
|
||||
|
||||
await create_verification_state(
|
||||
database, "!dm:example.com", "@user:example.com",
|
||||
"!room:example.com", "test phrase", 3, 50
|
||||
)
|
||||
|
||||
database.execute.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_verification_state_error(self):
|
||||
"""Test creating verification state with error (should not raise)."""
|
||||
database = Mock()
|
||||
database.execute = AsyncMock(side_effect=Exception("Database error"))
|
||||
|
||||
# Should not raise exception
|
||||
await create_verification_state(
|
||||
database, "!dm:example.com", "@user:example.com",
|
||||
"!room:example.com", "test phrase", 3, 50
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_verification_attempts_success(self):
|
||||
"""Test updating verification attempts successfully."""
|
||||
database = Mock()
|
||||
database.execute = AsyncMock()
|
||||
|
||||
await update_verification_attempts(database, "!dm:example.com", 2)
|
||||
|
||||
database.execute.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_verification_state_success(self):
|
||||
"""Test deleting verification state successfully."""
|
||||
database = Mock()
|
||||
database.execute = AsyncMock()
|
||||
|
||||
await delete_verification_state(database, "!dm:example.com")
|
||||
|
||||
database.execute.assert_called_once()
|
||||
@@ -0,0 +1,106 @@
|
||||
"""Tests for message utility functions."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
from mautrix.types import MessageType, MediaMessageEventContent
|
||||
|
||||
from community.helpers.message_utils import (
|
||||
flag_message, flag_instaban, censor_room,
|
||||
sanitize_room_name, generate_community_slug
|
||||
)
|
||||
|
||||
|
||||
class TestMessageUtils:
|
||||
"""Test cases for message utility functions."""
|
||||
|
||||
def test_flag_message_file_types(self):
|
||||
"""Test that file messages are flagged when censor_files is True."""
|
||||
msg = Mock()
|
||||
msg.content.msgtype = MessageType.FILE
|
||||
msg.content.body = "test file"
|
||||
|
||||
assert flag_message(msg, [], True) == True
|
||||
assert flag_message(msg, [], False) == False
|
||||
|
||||
def test_flag_message_wordlist(self):
|
||||
"""Test that messages are flagged based on wordlist patterns."""
|
||||
msg = Mock()
|
||||
msg.content.msgtype = MessageType.TEXT
|
||||
msg.content.body = "This is a test message with badword"
|
||||
|
||||
wordlist = [r"badword", r"another.*pattern"]
|
||||
|
||||
assert flag_message(msg, wordlist, False) == True
|
||||
|
||||
msg.content.body = "This is a clean message"
|
||||
assert flag_message(msg, wordlist, False) == False
|
||||
|
||||
def test_flag_message_invalid_regex(self):
|
||||
"""Test that invalid regex patterns are handled gracefully."""
|
||||
msg = Mock()
|
||||
msg.content.msgtype = MessageType.TEXT
|
||||
msg.content.body = "test message"
|
||||
|
||||
wordlist = [r"valid.*pattern", r"[invalid", r"another.*pattern"]
|
||||
|
||||
# Should not raise exception and should work with valid patterns
|
||||
result = flag_message(msg, wordlist, False)
|
||||
assert isinstance(result, bool)
|
||||
|
||||
def test_flag_instaban(self):
|
||||
"""Test instant ban flagging."""
|
||||
msg = Mock()
|
||||
msg.content.msgtype = MessageType.TEXT
|
||||
msg.content.body = "This contains instaban_word"
|
||||
|
||||
instaban_list = [r"instaban_word", r"another.*instaban"]
|
||||
|
||||
assert flag_instaban(msg, instaban_list) == True
|
||||
|
||||
msg.content.body = "This is clean"
|
||||
assert flag_instaban(msg, instaban_list) == False
|
||||
|
||||
def test_censor_room_boolean_config(self):
|
||||
"""Test room censoring with boolean configuration."""
|
||||
msg = Mock()
|
||||
msg.room_id = "!room123:example.com"
|
||||
|
||||
assert censor_room(msg, True) == True
|
||||
assert censor_room(msg, False) == False
|
||||
|
||||
def test_censor_room_list_config(self):
|
||||
"""Test room censoring with list configuration."""
|
||||
msg = Mock()
|
||||
msg.room_id = "!room123:example.com"
|
||||
|
||||
censor_list = ["!room123:example.com", "!room456:example.com"]
|
||||
|
||||
assert censor_room(msg, censor_list) == True
|
||||
|
||||
msg.room_id = "!room789:example.com"
|
||||
assert censor_room(msg, censor_list) == False
|
||||
|
||||
def test_censor_room_invalid_config(self):
|
||||
"""Test room censoring with invalid configuration."""
|
||||
msg = Mock()
|
||||
msg.room_id = "!room123:example.com"
|
||||
|
||||
assert censor_room(msg, "invalid") == False
|
||||
assert censor_room(msg, None) == False
|
||||
|
||||
def test_sanitize_room_name(self):
|
||||
"""Test room name sanitization."""
|
||||
assert sanitize_room_name("Test Room 123") == "testroom123"
|
||||
assert sanitize_room_name("Special@#$%Characters") == "specialcharacters"
|
||||
assert sanitize_room_name("UPPERCASE") == "uppercase"
|
||||
assert sanitize_room_name("123 Numbers") == "123numbers"
|
||||
assert sanitize_room_name("") == ""
|
||||
|
||||
def test_generate_community_slug(self):
|
||||
"""Test community slug generation."""
|
||||
assert generate_community_slug("Test Community") == "tc"
|
||||
assert generate_community_slug("My Awesome Community") == "mac"
|
||||
assert generate_community_slug("Single") == "s"
|
||||
assert generate_community_slug("Multiple Spaces") == "ms"
|
||||
assert generate_community_slug("") == ""
|
||||
assert generate_community_slug(" ") == ""
|
||||
@@ -0,0 +1,163 @@
|
||||
"""Tests for report utility functions."""
|
||||
|
||||
import pytest
|
||||
|
||||
from community.helpers.report_utils import (
|
||||
generate_activity_report, split_doctor_report, format_ban_results,
|
||||
format_sync_results
|
||||
)
|
||||
|
||||
|
||||
class TestReportUtils:
|
||||
"""Test cases for report utility functions."""
|
||||
|
||||
def test_generate_activity_report_success(self):
|
||||
"""Test generating activity report successfully."""
|
||||
database_results = {
|
||||
"active": [{"mxid": "@user1:example.com"}, {"mxid": "@user2:example.com"}],
|
||||
"inactive": [{"mxid": "@user3:example.com"}],
|
||||
"ignored": [{"mxid": "@user4:example.com"}]
|
||||
}
|
||||
|
||||
result = generate_activity_report(database_results)
|
||||
|
||||
assert result["active"] == ["@user1:example.com", "@user2:example.com"]
|
||||
assert result["inactive"] == ["@user3:example.com"]
|
||||
assert result["ignored"] == ["@user4:example.com"]
|
||||
|
||||
def test_generate_activity_report_empty(self):
|
||||
"""Test generating activity report with empty results."""
|
||||
database_results = {
|
||||
"active": [],
|
||||
"inactive": [],
|
||||
"ignored": []
|
||||
}
|
||||
|
||||
result = generate_activity_report(database_results)
|
||||
|
||||
assert result["active"] == ["none"]
|
||||
assert result["inactive"] == ["none"]
|
||||
assert result["ignored"] == ["none"]
|
||||
|
||||
def test_generate_activity_report_missing_keys(self):
|
||||
"""Test generating activity report with missing keys."""
|
||||
database_results = {}
|
||||
|
||||
result = generate_activity_report(database_results)
|
||||
|
||||
assert result["active"] == ["none"]
|
||||
assert result["inactive"] == ["none"]
|
||||
assert result["ignored"] == ["none"]
|
||||
|
||||
def test_split_doctor_report_small(self):
|
||||
"""Test splitting small report that doesn't need splitting."""
|
||||
report_text = "This is a small report that fits in one chunk."
|
||||
|
||||
result = split_doctor_report(report_text, 1000)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0] == report_text
|
||||
|
||||
def test_split_doctor_report_large(self):
|
||||
"""Test splitting large report into chunks."""
|
||||
# Create a large report
|
||||
lines = [f"Line {i}: This is a test line for splitting" for i in range(100)]
|
||||
report_text = "\n".join(lines)
|
||||
|
||||
result = split_doctor_report(report_text, 100) # Small chunk size
|
||||
|
||||
assert len(result) > 1
|
||||
assert all(len(chunk) <= 100 for chunk in result)
|
||||
|
||||
# Verify all content is preserved (account for newlines)
|
||||
combined = "\n".join(result)
|
||||
assert combined == report_text
|
||||
|
||||
def test_split_doctor_report_with_sections(self):
|
||||
"""Test splitting report with section headers."""
|
||||
report_text = """<h3>Section 1</h3>
|
||||
This is section 1 content.
|
||||
<h3>Section 2</h3>
|
||||
This is section 2 content.
|
||||
<h3>Section 3</h3>
|
||||
This is section 3 content."""
|
||||
|
||||
result = split_doctor_report(report_text, 50) # Small chunk size
|
||||
|
||||
assert len(result) > 1
|
||||
assert all(len(chunk) <= 50 for chunk in result)
|
||||
|
||||
def test_format_ban_results_success(self):
|
||||
"""Test formatting ban results with successful bans."""
|
||||
ban_event_map = {
|
||||
"ban_list": {
|
||||
"@user1:example.com": ["Room 1", "Room 2"],
|
||||
"@user2:example.com": ["Room 3"]
|
||||
},
|
||||
"error_list": {}
|
||||
}
|
||||
|
||||
result = format_ban_results(ban_event_map)
|
||||
|
||||
assert "Banned @user1:example.com from: Room 1, Room 2" in result
|
||||
assert "Banned @user2:example.com from: Room 3" in result
|
||||
|
||||
def test_format_ban_results_with_errors(self):
|
||||
"""Test formatting ban results with errors."""
|
||||
ban_event_map = {
|
||||
"ban_list": {
|
||||
"@user1:example.com": ["Room 1"]
|
||||
},
|
||||
"error_list": {
|
||||
"@user2:example.com": ["Room 2", "Room 3"]
|
||||
}
|
||||
}
|
||||
|
||||
result = format_ban_results(ban_event_map)
|
||||
|
||||
assert "Banned @user1:example.com from: Room 1" in result
|
||||
assert "Failed to ban @user2:example.com from: Room 2, Room 3" in result
|
||||
|
||||
def test_format_ban_results_empty(self):
|
||||
"""Test formatting empty ban results."""
|
||||
ban_event_map = {
|
||||
"ban_list": {},
|
||||
"error_list": {}
|
||||
}
|
||||
|
||||
result = format_ban_results(ban_event_map)
|
||||
|
||||
assert result == "No ban operations performed"
|
||||
|
||||
def test_format_sync_results_success(self):
|
||||
"""Test formatting sync results with data."""
|
||||
sync_results = {
|
||||
"added": ["@user1:example.com", "@user2:example.com"],
|
||||
"dropped": ["@user3:example.com"]
|
||||
}
|
||||
|
||||
result = format_sync_results(sync_results)
|
||||
|
||||
assert "Added: @user1:example.com<br />@user2:example.com" in result
|
||||
assert "Dropped: @user3:example.com" in result
|
||||
|
||||
def test_format_sync_results_empty(self):
|
||||
"""Test formatting empty sync results."""
|
||||
sync_results = {
|
||||
"added": [],
|
||||
"dropped": []
|
||||
}
|
||||
|
||||
result = format_sync_results(sync_results)
|
||||
|
||||
assert "Added: none" in result
|
||||
assert "Dropped: none" in result
|
||||
|
||||
def test_format_sync_results_missing_keys(self):
|
||||
"""Test formatting sync results with missing keys."""
|
||||
sync_results = {}
|
||||
|
||||
result = format_sync_results(sync_results)
|
||||
|
||||
assert "Added: none" in result
|
||||
assert "Dropped: none" in result
|
||||
@@ -0,0 +1,203 @@
|
||||
"""Tests for room utility functions."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, patch
|
||||
from mautrix.types import EventType, PowerLevelStateEventContent
|
||||
from mautrix.errors import MNotFound
|
||||
|
||||
from community.helpers.room_utils import (
|
||||
validate_room_alias, validate_room_aliases, get_room_version_and_creators,
|
||||
is_modern_room_version, user_has_unlimited_power, get_moderators_and_above
|
||||
)
|
||||
|
||||
|
||||
class TestRoomUtils:
|
||||
"""Test cases for room utility functions."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_room_alias_exists(self):
|
||||
"""Test alias validation when alias exists."""
|
||||
client = Mock()
|
||||
client.resolve_room_alias = AsyncMock()
|
||||
|
||||
# Alias exists - should return False
|
||||
result = await validate_room_alias(client, "test", "example.com")
|
||||
assert result == False
|
||||
client.resolve_room_alias.assert_called_once_with("#test:example.com")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_room_alias_not_exists(self):
|
||||
"""Test alias validation when alias doesn't exist."""
|
||||
client = Mock()
|
||||
client.resolve_room_alias = AsyncMock(side_effect=MNotFound("Room not found", 404))
|
||||
|
||||
# Alias doesn't exist - should return True
|
||||
result = await validate_room_alias(client, "test", "example.com")
|
||||
assert result == True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_room_alias_error(self):
|
||||
"""Test alias validation with error."""
|
||||
client = Mock()
|
||||
client.resolve_room_alias = AsyncMock(side_effect=Exception("Network error"))
|
||||
|
||||
# Error should return True (assume available)
|
||||
result = await validate_room_alias(client, "test", "example.com")
|
||||
assert result == True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_room_aliases_no_slug(self):
|
||||
"""Test alias validation without community slug."""
|
||||
client = Mock()
|
||||
|
||||
result = await validate_room_aliases(client, ["room1", "room2"], "", "example.com")
|
||||
assert result == (False, [])
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_room_aliases_success(self):
|
||||
"""Test successful alias validation."""
|
||||
client = Mock()
|
||||
client.resolve_room_alias = AsyncMock(side_effect=MNotFound("Room not found", 404))
|
||||
|
||||
result = await validate_room_aliases(client, ["room1", "room2"], "test", "example.com")
|
||||
assert result == (True, [])
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_room_aliases_conflicts(self):
|
||||
"""Test alias validation with conflicts."""
|
||||
client = Mock()
|
||||
|
||||
def resolve_side_effect(alias):
|
||||
if "room1" in alias:
|
||||
return {"room_id": "!room1:example.com"} # Exists
|
||||
else:
|
||||
raise MNotFound() # Doesn't exist
|
||||
|
||||
client.resolve_room_alias = AsyncMock(side_effect=resolve_side_effect)
|
||||
|
||||
result = await validate_room_aliases(client, ["room1", "room2"], "test", "example.com")
|
||||
assert result == (False, ["#room1-test:example.com"])
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_room_version_and_creators_success(self):
|
||||
"""Test getting room version and creators successfully."""
|
||||
client = Mock()
|
||||
|
||||
# Mock state events
|
||||
create_event = Mock()
|
||||
create_event.type = EventType.ROOM_CREATE
|
||||
create_event.sender = "@creator:example.com"
|
||||
create_event.content = {
|
||||
"room_version": "12",
|
||||
"additional_creators": ["@creator2:example.com"]
|
||||
}
|
||||
|
||||
other_event = Mock()
|
||||
other_event.type = EventType.ROOM_POWER_LEVELS
|
||||
|
||||
client.get_state = AsyncMock(return_value=[create_event, other_event])
|
||||
|
||||
version, creators = await get_room_version_and_creators(client, "!room:example.com")
|
||||
|
||||
assert version == "12"
|
||||
assert "@creator:example.com" in creators
|
||||
assert "@creator2:example.com" in creators
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_room_version_and_creators_no_create_event(self):
|
||||
"""Test getting room version when no create event exists."""
|
||||
client = Mock()
|
||||
client.get_state = AsyncMock(return_value=[])
|
||||
|
||||
version, creators = await get_room_version_and_creators(client, "!room:example.com")
|
||||
|
||||
assert version == "1"
|
||||
assert creators == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_room_version_and_creators_error(self):
|
||||
"""Test getting room version with error."""
|
||||
client = Mock()
|
||||
client.get_state = AsyncMock(side_effect=Exception("Network error"))
|
||||
|
||||
version, creators = await get_room_version_and_creators(client, "!room:example.com")
|
||||
|
||||
assert version == "1"
|
||||
assert creators == []
|
||||
|
||||
def test_is_modern_room_version(self):
|
||||
"""Test modern room version detection."""
|
||||
assert is_modern_room_version("12") == True
|
||||
assert is_modern_room_version("13") == True
|
||||
assert is_modern_room_version("11") == False
|
||||
assert is_modern_room_version("1") == False
|
||||
assert is_modern_room_version("invalid") == False
|
||||
assert is_modern_room_version("") == False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_has_unlimited_power_modern_room(self):
|
||||
"""Test unlimited power check in modern room."""
|
||||
client = Mock()
|
||||
|
||||
with patch('community.helpers.room_utils.get_room_version_and_creators') as mock_get_version:
|
||||
mock_get_version.return_value = ("12", ["@user:example.com"])
|
||||
|
||||
result = await user_has_unlimited_power(client, "@user:example.com", "!room:example.com")
|
||||
assert result == True
|
||||
|
||||
result = await user_has_unlimited_power(client, "@other:example.com", "!room:example.com")
|
||||
assert result == False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_has_unlimited_power_old_room(self):
|
||||
"""Test unlimited power check in old room."""
|
||||
client = Mock()
|
||||
|
||||
with patch('community.helpers.room_utils.get_room_version_and_creators') as mock_get_version:
|
||||
mock_get_version.return_value = ("11", ["@user:example.com"])
|
||||
|
||||
result = await user_has_unlimited_power(client, "@user:example.com", "!room:example.com")
|
||||
assert result == False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_has_unlimited_power_error(self):
|
||||
"""Test unlimited power check with error."""
|
||||
client = Mock()
|
||||
|
||||
with patch('community.helpers.room_utils.get_room_version_and_creators') as mock_get_version:
|
||||
mock_get_version.side_effect = Exception("Network error")
|
||||
|
||||
result = await user_has_unlimited_power(client, "@user:example.com", "!room:example.com")
|
||||
assert result == False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_moderators_and_above_success(self):
|
||||
"""Test getting moderators successfully."""
|
||||
client = Mock()
|
||||
|
||||
power_levels = Mock()
|
||||
power_levels.users = {
|
||||
"@user1:example.com": 50, # Moderator
|
||||
"@user2:example.com": 100, # Admin
|
||||
"@user3:example.com": 25, # Regular user
|
||||
"@user4:example.com": 75, # Above moderator
|
||||
}
|
||||
|
||||
client.get_state_event = AsyncMock(return_value=power_levels)
|
||||
|
||||
moderators = await get_moderators_and_above(client, "!room:example.com")
|
||||
|
||||
assert "@user1:example.com" in moderators
|
||||
assert "@user2:example.com" in moderators
|
||||
assert "@user4:example.com" in moderators
|
||||
assert "@user3:example.com" not in moderators
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_moderators_and_above_error(self):
|
||||
"""Test getting moderators with error."""
|
||||
client = Mock()
|
||||
client.get_state_event = AsyncMock(side_effect=Exception("Network error"))
|
||||
|
||||
moderators = await get_moderators_and_above(client, "!room:example.com")
|
||||
|
||||
assert moderators == []
|
||||
@@ -0,0 +1,223 @@
|
||||
"""Tests for user utility functions."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, patch
|
||||
from mautrix.types import EventType, UserID
|
||||
from mautrix.errors import MNotFound
|
||||
|
||||
from community.helpers.user_utils import (
|
||||
check_if_banned, get_banlist_roomids, ban_user_from_rooms, user_permitted
|
||||
)
|
||||
|
||||
|
||||
class TestUserUtils:
|
||||
"""Test cases for user utility functions."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_if_banned_success(self):
|
||||
"""Test successful ban check."""
|
||||
client = Mock()
|
||||
client.get_joined_rooms = AsyncMock(return_value=["!room1:example.com", "!room2:example.com"])
|
||||
|
||||
# Mock state events
|
||||
ban_rule = Mock()
|
||||
ban_rule.type.t = "m.policy.rule.user"
|
||||
ban_rule.content = {
|
||||
"entity": "@banned:example.com",
|
||||
"recommendation": "ban"
|
||||
}
|
||||
|
||||
client.get_state = AsyncMock(return_value=[ban_rule])
|
||||
|
||||
# Mock get_banlist_roomids to return the room ID directly
|
||||
with patch('community.helpers.user_utils.get_banlist_roomids') as mock_get_banlists:
|
||||
mock_get_banlists.return_value = ["!room1:example.com"]
|
||||
|
||||
logger = Mock()
|
||||
result = await check_if_banned(client, "@banned:example.com", ["!room1:example.com"], logger)
|
||||
|
||||
assert result == True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_if_banned_not_banned(self):
|
||||
"""Test ban check when user is not banned."""
|
||||
client = Mock()
|
||||
client.get_joined_rooms = AsyncMock(return_value=["!room1:example.com"])
|
||||
|
||||
with patch('community.helpers.user_utils.get_banlist_roomids') as mock_get_banlists:
|
||||
mock_get_banlists.return_value = ["!room1:example.com"]
|
||||
|
||||
# Mock state events with no ban rules
|
||||
client.get_state = AsyncMock(return_value=[])
|
||||
|
||||
logger = Mock()
|
||||
result = await check_if_banned(client, "@user:example.com", ["!room1:example.com"], logger)
|
||||
|
||||
assert result == False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_if_banned_room_not_joined(self):
|
||||
"""Test ban check when bot is not in banlist room."""
|
||||
client = Mock()
|
||||
client.get_joined_rooms = AsyncMock(return_value=["!room2:example.com"])
|
||||
|
||||
with patch('community.helpers.user_utils.get_banlist_roomids') as mock_get_banlists:
|
||||
mock_get_banlists.return_value = ["!room1:example.com"]
|
||||
|
||||
logger = Mock()
|
||||
result = await check_if_banned(client, "@user:example.com", ["!room1:example.com"], logger)
|
||||
|
||||
assert result == False
|
||||
logger.error.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_banlist_roomids_aliases(self):
|
||||
"""Test getting banlist room IDs with aliases."""
|
||||
client = Mock()
|
||||
client.resolve_room_alias = AsyncMock(return_value={"room_id": "!room1:example.com"})
|
||||
|
||||
banlists = ["#banlist1:example.com", "!room2:example.com"]
|
||||
logger = Mock()
|
||||
|
||||
result = await get_banlist_roomids(client, banlists, logger)
|
||||
|
||||
assert "!room1:example.com" in result
|
||||
assert "!room2:example.com" in result
|
||||
client.resolve_room_alias.assert_called_once_with("#banlist1:example.com")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_banlist_roomids_alias_error(self):
|
||||
"""Test getting banlist room IDs with alias resolution error."""
|
||||
client = Mock()
|
||||
client.resolve_room_alias = AsyncMock(side_effect=Exception("Network error"))
|
||||
|
||||
banlists = ["#banlist1:example.com", "!room2:example.com"]
|
||||
logger = Mock()
|
||||
|
||||
result = await get_banlist_roomids(client, banlists, logger)
|
||||
|
||||
assert "!room2:example.com" in result
|
||||
assert "!room1:example.com" not in result
|
||||
logger.error.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ban_user_from_rooms_success(self):
|
||||
"""Test successful user banning from rooms."""
|
||||
client = Mock()
|
||||
client.ban_user = AsyncMock()
|
||||
client.get_state_event = AsyncMock(return_value={"name": "Test Room"})
|
||||
|
||||
roomlist = ["!room1:example.com", "!room2:example.com"]
|
||||
logger = Mock()
|
||||
|
||||
result = await ban_user_from_rooms(
|
||||
client, "@user:example.com", roomlist, "banned", False, False, None, None, 0.1, logger
|
||||
)
|
||||
|
||||
assert "ban_list" in result
|
||||
assert "error_list" in result
|
||||
assert "@user:example.com" in result["ban_list"]
|
||||
assert len(result["ban_list"]["@user:example.com"]) == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ban_user_from_rooms_with_redaction(self):
|
||||
"""Test user banning with message redaction."""
|
||||
client = Mock()
|
||||
client.ban_user = AsyncMock()
|
||||
client.get_state_event = AsyncMock(return_value={"name": "Test Room"})
|
||||
|
||||
# Mock message redaction
|
||||
mock_msg = Mock()
|
||||
mock_msg.event_id = "!msg123:example.com"
|
||||
get_messages_func = AsyncMock(return_value=[mock_msg])
|
||||
|
||||
database = Mock()
|
||||
database.execute = AsyncMock()
|
||||
|
||||
roomlist = ["!room1:example.com"]
|
||||
logger = Mock()
|
||||
|
||||
result = await ban_user_from_rooms(
|
||||
client, "@user:example.com", roomlist, "banned", False, True,
|
||||
get_messages_func, database, 0.1, logger
|
||||
)
|
||||
|
||||
assert "ban_list" in result
|
||||
database.execute.assert_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ban_user_from_rooms_error(self):
|
||||
"""Test user banning with errors."""
|
||||
client = Mock()
|
||||
client.ban_user = AsyncMock(side_effect=Exception("Ban failed"))
|
||||
client.get_state_event = AsyncMock(return_value={"name": "Test Room"})
|
||||
|
||||
roomlist = ["!room1:example.com"]
|
||||
logger = Mock()
|
||||
|
||||
result = await ban_user_from_rooms(
|
||||
client, "@user:example.com", roomlist, "banned", False, False, None, None, 0.1, logger
|
||||
)
|
||||
|
||||
assert "error_list" in result
|
||||
assert "@user:example.com" in result["error_list"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_permitted_unlimited_power(self):
|
||||
"""Test user permission check with unlimited power."""
|
||||
client = Mock()
|
||||
|
||||
with patch('community.helpers.room_utils.user_has_unlimited_power') as mock_unlimited:
|
||||
mock_unlimited.return_value = True
|
||||
|
||||
result = await user_permitted(client, "@user:example.com", "!parent:example.com", 50, None, None)
|
||||
|
||||
assert result == True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_permitted_sufficient_level(self):
|
||||
"""Test user permission check with sufficient power level."""
|
||||
client = Mock()
|
||||
|
||||
with patch('community.helpers.room_utils.user_has_unlimited_power') as mock_unlimited:
|
||||
mock_unlimited.return_value = False
|
||||
|
||||
power_levels = Mock()
|
||||
power_levels.get_user_level.return_value = 75
|
||||
|
||||
client.get_state_event = AsyncMock(return_value=power_levels)
|
||||
|
||||
result = await user_permitted(client, "@user:example.com", "!parent:example.com", 50, None, None)
|
||||
|
||||
assert result == True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_permitted_insufficient_level(self):
|
||||
"""Test user permission check with insufficient power level."""
|
||||
client = Mock()
|
||||
|
||||
with patch('community.helpers.room_utils.user_has_unlimited_power') as mock_unlimited:
|
||||
mock_unlimited.return_value = False
|
||||
|
||||
power_levels = Mock()
|
||||
power_levels.get_user_level.return_value = 25
|
||||
|
||||
client.get_state_event = AsyncMock(return_value=power_levels)
|
||||
|
||||
result = await user_permitted(client, "@user:example.com", "!parent:example.com", 50, None, None)
|
||||
|
||||
assert result == False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_permitted_error(self):
|
||||
"""Test user permission check with error."""
|
||||
client = Mock()
|
||||
|
||||
with patch('community.helpers.room_utils.user_has_unlimited_power') as mock_unlimited:
|
||||
mock_unlimited.side_effect = Exception("Network error")
|
||||
|
||||
logger = Mock()
|
||||
result = await user_permitted(client, "@user:example.com", "!parent:example.com", 50, None, logger)
|
||||
|
||||
assert result == False
|
||||
logger.error.assert_called()
|
||||
Reference in New Issue
Block a user