Bot.shorten_url() method delegates to flaskpaste plugin when loaded. RSS, YouTube, and pastemoni announcements auto-shorten links. Includes test_flaskpaste.py (9 cases) and FakeBot updates in 3 test files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
213 lines
6.3 KiB
Python
213 lines
6.3 KiB
Python
"""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
|