Improve tests
All checks were successful
Test and build Docker image / docker (push) Successful in 1m40s

This commit is contained in:
Joakim Hellsén 2026-04-09 21:38:33 +02:00
commit c55610affa
Signed by: Joakim Hellsén
SSH key fingerprint: SHA256:/9h/CsExpFp+PRhsfA0xznFx2CGfTT5R/kpuFfUgEQk
11 changed files with 811 additions and 75 deletions

View file

@ -8,20 +8,33 @@ from unittest.mock import MagicMock
from unittest.mock import patch
import pytest
from reader import EntryNotFoundError
from reader import Feed
from reader import FeedNotFoundError
from reader import Reader
from reader import StorageError
from reader import make_reader
from discord_rss_bot import feeds
from discord_rss_bot.feeds import execute_webhook
from discord_rss_bot.feeds import extract_domain
from discord_rss_bot.feeds import get_webhook_url
from discord_rss_bot.feeds import is_youtube_feed
from discord_rss_bot.feeds import send_discord_quest_notification
from discord_rss_bot.feeds import send_entry_to_discord
from discord_rss_bot.feeds import send_to_discord
from discord_rss_bot.feeds import set_entry_as_read
from discord_rss_bot.feeds import should_send_embed_check
from discord_rss_bot.feeds import truncate_webhook_message
def test_send_to_discord() -> None:
"""Test sending to Discord."""
# Skip early if no webhook URL is configured to avoid a real network request.
webhook_url: str | None = os.environ.get("TEST_WEBHOOK_URL")
if not webhook_url:
pytest.skip("No webhook URL provided.")
with tempfile.TemporaryDirectory() as temp_dir:
# Create the temp directory.
Path.mkdir(Path(temp_dir), exist_ok=True)
@ -41,13 +54,6 @@ def test_send_to_discord() -> None:
feed: Feed = reader.get_feed("https://www.reddit.com/r/Python/.rss")
assert feed is not None, f"The feed should not be None. Got: {feed}"
# Get the webhook.
webhook_url: str | None = os.environ.get("TEST_WEBHOOK_URL")
if not webhook_url:
reader.close()
pytest.skip("No webhook URL provided.")
assert webhook_url is not None, f"The webhook URL should not be None. Got: {webhook_url}"
# Add tag to the feed and check if it is there.
@ -274,3 +280,154 @@ def test_extract_domain_special_characters() -> None:
)
def test_extract_domain(url: str, expected: str) -> None:
assert extract_domain(url) == expected
@patch("discord_rss_bot.feeds.execute_webhook")
def test_send_discord_quest_notification_text_match(mock_execute_webhook: MagicMock) -> None:
"""Send a quest link as a separate notification when plain text content contains one."""
entry = MagicMock()
entry.id = "entry-1"
entry.content = [MagicMock(type="text", value="Check this https://discord.com/quests/12345 now")]
reader = MagicMock()
send_discord_quest_notification(entry, "https://discord.com/api/webhooks/123/abc", reader)
mock_execute_webhook.assert_called_once()
webhook_sent = mock_execute_webhook.call_args[0][0]
assert webhook_sent.content == "https://discord.com/quests/12345"
@patch("discord_rss_bot.feeds.execute_webhook")
def test_send_discord_quest_notification_html_match(mock_execute_webhook: MagicMock) -> None:
"""Send a quest link when it is found inside HTML content."""
entry = MagicMock()
entry.id = "entry-2"
entry.content = [
MagicMock(
type="text/html",
value='<p>Click <a href="https://discord.com/quests/777">here</a></p>',
),
]
reader = MagicMock()
send_discord_quest_notification(entry, "https://discord.com/api/webhooks/123/abc", reader)
mock_execute_webhook.assert_called_once()
webhook_sent = mock_execute_webhook.call_args[0][0]
assert webhook_sent.content == "https://discord.com/quests/777"
@patch("discord_rss_bot.feeds.execute_webhook")
def test_send_discord_quest_notification_no_match(mock_execute_webhook: MagicMock) -> None:
"""Do nothing when no quest URL exists in entry content."""
entry = MagicMock()
entry.id = "entry-3"
entry.content = [MagicMock(type="text", value="No quest link here")]
reader = MagicMock()
send_discord_quest_notification(entry, "https://discord.com/api/webhooks/123/abc", reader)
mock_execute_webhook.assert_not_called()
def test_get_webhook_url_returns_value() -> None:
reader = MagicMock()
entry = MagicMock()
entry.feed_url = "https://example.com/feed.xml"
entry.feed.url = "https://example.com/feed.xml"
reader.get_tag.return_value = "https://discord.com/api/webhooks/123/abc"
result = get_webhook_url(reader, entry)
assert result == "https://discord.com/api/webhooks/123/abc"
def test_get_webhook_url_returns_empty_on_storage_error() -> None:
reader = MagicMock()
entry = MagicMock()
entry.feed_url = "https://example.com/feed.xml"
entry.feed.url = "https://example.com/feed.xml"
reader.get_tag.side_effect = StorageError("db error")
result = get_webhook_url(reader, entry)
assert not result
def test_set_entry_as_read_handles_entry_not_found_error() -> None:
reader = MagicMock()
entry = MagicMock(id="entry-4")
reader.set_entry_read.side_effect = EntryNotFoundError("https://example.com/feed.xml", "entry-4")
set_entry_as_read(reader, entry)
reader.set_entry_read.assert_called_once_with(entry, True)
def test_set_entry_as_read_handles_storage_error() -> None:
reader = MagicMock()
entry = MagicMock(id="entry-5")
reader.set_entry_read.side_effect = StorageError("db error")
set_entry_as_read(reader, entry)
reader.set_entry_read.assert_called_once_with(entry, True)
def test_execute_webhook_skips_when_feed_paused() -> None:
webhook = MagicMock()
reader = MagicMock()
entry = MagicMock()
entry.id = "entry-6"
entry.feed.url = "https://example.com/feed.xml"
entry.feed.updates_enabled = False
execute_webhook(webhook, entry, reader)
reader.get_feed.assert_not_called()
webhook.execute.assert_not_called()
def test_execute_webhook_skips_when_feed_missing() -> None:
webhook = MagicMock()
reader = MagicMock()
reader.get_feed.side_effect = FeedNotFoundError("missing")
entry = MagicMock()
entry.id = "entry-7"
entry.feed.url = "https://example.com/feed.xml"
entry.feed.updates_enabled = True
execute_webhook(webhook, entry, reader)
webhook.execute.assert_not_called()
@patch.object(feeds, "logger")
def test_execute_webhook_logs_error_on_bad_status(mock_logger: MagicMock) -> None:
webhook = MagicMock()
webhook.json = {"content": "test"}
webhook.execute.return_value = MagicMock(status_code=500, text="fail")
reader = MagicMock()
entry = MagicMock()
entry.id = "entry-8"
entry.feed.url = "https://example.com/feed.xml"
entry.feed.updates_enabled = True
execute_webhook(webhook, entry, reader)
mock_logger.error.assert_called_once()
@patch.object(feeds, "logger")
def test_execute_webhook_logs_info_on_success(mock_logger: MagicMock) -> None:
webhook = MagicMock()
webhook.execute.return_value = MagicMock(status_code=204, text="")
reader = MagicMock()
entry = MagicMock()
entry.id = "entry-9"
entry.feed.url = "https://example.com/feed.xml"
entry.feed.updates_enabled = True
execute_webhook(webhook, entry, reader)
mock_logger.info.assert_called_once_with("Sent entry to Discord: %s", "entry-9")