Files
meshcore-bot/tests/test_joke_command.py
agessaman 53112c5f05 Refactor: streamline imports and enhance documentation in utility and service files
- Updated the `resolve_path` function in `utils.py` to clarify behavior regarding absolute paths.
- Changed type hints in `discord_bridge_service.py` for better clarity and consistency.
- Removed unused imports and unnecessary comments in various test files to improve code cleanliness and readability.
2026-03-18 18:27:05 -07:00

320 lines
11 KiB
Python

"""Tests for modules.commands.joke_command — pure logic functions."""
import configparser
from datetime import datetime
from unittest.mock import MagicMock, Mock, patch
from modules.commands.joke_command import JokeCommand
from tests.conftest import mock_message
def _make_bot(seasonal=True, long_jokes=False):
bot = MagicMock()
bot.logger = Mock()
config = configparser.ConfigParser()
config.add_section("Bot")
config.set("Bot", "bot_name", "TestBot")
config.add_section("Channels")
config.set("Channels", "monitor_channels", "general")
config.set("Channels", "respond_to_dms", "true")
config.add_section("Keywords")
config.add_section("Joke_Command")
config.set("Joke_Command", "enabled", "true")
config.set("Joke_Command", "seasonal_jokes", str(seasonal).lower())
config.set("Joke_Command", "long_jokes", str(long_jokes).lower())
bot.config = config
bot.translator = MagicMock()
bot.translator.translate = Mock(side_effect=lambda key, **kw: key)
bot.command_manager = MagicMock()
bot.command_manager.monitor_channels = ["general"]
return bot
class TestGetSeasonalDefault:
"""Tests for get_seasonal_default."""
def test_october_returns_spooky(self):
cmd = JokeCommand(_make_bot(seasonal=True))
# datetime is imported inside the function, patch datetime.now at its source
with patch("datetime.datetime") as mock_dt:
mock_dt.now.return_value = datetime(2024, 10, 15)
result = cmd.get_seasonal_default()
assert result == "Spooky"
def test_december_returns_christmas(self):
cmd = JokeCommand(_make_bot(seasonal=True))
with patch("datetime.datetime") as mock_dt:
mock_dt.now.return_value = datetime(2024, 12, 15)
result = cmd.get_seasonal_default()
assert result == "Christmas"
def test_other_month_returns_none(self):
cmd = JokeCommand(_make_bot(seasonal=True))
with patch("datetime.datetime") as mock_dt:
mock_dt.now.return_value = datetime(2024, 6, 15)
result = cmd.get_seasonal_default()
assert result is None
def test_seasonal_disabled_returns_none(self):
cmd = JokeCommand(_make_bot(seasonal=False))
# Even in October, seasonal disabled returns None
result = cmd.get_seasonal_default()
assert result is None
class TestFormatJoke:
"""Tests for format_joke."""
def setup_method(self):
self.cmd = JokeCommand(_make_bot())
def test_single_type_joke(self):
data = {"type": "single", "joke": "Why so funny?"}
result = self.cmd.format_joke(data)
assert "🎭" in result
assert "Why so funny?" in result
def test_twopart_joke(self):
data = {"type": "twopart", "setup": "Why?", "delivery": "Because!"}
result = self.cmd.format_joke(data)
assert "🎭" in result
assert "Why?" in result
assert "Because!" in result
def test_empty_single_joke_returns_fallback(self):
data = {"type": "single", "joke": ""}
result = self.cmd.format_joke(data)
assert "🎭" in result
def test_unknown_type_fallback(self):
data = {"type": "weird", "joke": "Some joke"}
result = self.cmd.format_joke(data)
assert "🎭" in result
assert "Some joke" in result
class TestSplitJoke:
"""Tests for split_joke."""
def setup_method(self):
self.cmd = JokeCommand(_make_bot())
def test_splits_at_newline(self):
joke = "🎭 Setup part.\n\nDelivery part."
result = self.cmd.split_joke(joke)
assert len(result) == 2
def test_each_part_has_emoji(self):
joke = "🎭 First sentence. Second sentence here."
result = self.cmd.split_joke(joke)
for part in result:
assert "🎭" in part
def test_no_split_point_splits_midpoint(self):
joke = "🎭 abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz extra"
result = self.cmd.split_joke(joke)
assert len(result) == 2
class TestIsDarkJokeRequest:
"""Tests for is_dark_joke_request."""
def setup_method(self):
self.cmd = JokeCommand(_make_bot())
def test_dark_category_is_dark(self):
msg = mock_message(content="joke dark")
assert self.cmd.is_dark_joke_request(msg) is True
def test_other_category_not_dark(self):
msg = mock_message(content="joke programming")
assert self.cmd.is_dark_joke_request(msg) is False
def test_no_category_not_dark(self):
msg = mock_message(content="joke")
assert self.cmd.is_dark_joke_request(msg) is False
def test_exclamation_prefix(self):
msg = mock_message(content="!joke dark")
assert self.cmd.is_dark_joke_request(msg) is True
class TestJokeCanExecute:
"""Tests for can_execute."""
def test_dark_joke_in_channel_blocked(self):
bot = _make_bot()
cmd = JokeCommand(bot)
msg = mock_message(content="joke dark", channel="general", is_dm=False)
# Dark jokes in public channels should be blocked
assert cmd.can_execute(msg) is False
def test_dark_joke_in_dm_allowed(self):
bot = _make_bot()
cmd = JokeCommand(bot)
msg = mock_message(content="joke dark", is_dm=True)
assert cmd.can_execute(msg) is True
def test_disabled_command_blocked(self):
bot = _make_bot()
bot.config.set("Joke_Command", "enabled", "false")
cmd = JokeCommand(bot)
msg = mock_message(content="joke", channel="general")
assert cmd.can_execute(msg) is False
class TestJokeMatchesKeyword:
def test_joke_matches(self):
cmd = JokeCommand(_make_bot())
assert cmd.matches_keyword(mock_message(content="joke")) is True
def test_jokes_matches(self):
cmd = JokeCommand(_make_bot())
assert cmd.matches_keyword(mock_message(content="jokes")) is True
def test_joke_with_category_matches(self):
cmd = JokeCommand(_make_bot())
assert cmd.matches_keyword(mock_message(content="joke programming")) is True
def test_other_does_not_match(self):
cmd = JokeCommand(_make_bot())
assert cmd.matches_keyword(mock_message(content="dadjoke")) is False
def test_exclamation_prefix_matches(self):
cmd = JokeCommand(_make_bot())
assert cmd.matches_keyword(mock_message(content="!joke")) is True
class TestJokeGetHelpText:
def test_public_channel_excludes_dark(self):
cmd = JokeCommand(_make_bot())
msg = mock_message(content="joke", channel="general", is_dm=False)
result = cmd.get_help_text(message=msg)
assert "dark" not in result
assert "joke" in result.lower()
def test_dm_includes_dark(self):
cmd = JokeCommand(_make_bot())
msg = mock_message(content="joke", is_dm=True)
result = cmd.get_help_text(message=msg)
assert "dark" in result
def test_no_message_returns_all_categories(self):
cmd = JokeCommand(_make_bot())
result = cmd.get_help_text()
assert "joke" in result.lower()
class TestJokeFormatUnknownType:
def test_unknown_type_with_no_text_returns_fallback(self):
cmd = JokeCommand(_make_bot())
data = {"type": "weird"}
result = cmd.format_joke(data)
assert "🎭" in result
def test_twopart_only_setup(self):
cmd = JokeCommand(_make_bot())
data = {"type": "twopart", "setup": "Why?", "delivery": ""}
result = cmd.format_joke(data)
assert "🎭" in result
assert "Why?" in result
class TestJokeExecute:
def test_execute_invalid_category_sends_error(self):
import asyncio
from unittest.mock import AsyncMock
bot = _make_bot()
bot.command_manager.send_response = AsyncMock(return_value=True)
cmd = JokeCommand(bot)
msg = mock_message(content="joke badcategory", channel="general")
result = asyncio.run(cmd.execute(msg))
assert result is True
bot.command_manager.send_response.assert_called_once()
def test_execute_no_category_with_joke(self):
import asyncio
from unittest.mock import AsyncMock, patch
bot = _make_bot()
bot.command_manager.send_response = AsyncMock(return_value=True)
cmd = JokeCommand(bot)
msg = mock_message(content="joke", channel="general")
with patch.object(cmd, "get_joke_with_length_handling", new_callable=AsyncMock,
return_value={"type": "single", "joke": "A short joke!"}):
result = asyncio.run(cmd.execute(msg))
assert result is True
def test_execute_returns_true_when_no_joke(self):
import asyncio
from unittest.mock import AsyncMock, patch
bot = _make_bot()
bot.command_manager.send_response = AsyncMock(return_value=True)
cmd = JokeCommand(bot)
msg = mock_message(content="joke", channel="general")
with patch.object(cmd, "get_joke_with_length_handling", new_callable=AsyncMock,
return_value=None):
result = asyncio.run(cmd.execute(msg))
assert result is True
def test_execute_dark_category_no_joke(self):
import asyncio
from unittest.mock import AsyncMock, patch
bot = _make_bot()
bot.command_manager.send_response = AsyncMock(return_value=True)
cmd = JokeCommand(bot)
msg = mock_message(content="joke dark", is_dm=True)
with patch.object(cmd, "get_joke_with_length_handling", new_callable=AsyncMock,
return_value=None):
result = asyncio.run(cmd.execute(msg))
assert result is True
def test_execute_handles_exception(self):
import asyncio
from unittest.mock import AsyncMock, patch
bot = _make_bot()
bot.command_manager.send_response = AsyncMock(return_value=True)
cmd = JokeCommand(bot)
msg = mock_message(content="joke", channel="general")
with patch.object(cmd, "get_joke_with_length_handling", new_callable=AsyncMock,
side_effect=Exception("API error")):
result = asyncio.run(cmd.execute(msg))
assert result is True
class TestSendJokeWithLengthHandling:
def test_short_joke_sends_single_message(self):
import asyncio
from unittest.mock import AsyncMock
bot = _make_bot()
bot.command_manager.send_response = AsyncMock(return_value=True)
cmd = JokeCommand(bot)
msg = mock_message(content="joke")
joke_data = {"type": "single", "joke": "Short!"}
asyncio.run(cmd.send_joke_with_length_handling(msg, joke_data))
bot.command_manager.send_response.assert_called_once()
def test_long_joke_split(self):
import asyncio
from unittest.mock import AsyncMock
bot = _make_bot()
bot.command_manager.send_response = AsyncMock(return_value=True)
cmd = JokeCommand(bot)
msg = mock_message(content="joke")
long_joke = "x" * 50 + ". " + "y" * 80
joke_data = {"type": "single", "joke": long_joke}
asyncio.run(cmd.send_joke_with_length_handling(msg, joke_data))
assert bot.command_manager.send_response.call_count >= 1