Use Steam game names as feed display titles; custom.html are now partly rendered from Python
All checks were successful
Test and build Docker image / docker (push) Successful in 30s
All checks were successful
Test and build Docker image / docker (push) Successful in 30s
This commit is contained in:
parent
cd9201d4ef
commit
6c0aea053b
15 changed files with 976 additions and 615 deletions
|
|
@ -1,5 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
import re
|
||||
import typing
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock
|
||||
|
|
@ -16,6 +18,7 @@ from discord_rss_bot.custom_message import get_first_image
|
|||
from discord_rss_bot.custom_message import get_image_urls
|
||||
from discord_rss_bot.custom_message import normalize_message_avatar_url
|
||||
from discord_rss_bot.custom_message import normalize_message_username
|
||||
from discord_rss_bot.custom_message import render_tag_reference_html
|
||||
from discord_rss_bot.custom_message import replace_tags_in_embed
|
||||
from discord_rss_bot.custom_message import replace_tags_in_text_message
|
||||
from discord_rss_bot.custom_message import save_embed
|
||||
|
|
@ -225,6 +228,42 @@ def test_replace_tags_in_embed_uses_authors_str(mock_get_embed: MagicMock) -> No
|
|||
assert embed.description == "Feed Author One, Feed Author Two | Entry Author One, Entry Author Two"
|
||||
|
||||
|
||||
@patch("discord_rss_bot.custom_message.get_custom_message")
|
||||
def test_replace_tags_in_text_message_uses_steam_game_name_for_feed_title(
|
||||
mock_get_custom_message: MagicMock,
|
||||
) -> None:
|
||||
"""{{feed_title}} should render the cached Steam game name for Steam feeds."""
|
||||
mock_reader = MagicMock()
|
||||
mock_reader.get_tag.return_value = "Dota"
|
||||
mock_get_custom_message.return_value = "{{feed_title}}"
|
||||
entry_ns: SimpleNamespace = make_entry("<p>Summary</p>")
|
||||
entry_ns.feed.url = "https://store.steampowered.com/feeds/news/app/570/?cc=US&l=english"
|
||||
entry_ns.feed.title = "570 RSS Feed"
|
||||
|
||||
rendered: str = replace_tags_in_text_message(typing.cast("Entry", entry_ns), reader=mock_reader)
|
||||
|
||||
assert rendered == "Dota"
|
||||
|
||||
|
||||
@patch("discord_rss_bot.custom_message.get_embed")
|
||||
def test_replace_tags_in_embed_uses_steam_game_name_for_feed_title(mock_get_embed: MagicMock) -> None:
|
||||
"""{{feed_title}} in an embed should render the cached Steam game name."""
|
||||
mock_reader = MagicMock()
|
||||
mock_reader.get_tag.return_value = "Dota"
|
||||
mock_get_embed.return_value = CustomEmbed(description="{{feed_title}}")
|
||||
entry_ns: SimpleNamespace = make_entry("<p>Summary</p>")
|
||||
entry_ns.feed.url = "https://store.steampowered.com/feeds/news/app/570/?cc=US&l=english"
|
||||
entry_ns.feed.title = "570 RSS Feed"
|
||||
|
||||
embed: CustomEmbed = replace_tags_in_embed(
|
||||
entry_ns.feed,
|
||||
typing.cast("Entry", entry_ns),
|
||||
reader=mock_reader,
|
||||
)
|
||||
|
||||
assert embed.description == "Dota"
|
||||
|
||||
|
||||
def test_get_first_image_prefers_content_image_over_summary_image() -> None:
|
||||
summary = '<p><img src="https://example.com/from-summary.jpg" /></p>'
|
||||
content = '<p><img src="https://example.com/from-content.jpg" /></p>'
|
||||
|
|
@ -556,3 +595,123 @@ class TestNormalizeMessageAvatarUrl:
|
|||
|
||||
def test_invalid_url_returns_empty(self) -> None:
|
||||
assert not normalize_message_avatar_url("not-a-url")
|
||||
|
||||
|
||||
def test_render_tag_reference_html_renders_feed_tags() -> None:
|
||||
"""The reference list should show every feed tag with its preview value."""
|
||||
feed = typing.cast("Feed", make_feed())
|
||||
|
||||
output = render_tag_reference_html(
|
||||
feed=feed,
|
||||
entry=None,
|
||||
feed_title="Example Feed",
|
||||
first_image="",
|
||||
)
|
||||
|
||||
assert '<ul class="list-inline">' in output
|
||||
assert "{{feed_author}}</code> Entry Author" in output
|
||||
assert "{{feed_title}}</code> Example Feed" in output
|
||||
assert "{{feed_url}}</code> https://example.com/feed.xml" in output
|
||||
assert "{{ feed_author }}" not in output
|
||||
assert "{{entry_added}}" not in output
|
||||
|
||||
|
||||
def test_render_tag_reference_html_renders_entry_tags() -> None:
|
||||
"""The reference list should include entry tags, content, and image tags."""
|
||||
feed = typing.cast("Feed", make_feed())
|
||||
entry_ns = make_entry("<p>Summary text</p>")
|
||||
entry_ns.content = [SimpleNamespace(value="<p>Content text</p>")]
|
||||
entry = typing.cast("Entry", entry_ns)
|
||||
|
||||
output = render_tag_reference_html(
|
||||
feed=feed,
|
||||
entry=entry,
|
||||
feed_title="Example Feed",
|
||||
first_image="https://example.com/first.jpg",
|
||||
)
|
||||
|
||||
assert "{{entry_title}}</code> Entry Title" in output
|
||||
assert "{{entry_content}}</code> Content text" in output
|
||||
assert "{{entry_content_raw}}" in output
|
||||
assert "{{entry_summary}}</code> Summary text" in output
|
||||
assert "{{entry_summary_raw}}" in output
|
||||
assert "{{entry_text}}" in output
|
||||
assert "{{image_1}}</code> https://example.com/first.jpg" in output
|
||||
assert "{{ entry_title }}" not in output
|
||||
|
||||
|
||||
def test_render_tag_reference_html_no_entry_shows_message() -> None:
|
||||
"""Without an entry the list should show the fallback message instead."""
|
||||
feed = typing.cast("Feed", make_feed())
|
||||
|
||||
output = render_tag_reference_html(
|
||||
feed=feed,
|
||||
entry=None,
|
||||
feed_title="Example Feed",
|
||||
first_image="",
|
||||
)
|
||||
|
||||
assert "Something went wrong, there was no entry found" in output
|
||||
assert "{{entry_title}}" not in output
|
||||
|
||||
|
||||
def test_render_tag_reference_html_escapes_values() -> None:
|
||||
"""Values containing HTML should be escaped so they render as text."""
|
||||
feed_ns = make_feed()
|
||||
feed_ns.subtitle = "<b>bold & loud</b>"
|
||||
feed = typing.cast("Feed", feed_ns)
|
||||
|
||||
output = render_tag_reference_html(
|
||||
feed=feed,
|
||||
entry=None,
|
||||
feed_title="Example Feed",
|
||||
first_image="",
|
||||
)
|
||||
|
||||
assert "<b>bold & loud</b>" not in output
|
||||
assert "<b>bold & loud</b>" in output
|
||||
|
||||
|
||||
def test_render_tag_reference_html_shows_extension_variables() -> None:
|
||||
"""Enabled extension variables should be listed with their values."""
|
||||
feed = typing.cast("Feed", make_feed())
|
||||
entry = typing.cast("Entry", make_entry("<p>Summary text</p>"))
|
||||
|
||||
output = render_tag_reference_html(
|
||||
feed=feed,
|
||||
entry=entry,
|
||||
feed_title="Example Feed",
|
||||
first_image="",
|
||||
extension_variables=["wp_content", "wp_excerpt"],
|
||||
extension_values={"wp_content": "Full <p>post</p>", "wp_excerpt": ""},
|
||||
)
|
||||
|
||||
assert "Extension variables (from enabled extensions):" in output
|
||||
assert "{{wp_content}}</code> Full <p>post</p>" in output
|
||||
assert "{{wp_excerpt}}</code> (empty)" in output
|
||||
assert "{{ wp_content }}" not in output
|
||||
|
||||
|
||||
def test_render_tag_reference_html_lists_all_replaceable_tags() -> None:
|
||||
"""Every replaceable tag should be listed on the reference preview."""
|
||||
feed = typing.cast("Feed", make_feed())
|
||||
entry_ns = make_entry("<p>Summary text</p>")
|
||||
entry_ns.content = [SimpleNamespace(value="<p>Content text</p>")]
|
||||
entry = typing.cast("Entry", entry_ns)
|
||||
|
||||
output = render_tag_reference_html(
|
||||
feed=feed,
|
||||
entry=entry,
|
||||
feed_title="Example Feed",
|
||||
first_image="https://example.com/first.jpg",
|
||||
)
|
||||
|
||||
# Extract the literal {{...}} tags from both replacement tables so this
|
||||
# test automatically catches any tag that is replaceable but not shown.
|
||||
tag_pattern: re.Pattern[str] = re.compile(r'\{"\{\{([a-z_0-9]+)\}\}":')
|
||||
replaceable_tags: set[str] = set(tag_pattern.findall(inspect.getsource(replace_tags_in_text_message)))
|
||||
replaceable_tags.update(tag_pattern.findall(inspect.getsource(replace_tags_in_embed)))
|
||||
|
||||
assert replaceable_tags, "No replaceable tags were found to compare"
|
||||
for tag in sorted(replaceable_tags):
|
||||
assert f"{{{{{tag}}}}}" in output, f"Replaceable tag {{{{{tag}}}}} is missing from the reference list"
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@ from threading import Thread
|
|||
from types import SimpleNamespace
|
||||
from typing import TYPE_CHECKING
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from httpx2 import HTTPError
|
||||
from reader import Reader as ReaderType
|
||||
from reader import make_reader
|
||||
|
||||
|
|
@ -39,6 +41,11 @@ from discord_rss_bot.extensions.jwplayer_thumbnail import JWPlayerThumbnailExten
|
|||
from discord_rss_bot.extensions.steam import SteamExtension
|
||||
from discord_rss_bot.extensions.steam import fix_steam_image_url
|
||||
from discord_rss_bot.extensions.steam import fix_steam_image_urls_in_payload
|
||||
from discord_rss_bot.extensions.steam import get_cached_feed_display_title
|
||||
from discord_rss_bot.extensions.steam import get_cached_steam_game_name
|
||||
from discord_rss_bot.extensions.steam import get_feed_display_title
|
||||
from discord_rss_bot.extensions.steam import get_steam_game_name
|
||||
from discord_rss_bot.extensions.steam import set_steam_game_name
|
||||
from discord_rss_bot.extensions.storage import get_enabled_extensions_for_feed
|
||||
from discord_rss_bot.extensions.storage import set_enabled_extensions_for_feed
|
||||
from discord_rss_bot.extensions.wordpress import WordPressExtension
|
||||
|
|
@ -829,6 +836,175 @@ def test_steam_modify_webhook_skips_non_steam_feed() -> None:
|
|||
assert result.json["content"] == "https://clan.akamai.steamstatic.com/images/123/abc/english.png"
|
||||
|
||||
|
||||
def _steam_feed(url: str = "https://store.steampowered.com/feeds/news/app/570/") -> SimpleNamespace:
|
||||
"""Build a minimal Steam feed object.
|
||||
|
||||
Returns:
|
||||
A ``SimpleNamespace`` with a Steam feed URL and its raw title.
|
||||
"""
|
||||
return SimpleNamespace(url=url, title="570 RSS Feed")
|
||||
|
||||
|
||||
def _appdetails_response(app_id: str, name: str) -> MagicMock:
|
||||
"""Build a mock Steam Store API response carrying a game name.
|
||||
|
||||
Returns:
|
||||
A mock response whose ``json()`` returns an app-details payload.
|
||||
"""
|
||||
response = MagicMock()
|
||||
response.json.return_value = {app_id: {"success": True, "data": {"name": name}}}
|
||||
return response
|
||||
|
||||
|
||||
@patch("discord_rss_bot.extensions.steam.httpx2.get")
|
||||
def test_get_steam_game_name_fetches_from_api(mock_get: MagicMock) -> None:
|
||||
"""get_steam_game_name should return the name from the Steam Store API."""
|
||||
mock_get.return_value = _appdetails_response("570", "Dota")
|
||||
|
||||
result: str | None = get_steam_game_name("570")
|
||||
|
||||
assert result == "Dota"
|
||||
mock_get.assert_called_once()
|
||||
assert mock_get.call_args.kwargs["params"]["appids"] == "570"
|
||||
|
||||
|
||||
@patch("discord_rss_bot.extensions.steam.httpx2.get")
|
||||
def test_get_steam_game_name_returns_none_on_api_error(mock_get: MagicMock) -> None:
|
||||
"""get_steam_game_name should return None when the API call fails."""
|
||||
mock_get.side_effect = HTTPError("boom")
|
||||
|
||||
assert get_steam_game_name("570") is None
|
||||
|
||||
|
||||
@patch("discord_rss_bot.extensions.steam.httpx2.get")
|
||||
def test_get_steam_game_name_returns_none_on_missing_details(mock_get: MagicMock) -> None:
|
||||
"""get_steam_game_name should return None when the API lacks game details."""
|
||||
mock_get.return_value = _appdetails_response("570", "")
|
||||
mock_get.return_value.json.return_value = {"570": {"success": False, "data": {}}}
|
||||
|
||||
assert get_steam_game_name("570") is None
|
||||
|
||||
|
||||
def test_get_cached_steam_game_name_reads_reader_tag(mock_reader: MagicMock) -> None:
|
||||
"""get_cached_steam_game_name should read the cached name from the reader."""
|
||||
feed: SimpleNamespace = _steam_feed()
|
||||
assert get_cached_steam_game_name(mock_reader, feed) is None
|
||||
|
||||
set_steam_game_name(mock_reader, feed, "Dota")
|
||||
assert get_cached_steam_game_name(mock_reader, feed) == "Dota"
|
||||
|
||||
|
||||
def test_set_steam_game_name_stores_reader_tag(mock_reader: MagicMock) -> None:
|
||||
"""set_steam_game_name should persist the name in the reader DB."""
|
||||
feed: SimpleNamespace = _steam_feed()
|
||||
set_steam_game_name(mock_reader, feed, "Dota 2")
|
||||
|
||||
assert mock_reader.tags[feed.url]["steam_game_name"] == "Dota 2"
|
||||
|
||||
|
||||
@patch("discord_rss_bot.extensions.steam.httpx2.get")
|
||||
def test_get_feed_display_title_uses_cached_game_name(
|
||||
mock_get: MagicMock,
|
||||
mock_reader: MagicMock,
|
||||
) -> None:
|
||||
"""A cached game name should be used without any network request."""
|
||||
feed: SimpleNamespace = _steam_feed()
|
||||
set_steam_game_name(mock_reader, feed, "Dota")
|
||||
|
||||
result: str = get_feed_display_title(feed, mock_reader) # type: ignore[arg-type]
|
||||
|
||||
assert result == "Dota"
|
||||
mock_get.assert_not_called()
|
||||
|
||||
|
||||
@patch("discord_rss_bot.extensions.steam.httpx2.get")
|
||||
def test_get_feed_display_title_fetches_and_caches_once(mock_get: MagicMock) -> None:
|
||||
"""A missing game name should be fetched once, cached, then reused."""
|
||||
mock_get.return_value = _appdetails_response("570", "Dota")
|
||||
feed: SimpleNamespace = _steam_feed()
|
||||
reader: MagicMock = MagicMock()
|
||||
reader.tags = {} # type: ignore[valid-type]
|
||||
|
||||
def set_tag(feed_url: object, key: str, value: object) -> None:
|
||||
reader.tags.setdefault(str(feed_url), {})[key] = value
|
||||
|
||||
def get_tag(feed_url: object, key: str, default: object = None) -> object:
|
||||
return reader.tags.get(str(feed_url), {}).get(key, default)
|
||||
|
||||
reader.set_tag.side_effect = set_tag
|
||||
reader.get_tag.side_effect = get_tag
|
||||
|
||||
first: str = get_feed_display_title(feed, reader) # type: ignore[arg-type]
|
||||
second: str = get_feed_display_title(feed, reader) # type: ignore[arg-type]
|
||||
|
||||
assert first == "Dota"
|
||||
assert second == "Dota"
|
||||
mock_get.assert_called_once()
|
||||
|
||||
|
||||
@patch("discord_rss_bot.extensions.steam.httpx2.get")
|
||||
def test_get_feed_display_title_falls_back_to_feed_title_on_error(mock_get: MagicMock) -> None:
|
||||
"""When the API fails, the feed's own title should be returned."""
|
||||
mock_get.side_effect = HTTPError("boom")
|
||||
feed: SimpleNamespace = _steam_feed(url="https://store.steampowered.com/feeds/news/app/111111/")
|
||||
|
||||
result: str = get_feed_display_title(feed, MagicMock()) # type: ignore[arg-type]
|
||||
|
||||
assert result == "570 RSS Feed"
|
||||
|
||||
|
||||
@patch("discord_rss_bot.extensions.steam.httpx2.get")
|
||||
def test_get_feed_display_title_does_not_repeat_failed_fetch(mock_get: MagicMock) -> None:
|
||||
"""A failed lookup should not trigger a network request on every call."""
|
||||
mock_get.side_effect = HTTPError("boom")
|
||||
feed: SimpleNamespace = _steam_feed(url="https://store.steampowered.com/feeds/news/app/999999/")
|
||||
reader: MagicMock = MagicMock()
|
||||
reader.tags = {} # type: ignore[valid-type]
|
||||
|
||||
def set_tag(feed_url: object, key: str, value: object) -> None:
|
||||
reader.tags.setdefault(str(feed_url), {})[key] = value
|
||||
|
||||
def get_tag(feed_url: object, key: str, default: object = None) -> object:
|
||||
return reader.tags.get(str(feed_url), {}).get(key, default)
|
||||
|
||||
reader.set_tag.side_effect = set_tag
|
||||
reader.get_tag.side_effect = get_tag
|
||||
|
||||
first: str = get_feed_display_title(feed, reader) # type: ignore[arg-type]
|
||||
second: str = get_feed_display_title(feed, reader) # type: ignore[arg-type]
|
||||
|
||||
assert first == "570 RSS Feed"
|
||||
assert second == "570 RSS Feed"
|
||||
mock_get.assert_called_once()
|
||||
|
||||
|
||||
def test_get_feed_display_title_returns_title_for_non_steam_feed() -> None:
|
||||
"""Non-Steam feeds should be returned with their title unchanged."""
|
||||
feed: SimpleNamespace = SimpleNamespace(url="https://example.com/feed.xml", title="Example Feed")
|
||||
|
||||
result: str = get_feed_display_title(feed, MagicMock()) # type: ignore[arg-type]
|
||||
|
||||
assert result == "Example Feed"
|
||||
|
||||
|
||||
@patch("discord_rss_bot.extensions.steam.httpx2.get")
|
||||
def test_get_cached_feed_display_title_never_fetches(
|
||||
mock_get: MagicMock,
|
||||
mock_reader: MagicMock,
|
||||
) -> None:
|
||||
"""get_cached_feed_display_title should be read-only and never hit the network."""
|
||||
feed: SimpleNamespace = _steam_feed()
|
||||
|
||||
uncached: str = get_cached_feed_display_title(feed, mock_reader) # type: ignore[arg-type]
|
||||
assert uncached == "570 RSS Feed"
|
||||
mock_get.assert_not_called()
|
||||
|
||||
set_steam_game_name(mock_reader, feed, "Dota")
|
||||
cached: str = get_cached_feed_display_title(feed, mock_reader) # type: ignore[arg-type]
|
||||
assert cached == "Dota"
|
||||
mock_get.assert_not_called()
|
||||
|
||||
|
||||
def test_youtube_extension_has_auto_enable_patterns() -> None:
|
||||
"""The built-in YouTube extension should declare URL patterns."""
|
||||
assert len(YouTubeExtension.auto_enable_url_patterns) > 0
|
||||
|
|
|
|||
|
|
@ -330,6 +330,49 @@ def test_extract_steam_app_id_from_url(url: str, expected_app_id: str | None) ->
|
|||
assert extract_app_id(url) == expected_app_id
|
||||
|
||||
|
||||
def test_get_feed_display_name_uses_cached_steam_game_name() -> None:
|
||||
"""Steam feeds should display the cached game name when a reader is provided."""
|
||||
reader = MagicMock()
|
||||
reader.get_tag.return_value = "Dota"
|
||||
feed = MagicMock()
|
||||
feed.url = "https://store.steampowered.com/feeds/news/app/570/"
|
||||
feed.title = "570 RSS Feed"
|
||||
feed.authors_str = ""
|
||||
|
||||
result: str = feeds.get_feed_display_name(feed, reader)
|
||||
|
||||
assert result == "Dota"
|
||||
|
||||
|
||||
def test_get_feed_display_name_without_reader_returns_feed_title() -> None:
|
||||
"""Without a reader, Steam feeds should keep their raw title."""
|
||||
feed = MagicMock()
|
||||
feed.url = "https://store.steampowered.com/feeds/news/app/570/"
|
||||
feed.title = "570 RSS Feed"
|
||||
feed.authors_str = ""
|
||||
|
||||
assert feeds.get_feed_display_name(feed) == "570 RSS Feed"
|
||||
|
||||
|
||||
def test_apply_feed_webhook_identity_uses_steam_game_name_for_username_fallback() -> None:
|
||||
"""Webhook username fallback should use the Steam game name, not the raw title."""
|
||||
feed = MagicMock()
|
||||
feed.url = "https://store.steampowered.com/feeds/news/app/570/"
|
||||
feed.title = "570 RSS Feed"
|
||||
feed.authors_str = ""
|
||||
entry = MagicMock()
|
||||
entry.feed = feed
|
||||
reader = MagicMock()
|
||||
reader.get_tag.side_effect = lambda resource, key, default=None: ( # ruff:ignore[unused-lambda-argument]
|
||||
"Dota 2" if key == "steam_game_name" else default
|
||||
)
|
||||
|
||||
webhook = feeds.DiscordWebhook(url="https://discord.com/api/webhooks/123/abc")
|
||||
result: feeds.DiscordWebhook = feeds.apply_feed_webhook_identity(webhook, entry, reader)
|
||||
|
||||
assert result.username == "Dota 2"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("tag_value", "expected_limit"),
|
||||
[
|
||||
|
|
|
|||
|
|
@ -325,6 +325,7 @@ def test_feed_page_shows_steam_thumbnail_hint_for_steam_feeds() -> None:
|
|||
"media_gallery_image_limit": 0,
|
||||
"webhook_text_length_limit": 4000,
|
||||
"save_sent_webhooks": True,
|
||||
"steam_game_name": "Dota 2",
|
||||
}.get(key, default)
|
||||
|
||||
def get_entry_counts(self, **_kwargs: TestKwargValue) -> SimpleNamespace:
|
||||
|
|
@ -344,6 +345,47 @@ def test_feed_page_shows_steam_thumbnail_hint_for_steam_feeds() -> None:
|
|||
response: Response = client.get(url="/feed", params={"feed_url": stub.feed.url})
|
||||
|
||||
assert response.status_code == 200, f"/feed failed: {response.text}"
|
||||
assert "Dota 2" in response.text
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
def test_embed_page_shows_steam_game_name_for_feed_title() -> None:
|
||||
"""The /embed page's {{feed_title}} preview should show the cached game name."""
|
||||
|
||||
@dataclass(slots=True)
|
||||
class DummyFeed:
|
||||
url: str
|
||||
title: str
|
||||
|
||||
class StubReader:
|
||||
def __init__(self) -> None:
|
||||
self.feed = DummyFeed(
|
||||
url="https://store.steampowered.com/feeds/news/app/570/?cc=US&l=english",
|
||||
title="570 RSS Feed",
|
||||
)
|
||||
|
||||
def get_feed(self, feed_url: str) -> DummyFeed:
|
||||
assert feed_url == self.feed.url
|
||||
return self.feed
|
||||
|
||||
def get_tag(self, _resource: object, key: str, default: TestTagValue = None) -> TestTagValue:
|
||||
return {
|
||||
"embed": "",
|
||||
"steam_game_name": "Dota 2",
|
||||
}.get(key, default)
|
||||
|
||||
def get_entries(self, **_kwargs: TestKwargValue) -> list[Entry]:
|
||||
return []
|
||||
|
||||
stub = StubReader()
|
||||
app.dependency_overrides[get_reader_dependency] = lambda: stub
|
||||
|
||||
try:
|
||||
response: Response = client.get(url="/embed", params={"feed_url": stub.feed.url})
|
||||
|
||||
assert response.status_code == 200, f"/embed failed: {response.text}"
|
||||
assert "Dota 2" in response.text
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue