""" Tests for the mention conversion utilities. These tests verify that human-readable @mentions are properly converted to Discord's ID-based format for triggering notifications. """ import pytest from unittest.mock import MagicMock, AsyncMock from utils.mentions import convert_mentions, convert_mentions_async class TestConvertMentions: """Tests for the synchronous convert_mentions function.""" def test_converts_role_mention(self): """Test that @RoleName is converted to <@&role_id>.""" # Create mock guild with a role mock_role = MagicMock() mock_role.name = "Waikiki Whale Sharks" mock_role.id = 123456789 mock_guild = MagicMock() mock_guild.roles = [mock_role] mock_guild.members = [] content = "@Waikiki Whale Sharks" result = convert_mentions(content, mock_guild) assert result == "<@&123456789>" def test_converts_role_mention_case_insensitive(self): """Test that role matching is case-insensitive.""" mock_role = MagicMock() mock_role.name = "Waikiki Whale Sharks" mock_role.id = 123456789 mock_guild = MagicMock() mock_guild.roles = [mock_role] mock_guild.members = [] content = "@waikiki whale sharks" result = convert_mentions(content, mock_guild) assert result == "<@&123456789>" def test_converts_user_mention_by_display_name(self): """Test that @DisplayName is converted to <@user_id>.""" mock_member = MagicMock() mock_member.display_name = "CalCorum" mock_member.name = "cal" mock_member.id = 987654321 mock_guild = MagicMock() mock_guild.roles = [] mock_guild.members = [mock_member] content = "@CalCorum" result = convert_mentions(content, mock_guild) assert result == "<@987654321>" def test_converts_user_mention_by_username(self): """Test that @username is converted to <@user_id>.""" mock_member = MagicMock() mock_member.display_name = "Cal Corum" mock_member.name = "calcorum" mock_member.id = 987654321 mock_guild = MagicMock() mock_guild.roles = [] mock_guild.members = [mock_member] content = "@calcorum" result = convert_mentions(content, mock_guild) assert result == "<@987654321>" def test_converts_multiple_mentions(self): """Test that multiple mentions in same content are all converted.""" mock_role1 = MagicMock() mock_role1.name = "Team A" mock_role1.id = 111 mock_role2 = MagicMock() mock_role2.name = "Team B" mock_role2.id = 222 mock_guild = MagicMock() mock_guild.roles = [mock_role1, mock_role2] mock_guild.members = [] content = "@Team A vs @Team B" result = convert_mentions(content, mock_guild) assert result == "<@&111> vs <@&222>" def test_preserves_already_formatted_mentions(self): """Test that <@123> and <@&456> are not modified.""" mock_guild = MagicMock() mock_guild.roles = [] mock_guild.members = [] content = "Already formatted: <@123456789> and <@&987654321>" result = convert_mentions(content, mock_guild) assert result == content def test_preserves_unrecognized_mentions(self): """Test that @mentions with no match are left unchanged.""" mock_guild = MagicMock() mock_guild.roles = [] mock_guild.members = [] content = "@UnknownRole says hello" result = convert_mentions(content, mock_guild) assert result == "@UnknownRole says hello" def test_handles_none_guild(self): """Test that None guild returns content unchanged.""" content = "@SomeRole" result = convert_mentions(content, None) assert result == content def test_handles_empty_content(self): """Test that empty content returns empty.""" mock_guild = MagicMock() result = convert_mentions("", mock_guild) assert result == "" def test_handles_mentions_with_punctuation(self): """Test that mentions followed by punctuation are handled.""" mock_role = MagicMock() mock_role.name = "TestRole" mock_role.id = 123 mock_guild = MagicMock() mock_guild.roles = [mock_role] mock_guild.members = [] content = "Hello @TestRole!" result = convert_mentions(content, mock_guild) assert result == "Hello <@&123>!" def test_role_takes_priority_over_user(self): """Test that when a name matches both role and user, role wins.""" mock_role = MagicMock() mock_role.name = "SameName" mock_role.id = 111 mock_member = MagicMock() mock_member.display_name = "SameName" mock_member.name = "samename" mock_member.id = 222 mock_guild = MagicMock() mock_guild.roles = [mock_role] mock_guild.members = [mock_member] content = "@SameName" result = convert_mentions(content, mock_guild) # Role should be matched first assert result == "<@&111>" class TestConvertMentionsAsync: """Tests for the async convert_mentions_async function.""" @pytest.mark.asyncio async def test_async_converts_role_mention(self): """Test async version converts mentions.""" mock_role = MagicMock() mock_role.name = "TestRole" mock_role.id = 123 mock_guild = MagicMock() mock_guild.roles = [mock_role] mock_guild.members = [] mock_guild.chunked = True content = "@TestRole" result = await convert_mentions_async(content, mock_guild) assert result == "<@&123>" @pytest.mark.asyncio async def test_async_chunks_guild_when_requested(self): """Test that guild.chunk() is called when fetch_members=True and not chunked.""" mock_guild = MagicMock() mock_guild.roles = [] mock_guild.members = [] mock_guild.chunked = False mock_guild.chunk = AsyncMock() await convert_mentions_async("@Test", mock_guild, fetch_members=True) mock_guild.chunk.assert_called_once() @pytest.mark.asyncio async def test_async_skips_chunk_when_already_chunked(self): """Test that guild.chunk() is not called when already chunked.""" mock_guild = MagicMock() mock_guild.roles = [] mock_guild.members = [] mock_guild.chunked = True mock_guild.chunk = AsyncMock() await convert_mentions_async("@Test", mock_guild, fetch_members=True) mock_guild.chunk.assert_not_called()