Files
derp/tests/test_plugin.py
user bf45abcbad feat: initial implementation
Asyncio IRC bot with decorator-based plugin system.
Zero external dependencies, Python 3.11+.

- IRC protocol: message parsing, formatting, async TCP/TLS connection
- Plugin system: @command and @event decorators, file-based loading
- Bot orchestrator: connect, dispatch, reconnect, nick recovery
- CLI: argparse entry point with TOML config
- Built-in plugins: ping, help, version, echo
- 28 unit tests for parser and plugin system
2026-02-15 00:37:31 +01:00

129 lines
3.5 KiB
Python

"""Tests for the plugin system."""
import textwrap
from pathlib import Path
from derp.plugin import PluginRegistry, command, event
class TestDecorators:
"""Test that decorators mark functions correctly."""
def test_command_decorator(self):
@command("test", help="a test command")
async def handler(bot, msg):
pass
assert handler._derp_command == "test"
assert handler._derp_help == "a test command"
def test_event_decorator(self):
@event("join")
async def handler(bot, msg):
pass
assert handler._derp_event == "JOIN"
def test_event_decorator_case(self):
@event("PRIVMSG")
async def handler(bot, msg):
pass
assert handler._derp_event == "PRIVMSG"
class TestRegistry:
"""Test the plugin registry."""
def test_register_command(self):
registry = PluginRegistry()
async def handler(bot, msg):
pass
registry.register_command("test", handler, help="test help")
assert "test" in registry.commands
assert registry.commands["test"].help == "test help"
def test_register_event(self):
registry = PluginRegistry()
async def handler(bot, msg):
pass
registry.register_event("PRIVMSG", handler)
assert "PRIVMSG" in registry.events
assert len(registry.events["PRIVMSG"]) == 1
def test_multiple_event_handlers(self):
registry = PluginRegistry()
async def handler_a(bot, msg):
pass
async def handler_b(bot, msg):
pass
registry.register_event("JOIN", handler_a)
registry.register_event("JOIN", handler_b)
assert len(registry.events["JOIN"]) == 2
def test_load_plugin_file(self, tmp_path: Path):
plugin_code = textwrap.dedent("""\
from derp.plugin import command, event
@command("greet", help="Say hello")
async def cmd_greet(bot, msg):
pass
@event("JOIN")
async def on_join(bot, msg):
pass
""")
plugin_file = tmp_path / "greet.py"
plugin_file.write_text(plugin_code)
registry = PluginRegistry()
registry.load_plugin(plugin_file)
assert "greet" in registry.commands
assert "JOIN" in registry.events
def test_load_directory(self, tmp_path: Path):
for name in ("a.py", "b.py"):
(tmp_path / name).write_text(textwrap.dedent(f"""\
from derp.plugin import command
@command("{name[0]}", help="{name}")
async def cmd(bot, msg):
pass
"""))
registry = PluginRegistry()
registry.load_directory(tmp_path)
assert "a" in registry.commands
assert "b" in registry.commands
def test_skip_underscore_files(self, tmp_path: Path):
(tmp_path / "__init__.py").write_text("")
(tmp_path / "_private.py").write_text("x = 1\n")
(tmp_path / "valid.py").write_text(textwrap.dedent("""\
from derp.plugin import command
@command("valid")
async def cmd(bot, msg):
pass
"""))
registry = PluginRegistry()
registry.load_directory(tmp_path)
assert "valid" in registry.commands
assert len(registry.commands) == 1
def test_load_missing_directory(self, tmp_path: Path):
registry = PluginRegistry()
registry.load_directory(tmp_path / "nonexistent")
assert len(registry.commands) == 0