"""Tests for the FlaskPaste plugin and Bot.shorten_url method.""" import asyncio import importlib.util import sys from pathlib import Path from unittest.mock import patch from derp.irc import Message # plugins/ is not a Python package -- load the module from file path _spec = importlib.util.spec_from_file_location( "plugins.flaskpaste", Path(__file__).resolve().parent.parent / "plugins" / "flaskpaste.py", ) _mod = importlib.util.module_from_spec(_spec) sys.modules[_spec.name] = _mod _spec.loader.exec_module(_mod) from plugins.flaskpaste import ( # noqa: E402 cmd_paste, cmd_shorten, create_paste, shorten_url, ) # -- Helpers ----------------------------------------------------------------- class _FakeState: """In-memory stand-in for bot.state.""" def __init__(self): self._store: dict[str, dict[str, str]] = {} def get(self, plugin: str, key: str, default: str | None = None) -> str | None: return self._store.get(plugin, {}).get(key, default) def set(self, plugin: str, key: str, value: str) -> None: self._store.setdefault(plugin, {})[key] = value def delete(self, plugin: str, key: str) -> bool: try: del self._store[plugin][key] return True except KeyError: return False def keys(self, plugin: str) -> list[str]: return sorted(self._store.get(plugin, {}).keys()) class _FakeRegistry: """Minimal registry stand-in.""" def __init__(self): self._modules: dict = {} class _FakeBot: """Minimal bot stand-in that captures sent/replied messages.""" def __init__(self, *, admin: bool = False): self.sent: list[tuple[str, str]] = [] self.replied: list[str] = [] self.state = _FakeState() self.registry = _FakeRegistry() self._admin = admin self.config: dict = {} async def send(self, target: str, text: str) -> None: self.sent.append((target, text)) async def reply(self, message, text: str) -> None: self.replied.append(text) def _is_admin(self, message) -> bool: return self._admin def _msg(text: str, nick: str = "alice", target: str = "#test") -> Message: """Create a channel PRIVMSG.""" return Message( raw="", prefix=f"{nick}!~{nick}@host", nick=nick, command="PRIVMSG", params=[target, text], tags={}, ) # --------------------------------------------------------------------------- # TestCmdShorten # --------------------------------------------------------------------------- class TestCmdShorten: def test_missing_url(self): bot = _FakeBot() asyncio.run(cmd_shorten(bot, _msg("!shorten"))) assert "Usage:" in bot.replied[0] def test_invalid_scheme(self): bot = _FakeBot() asyncio.run(cmd_shorten(bot, _msg("!shorten ftp://example.com"))) assert "http://" in bot.replied[0] def test_success(self): bot = _FakeBot() async def inner(): with patch.object( _mod, "_shorten_url", return_value="https://paste.mymx.me/s/abc123", ): await cmd_shorten(bot, _msg("!shorten https://example.com/long")) assert "paste.mymx.me/s/abc123" in bot.replied[0] asyncio.run(inner()) def test_failure(self): bot = _FakeBot() async def inner(): with patch.object( _mod, "_shorten_url", side_effect=ConnectionError("down"), ): await cmd_shorten(bot, _msg("!shorten https://example.com/long")) assert "shorten failed" in bot.replied[0] asyncio.run(inner()) # --------------------------------------------------------------------------- # TestCmdPaste # --------------------------------------------------------------------------- class TestCmdPaste: def test_missing_text(self): bot = _FakeBot() asyncio.run(cmd_paste(bot, _msg("!paste"))) assert "Usage:" in bot.replied[0] def test_success(self): bot = _FakeBot() async def inner(): with patch.object( _mod, "_create_paste", return_value="https://paste.mymx.me/xyz789", ): await cmd_paste(bot, _msg("!paste hello world")) assert "paste.mymx.me/xyz789" in bot.replied[0] asyncio.run(inner()) def test_failure(self): bot = _FakeBot() async def inner(): with patch.object( _mod, "_create_paste", side_effect=ConnectionError("down"), ): await cmd_paste(bot, _msg("!paste hello world")) assert "paste failed" in bot.replied[0] asyncio.run(inner()) # --------------------------------------------------------------------------- # TestShortenUrlHelper # --------------------------------------------------------------------------- class TestShortenUrlHelper: def test_returns_short_url(self): bot = _FakeBot() with patch.object( _mod, "_shorten_url", return_value="https://paste.mymx.me/s/short", ): result = shorten_url(bot, "https://example.com/long") assert result == "https://paste.mymx.me/s/short" def test_returns_original_on_error(self): bot = _FakeBot() with patch.object( _mod, "_shorten_url", side_effect=ConnectionError("fail"), ): result = shorten_url(bot, "https://example.com/long") assert result == "https://example.com/long" # --------------------------------------------------------------------------- # TestCreatePasteHelper # --------------------------------------------------------------------------- class TestCreatePasteHelper: def test_returns_paste_url(self): bot = _FakeBot() with patch.object( _mod, "_create_paste", return_value="https://paste.mymx.me/abc", ): result = create_paste(bot, "hello") assert result == "https://paste.mymx.me/abc" def test_returns_none_on_error(self): bot = _FakeBot() with patch.object( _mod, "_create_paste", side_effect=ConnectionError("fail"), ): result = create_paste(bot, "hello") assert result is None