test: add config, format_msg, and Bot API tests

New test_config.py: merge, load, resolve_config tests.
Extend test_irc.py: format_msg edge cases (colon, empty, multi-param).
Extend test_plugin.py: Bot API via FakeConnection, _split_utf8 tests.
Test count: 92 -> 120.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
user
2026-02-15 03:27:18 +01:00
parent b48c289403
commit 8129b79cdb
3 changed files with 216 additions and 1 deletions

View File

@@ -1,9 +1,10 @@
"""Tests for the plugin system."""
import asyncio
import textwrap
from pathlib import Path
from derp.bot import _AMBIGUOUS, Bot
from derp.bot import _AMBIGUOUS, Bot, _split_utf8
from derp.irc import Message
from derp.plugin import PluginRegistry, command, event
from derp.state import StateStore
@@ -545,3 +546,110 @@ class TestStateStore:
store.close()
store2 = StateStore(db_path)
assert store2.get("plug", "persist") == "yes"
class _FakeConnection:
"""Minimal IRCConnection stand-in that captures sent lines."""
def __init__(self):
self.sent: list[str] = []
async def send(self, line: str) -> None:
self.sent.append(line)
def _make_test_bot() -> Bot:
"""Create a Bot with a FakeConnection for testing the public API."""
config = {
"server": {"host": "localhost", "port": 6667, "tls": False,
"nick": "test", "user": "test", "realname": "test"},
"bot": {"prefix": "!", "channels": [], "plugins_dir": "plugins"},
}
bot = Bot(config, PluginRegistry())
bot.conn = _FakeConnection() # type: ignore[assignment]
return bot
class TestBotAPI:
"""Test Bot public API methods via FakeConnection."""
def test_send(self):
bot = _make_test_bot()
asyncio.run(bot.send("#ch", "hello world"))
assert bot.conn.sent == ["PRIVMSG #ch :hello world"]
def test_send_single_word(self):
bot = _make_test_bot()
asyncio.run(bot.send("#ch", "hello"))
assert bot.conn.sent == ["PRIVMSG #ch hello"]
def test_send_multiline(self):
bot = _make_test_bot()
asyncio.run(bot.send("#ch", "line one\nline two"))
assert len(bot.conn.sent) == 2
assert bot.conn.sent[0] == "PRIVMSG #ch :line one"
assert bot.conn.sent[1] == "PRIVMSG #ch :line two"
def test_reply_channel(self):
bot = _make_test_bot()
msg = Message(raw="", prefix="nick!u@h", nick="nick",
command="PRIVMSG", params=["#ch", "hi"], tags={})
asyncio.run(bot.reply(msg, "hello there"))
assert bot.conn.sent == ["PRIVMSG #ch :hello there"]
def test_reply_pm(self):
bot = _make_test_bot()
msg = Message(raw="", prefix="nick!u@h", nick="nick",
command="PRIVMSG", params=["testbot", "hi"], tags={})
asyncio.run(bot.reply(msg, "hello there"))
assert bot.conn.sent == ["PRIVMSG nick :hello there"]
def test_join(self):
bot = _make_test_bot()
asyncio.run(bot.join("#test"))
assert bot.conn.sent == ["JOIN :#test"]
def test_kick(self):
bot = _make_test_bot()
asyncio.run(bot.kick("#ch", "baduser", "bad behavior"))
assert bot.conn.sent == ["KICK #ch baduser :bad behavior"]
def test_mode(self):
bot = _make_test_bot()
asyncio.run(bot.mode("#ch", "+o", "nick"))
assert bot.conn.sent == ["MODE #ch +o nick"]
def test_set_topic(self):
bot = _make_test_bot()
asyncio.run(bot.set_topic("#ch", "new topic"))
assert bot.conn.sent == ["TOPIC #ch :new topic"]
class TestSplitUtf8:
"""Test UTF-8 safe message splitting."""
def test_short_text(self):
assert _split_utf8("hello", 100) == ["hello"]
def test_ascii_split(self):
text = "a" * 20
chunks = _split_utf8(text, 10)
assert len(chunks) == 2
assert all(len(c.encode("utf-8")) <= 10 for c in chunks)
assert "".join(chunks) == text
def test_multibyte_boundary(self):
# Each char is 3 bytes in UTF-8
text = "\u00e9" * 10 # e-acute, 2 bytes each -> 20 bytes total
chunks = _split_utf8(text, 7)
recombined = "".join(chunks)
assert recombined == text
for chunk in chunks:
assert len(chunk.encode("utf-8")) <= 7
def test_empty(self):
assert _split_utf8("", 10) == [""]
def test_exact_fit(self):
text = "abcde"
assert _split_utf8(text, 5) == ["abcde"]