Files
meshcore-bot/tests/test_plugin_loader.py
agessaman 8cf7348321 Enhance command tests with additional assertions and new truncation test
- Added assertions to ensure call arguments are not None in multiple command tests.
- Introduced a new test for the CmdCommand class to verify that long command lists are truncated correctly with a '(N more)' suffix, improving command list readability.
2026-02-13 15:45:29 -08:00

162 lines
5.4 KiB
Python

"""Tests for modules.plugin_loader."""
import pytest
from unittest.mock import Mock, MagicMock, AsyncMock
from modules.plugin_loader import PluginLoader
from modules.commands.base_command import BaseCommand
@pytest.fixture
def loader_bot(mock_logger, minimal_config):
"""Mock bot for PluginLoader tests."""
bot = MagicMock()
bot.logger = mock_logger
bot.config = minimal_config
bot.translator = MagicMock()
bot.translator.translate = Mock(side_effect=lambda k, **kw: k)
bot.translator.get_value = Mock(return_value=None)
bot.command_manager = MagicMock()
bot.command_manager.monitor_channels = ["general"]
bot.command_manager.send_response = AsyncMock(return_value=True)
bot.meshcore = None
return bot
def _load_and_register(loader, plugin_file):
"""Load a plugin and register it in the loader's internal state (like load_all_plugins does)."""
instance = loader.load_plugin(plugin_file)
if instance:
metadata = instance.get_metadata()
name = metadata["name"]
loader.loaded_plugins[name] = instance
loader.plugin_metadata[name] = metadata
loader._build_keyword_mappings(name, metadata)
return instance
class TestDiscover:
"""Tests for plugin discovery."""
def test_discover_plugins_finds_command_files(self, loader_bot):
loader = PluginLoader(loader_bot)
plugins = loader.discover_plugins()
assert isinstance(plugins, list)
assert len(plugins) > 0
# Should find well-known commands
assert "ping_command" in plugins
assert "help_command" in plugins
def test_discover_plugins_excludes_base_and_init(self, loader_bot):
loader = PluginLoader(loader_bot)
plugins = loader.discover_plugins()
assert "__init__" not in plugins
assert "base_command" not in plugins
def test_discover_alternative_plugins_empty_when_no_dir(self, loader_bot, tmp_path):
loader = PluginLoader(loader_bot, commands_dir=str(tmp_path / "nonexistent"))
result = loader.discover_alternative_plugins()
assert result == []
class TestValidatePlugin:
"""Tests for plugin class validation."""
def test_validate_missing_execute(self, loader_bot):
loader = PluginLoader(loader_bot)
class NoExecute:
name = "test"
keywords = ["test"]
errors = loader._validate_plugin(NoExecute)
assert any("execute" in e.lower() for e in errors)
def test_validate_sync_execute(self, loader_bot):
loader = PluginLoader(loader_bot)
class SyncExecute:
name = "test"
keywords = ["test"]
def execute(self, message):
return True
errors = loader._validate_plugin(SyncExecute)
assert any("async" in e.lower() for e in errors)
def test_validate_valid_class(self, loader_bot):
loader = PluginLoader(loader_bot)
class ValidCommand:
name = "test"
keywords = ["test"]
async def execute(self, message):
return True
errors = loader._validate_plugin(ValidCommand)
assert len(errors) == 0
class TestLoadPlugin:
"""Tests for loading individual plugins."""
def test_load_ping_command(self, loader_bot):
loader = PluginLoader(loader_bot)
plugin = loader.load_plugin("ping_command")
assert plugin is not None
assert isinstance(plugin, BaseCommand)
assert plugin.name == "ping"
def test_load_nonexistent_returns_none(self, loader_bot):
loader = PluginLoader(loader_bot)
plugin = loader.load_plugin("totally_nonexistent_command")
assert plugin is None
assert "totally_nonexistent_command" in loader._failed_plugins
class TestKeywordLookup:
"""Tests for keyword-based plugin lookup after registration."""
def test_get_plugin_by_keyword(self, loader_bot):
loader = PluginLoader(loader_bot)
_load_and_register(loader, "ping_command")
result = loader.get_plugin_by_keyword("ping")
assert result is not None
assert result.name == "ping"
def test_get_plugin_by_keyword_miss(self, loader_bot):
loader = PluginLoader(loader_bot)
assert loader.get_plugin_by_keyword("nonexistent") is None
def test_get_plugin_by_name(self, loader_bot):
loader = PluginLoader(loader_bot)
_load_and_register(loader, "ping_command")
result = loader.get_plugin_by_name("ping")
assert result is not None
assert result.name == "ping"
class TestCategoryAndFailed:
"""Tests for category filtering and failed plugin tracking."""
def test_get_plugins_by_category(self, loader_bot):
loader = PluginLoader(loader_bot)
_load_and_register(loader, "ping_command")
_load_and_register(loader, "help_command")
# Ping and help are in the "basic" category
result = loader.get_plugins_by_category("basic")
assert isinstance(result, dict)
assert "ping" in result or "help" in result
def test_get_failed_plugins_returns_copy(self, loader_bot):
loader = PluginLoader(loader_bot)
loader.load_plugin("nonexistent_command")
failed = loader.get_failed_plugins()
assert isinstance(failed, dict)
assert "nonexistent_command" in failed
# Mutating the return should not affect internal state
failed.clear()
assert len(loader.get_failed_plugins()) > 0