"""Tests for the Wayback Machine archive plugin.""" import asyncio import importlib.util import sys import urllib.error from pathlib import Path from unittest.mock import MagicMock, 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.archive", Path(__file__).resolve().parent.parent / "plugins" / "archive.py", ) _mod = importlib.util.module_from_spec(_spec) sys.modules[_spec.name] = _mod _spec.loader.exec_module(_mod) from plugins.archive import _save_page, cmd_archive # noqa: E402 # -- Helpers ----------------------------------------------------------------- class _FakeBot: def __init__(self): self.replied: list[str] = [] async def reply(self, message, text: str) -> None: self.replied.append(text) def _msg(text: str) -> Message: return Message( raw="", prefix="alice!~alice@host", nick="alice", command="PRIVMSG", params=["#test", text], tags={}, ) # -- _save_page -------------------------------------------------------------- class TestSavePage: def test_content_location_header(self): resp = MagicMock() resp.headers = {"Content-Location": "/web/20260220/https://example.com"} resp.read.return_value = b"" resp.geturl.return_value = "https://web.archive.org/save/https://example.com" with patch.object(_mod, "_urlopen", return_value=resp): result = _save_page("https://example.com") assert "url" in result assert "/web/20260220" in result["url"] def test_final_url_redirect(self): resp = MagicMock() resp.headers = {} resp.read.return_value = b"" resp.geturl.return_value = "https://web.archive.org/web/20260220/https://example.com" with patch.object(_mod, "_urlopen", return_value=resp): result = _save_page("https://example.com") assert "url" in result assert "/web/20260220" in result["url"] def test_fallback_url(self): resp = MagicMock() resp.headers = {} resp.read.return_value = b"" resp.geturl.return_value = "https://web.archive.org/save/ok" with patch.object(_mod, "_urlopen", return_value=resp): result = _save_page("https://example.com") assert "url" in result assert "/web/*/" in result["url"] def test_rate_limit(self): exc = urllib.error.HTTPError( "url", 429, "Too Many Requests", {}, None, ) with patch.object(_mod, "_urlopen", side_effect=exc): result = _save_page("https://example.com") assert "error" in result assert "rate limit" in result["error"] def test_origin_unreachable(self): exc = urllib.error.HTTPError( "url", 523, "Origin Unreachable", {}, None, ) with patch.object(_mod, "_urlopen", side_effect=exc): result = _save_page("https://example.com") assert "error" in result assert "unreachable" in result["error"] def test_generic_http_error(self): exc = urllib.error.HTTPError( "url", 500, "Server Error", {}, None, ) with patch.object(_mod, "_urlopen", side_effect=exc): result = _save_page("https://example.com") assert "error" in result assert "500" in result["error"] def test_timeout(self): with patch.object(_mod, "_urlopen", side_effect=TimeoutError("timed out")): result = _save_page("https://example.com") assert "error" in result assert "timeout" in result["error"] # -- Command handler --------------------------------------------------------- class TestCmdArchive: def test_no_args(self): bot = _FakeBot() asyncio.run(cmd_archive(bot, _msg("!archive"))) assert "Usage" in bot.replied[0] def test_no_scheme(self): bot = _FakeBot() asyncio.run(cmd_archive(bot, _msg("!archive example.com"))) assert "http://" in bot.replied[0] def test_success(self): bot = _FakeBot() result = {"url": "https://web.archive.org/web/20260220/https://example.com"} with patch.object(_mod, "_save_page", return_value=result): asyncio.run(cmd_archive(bot, _msg("!archive https://example.com"))) assert len(bot.replied) == 2 assert "Archiving" in bot.replied[0] assert "Archived:" in bot.replied[1] assert "/web/20260220" in bot.replied[1] def test_error(self): bot = _FakeBot() result = {"error": "rate limited -- try again later"} with patch.object(_mod, "_save_page", return_value=result): asyncio.run(cmd_archive(bot, _msg("!archive https://example.com"))) assert len(bot.replied) == 2 assert "failed" in bot.replied[1].lower() assert "rate limit" in bot.replied[1]