Fix filter preview?
All checks were successful
Test and build Docker image / docker (push) Successful in 29s
All checks were successful
Test and build Docker image / docker (push) Successful in 29s
This commit is contained in:
parent
6a3bba5b69
commit
984ce298cd
3 changed files with 455 additions and 9 deletions
|
|
@ -715,9 +715,11 @@ def build_filter_preview_context(
|
|||
helper_text: str = "Saved whitelist rules still apply while previewing blacklist changes."
|
||||
|
||||
if filter_name == "blacklist":
|
||||
preview_blacklist_values = coerce_filter_values("blacklist", form_values)
|
||||
if form_values is not None:
|
||||
preview_blacklist_values = coerce_filter_values("blacklist", form_values)
|
||||
else:
|
||||
preview_whitelist_values = coerce_filter_values("whitelist", form_values)
|
||||
if form_values is not None:
|
||||
preview_whitelist_values = coerce_filter_values("whitelist", form_values)
|
||||
helper_text = "Saved blacklist rules still apply while previewing whitelist changes."
|
||||
|
||||
preview_entries: list[Entry] = list(reader.get_entries(feed=feed, limit=FILTER_PREVIEW_LIMIT))
|
||||
|
|
@ -761,17 +763,22 @@ def build_filter_preview_context(
|
|||
},
|
||||
)
|
||||
|
||||
preview_html: str = create_html_for_feed(
|
||||
reader=reader,
|
||||
entries=preview_entries,
|
||||
current_feed_url=feed.url,
|
||||
entry_decisions=preview_decisions,
|
||||
)
|
||||
preview_html = ""
|
||||
if sent_count:
|
||||
preview_html = create_html_for_feed(
|
||||
reader=reader,
|
||||
entries=[
|
||||
entry for entry in preview_entries if preview_decisions[get_entry_decision_key(entry)].should_send
|
||||
],
|
||||
current_feed_url=feed.url,
|
||||
entry_decisions=preview_decisions,
|
||||
)
|
||||
|
||||
return {
|
||||
"filter_name": filter_name,
|
||||
"filter_label": filter_name.title(),
|
||||
"preview_entries": preview_entries,
|
||||
"preview_rendered_count": sent_count,
|
||||
"preview_rows": preview_rows,
|
||||
"preview_html": preview_html,
|
||||
"preview_limit": FILTER_PREVIEW_LIMIT,
|
||||
|
|
|
|||
|
|
@ -79,7 +79,11 @@
|
|||
<div class="filter-preview__rendered">{{ preview_html|safe }}</div>
|
||||
{% else %}
|
||||
<div class="p-3 border border-dark rounded-0">
|
||||
<p class="text-muted mb-0">Rendered preview will appear here when entries are available.</p>
|
||||
{% if preview_entries %}
|
||||
<p class="text-muted mb-0">No entries would be sent with the current rules.</p>
|
||||
{% else %}
|
||||
<p class="text-muted mb-0">Rendered preview will appear here when entries are available.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -252,6 +252,172 @@ def test_whitelist_page_uses_live_preview_layout() -> None:
|
|||
assert "Whitelist Rules" in response.text
|
||||
|
||||
|
||||
def test_blacklist_page_initial_preview_uses_saved_blacklist_rules() -> None:
|
||||
@dataclass(slots=True)
|
||||
class DummyFeed:
|
||||
url: str
|
||||
title: str
|
||||
|
||||
@dataclass(slots=True)
|
||||
class DummyEntry:
|
||||
id: str
|
||||
feed: DummyFeed
|
||||
title: str
|
||||
summary: str
|
||||
author: str
|
||||
link: str
|
||||
published: datetime | None
|
||||
content: list[object] = field(default_factory=list)
|
||||
|
||||
class StubReader:
|
||||
def __init__(self) -> None:
|
||||
self.feed = DummyFeed(url="https://example.com/preview.xml", title="Preview Feed")
|
||||
self.entries: list[Entry] = [
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="blocked-only",
|
||||
feed=self.feed,
|
||||
title="Blocked update",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/blocked-only",
|
||||
published=datetime(2024, 1, 1, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="allowed-only",
|
||||
feed=self.feed,
|
||||
title="Allowed note",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/allowed-only",
|
||||
published=datetime(2024, 1, 2, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
def get_feed(self, _feed_url: str) -> DummyFeed:
|
||||
return self.feed
|
||||
|
||||
def get_entries(self, **_kwargs: object) -> list[Entry]:
|
||||
return self.entries
|
||||
|
||||
def get_tag(self, _resource: object, key: str, default: object = None) -> object:
|
||||
if key == "blacklist_title":
|
||||
return "blocked"
|
||||
return default
|
||||
|
||||
stub_reader = StubReader()
|
||||
app.dependency_overrides[get_reader_dependency] = lambda: stub_reader
|
||||
rendered_titles: list[str] = []
|
||||
|
||||
def fake_create_html_for_feed(*, entries: list[Entry], **_kwargs: object) -> str:
|
||||
entry_titles: list[str] = [entry.title or entry.id for entry in entries]
|
||||
rendered_titles.extend(entry_titles)
|
||||
return " | ".join(entry_titles)
|
||||
|
||||
try:
|
||||
with patch("discord_rss_bot.main.create_html_for_feed", side_effect=fake_create_html_for_feed):
|
||||
response: Response = client.get(url="/blacklist", params={"feed_url": stub_reader.feed.url})
|
||||
|
||||
assert response.status_code == 200, f"/blacklist failed: {response.text}"
|
||||
assert rendered_titles == ["Allowed note"]
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
def test_whitelist_page_initial_preview_uses_saved_whitelist_rules() -> None:
|
||||
@dataclass(slots=True)
|
||||
class DummyFeed:
|
||||
url: str
|
||||
title: str
|
||||
|
||||
@dataclass(slots=True)
|
||||
class DummyEntry:
|
||||
id: str
|
||||
feed: DummyFeed
|
||||
title: str
|
||||
summary: str
|
||||
author: str
|
||||
link: str
|
||||
published: datetime | None
|
||||
content: list[object] = field(default_factory=list)
|
||||
|
||||
class StubReader:
|
||||
def __init__(self) -> None:
|
||||
self.feed = DummyFeed(url="https://example.com/preview.xml", title="Preview Feed")
|
||||
self.entries: list[Entry] = [
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="blocked-only",
|
||||
feed=self.feed,
|
||||
title="Blocked update",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/blocked-only",
|
||||
published=datetime(2024, 1, 1, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="allowed-only",
|
||||
feed=self.feed,
|
||||
title="Allowed note",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/allowed-only",
|
||||
published=datetime(2024, 1, 2, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="blocked-and-allowed",
|
||||
feed=self.feed,
|
||||
title="Blocked allowed",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/blocked-and-allowed",
|
||||
published=datetime(2024, 1, 3, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
def get_feed(self, _feed_url: str) -> DummyFeed:
|
||||
return self.feed
|
||||
|
||||
def get_entries(self, **_kwargs: object) -> list[Entry]:
|
||||
return self.entries
|
||||
|
||||
def get_tag(self, _resource: object, key: str, default: object = None) -> object:
|
||||
if key == "whitelist_title":
|
||||
return "allowed"
|
||||
return default
|
||||
|
||||
stub_reader = StubReader()
|
||||
app.dependency_overrides[get_reader_dependency] = lambda: stub_reader
|
||||
rendered_titles: list[str] = []
|
||||
|
||||
def fake_create_html_for_feed(*, entries: list[Entry], **_kwargs: object) -> str:
|
||||
entry_titles: list[str] = [entry.title or entry.id for entry in entries]
|
||||
rendered_titles.extend(entry_titles)
|
||||
return " | ".join(entry_titles)
|
||||
|
||||
try:
|
||||
with patch("discord_rss_bot.main.create_html_for_feed", side_effect=fake_create_html_for_feed):
|
||||
response: Response = client.get(url="/whitelist", params={"feed_url": stub_reader.feed.url})
|
||||
|
||||
assert response.status_code == 200, f"/whitelist failed: {response.text}"
|
||||
assert rendered_titles == ["Allowed note", "Blocked allowed"]
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
def test_blacklist_preview_does_not_persist_unsaved_rules() -> None:
|
||||
reader: Reader = ensure_preview_feed_exists()
|
||||
reader.set_tag(feed_url, "blacklist_title", "saved-blacklist") # pyright: ignore[reportArgumentType]
|
||||
|
|
@ -294,6 +460,275 @@ def test_whitelist_preview_shows_precedence_over_blacklist() -> None:
|
|||
reader.delete_tag(feed_url, "blacklist_title")
|
||||
|
||||
|
||||
def test_whitelist_preview_rendered_entries_respect_saved_blacklist_rules() -> None:
|
||||
@dataclass(slots=True)
|
||||
class DummyFeed:
|
||||
url: str
|
||||
title: str
|
||||
|
||||
@dataclass(slots=True)
|
||||
class DummyEntry:
|
||||
id: str
|
||||
feed: DummyFeed
|
||||
title: str
|
||||
summary: str
|
||||
author: str
|
||||
link: str
|
||||
published: datetime | None
|
||||
content: list[object] = field(default_factory=list)
|
||||
|
||||
class StubReader:
|
||||
def __init__(self) -> None:
|
||||
self.feed = DummyFeed(url="https://example.com/preview.xml", title="Preview Feed")
|
||||
self.entries: list[Entry] = [
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="blocked-only",
|
||||
feed=self.feed,
|
||||
title="Blocked update",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/blocked-only",
|
||||
published=datetime(2024, 1, 1, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="allowed-only",
|
||||
feed=self.feed,
|
||||
title="Allowed note",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/allowed-only",
|
||||
published=datetime(2024, 1, 2, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="blocked-and-allowed",
|
||||
feed=self.feed,
|
||||
title="Blocked allowed",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/blocked-and-allowed",
|
||||
published=datetime(2024, 1, 3, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
def get_feed(self, _feed_url: str) -> DummyFeed:
|
||||
return self.feed
|
||||
|
||||
def get_entries(self, **_kwargs: object) -> list[Entry]:
|
||||
return self.entries
|
||||
|
||||
def get_tag(self, _resource: object, key: str, default: object = None) -> object:
|
||||
if key == "blacklist_title":
|
||||
return "blocked"
|
||||
return default
|
||||
|
||||
stub_reader = StubReader()
|
||||
app.dependency_overrides[get_reader_dependency] = lambda: stub_reader
|
||||
rendered_titles: list[str] = []
|
||||
|
||||
def fake_create_html_for_feed(*, entries: list[Entry], **_kwargs: object) -> str:
|
||||
entry_titles: list[str] = [entry.title or entry.id for entry in entries]
|
||||
rendered_titles.extend(entry_titles)
|
||||
return " | ".join(entry_titles)
|
||||
|
||||
try:
|
||||
with patch("discord_rss_bot.main.create_html_for_feed", side_effect=fake_create_html_for_feed):
|
||||
response: Response = client.get(
|
||||
url="/whitelist_preview",
|
||||
params={"feed_url": stub_reader.feed.url, "whitelist_title": "allowed"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200, f"/whitelist_preview failed: {response.text}"
|
||||
assert rendered_titles == ["Allowed note", "Blocked allowed"]
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
def test_blacklist_preview_rendered_entries_respect_saved_whitelist_rules() -> None:
|
||||
@dataclass(slots=True)
|
||||
class DummyFeed:
|
||||
url: str
|
||||
title: str
|
||||
|
||||
@dataclass(slots=True)
|
||||
class DummyEntry:
|
||||
id: str
|
||||
feed: DummyFeed
|
||||
title: str
|
||||
summary: str
|
||||
author: str
|
||||
link: str
|
||||
published: datetime | None
|
||||
content: list[object] = field(default_factory=list)
|
||||
|
||||
class StubReader:
|
||||
def __init__(self) -> None:
|
||||
self.feed = DummyFeed(url="https://example.com/preview.xml", title="Preview Feed")
|
||||
self.entries: list[Entry] = [
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="blocked-only",
|
||||
feed=self.feed,
|
||||
title="Blocked update",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/blocked-only",
|
||||
published=datetime(2024, 1, 1, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="allowed-only",
|
||||
feed=self.feed,
|
||||
title="Allowed note",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/allowed-only",
|
||||
published=datetime(2024, 1, 2, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="blocked-and-allowed",
|
||||
feed=self.feed,
|
||||
title="Blocked allowed",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/blocked-and-allowed",
|
||||
published=datetime(2024, 1, 3, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
def get_feed(self, _feed_url: str) -> DummyFeed:
|
||||
return self.feed
|
||||
|
||||
def get_entries(self, **_kwargs: object) -> list[Entry]:
|
||||
return self.entries
|
||||
|
||||
def get_tag(self, _resource: object, key: str, default: object = None) -> object:
|
||||
if key == "whitelist_title":
|
||||
return "allowed"
|
||||
return default
|
||||
|
||||
stub_reader = StubReader()
|
||||
app.dependency_overrides[get_reader_dependency] = lambda: stub_reader
|
||||
rendered_titles: list[str] = []
|
||||
|
||||
def fake_create_html_for_feed(*, entries: list[Entry], **_kwargs: object) -> str:
|
||||
entry_titles: list[str] = [entry.title or entry.id for entry in entries]
|
||||
rendered_titles.extend(entry_titles)
|
||||
return " | ".join(entry_titles)
|
||||
|
||||
try:
|
||||
with patch("discord_rss_bot.main.create_html_for_feed", side_effect=fake_create_html_for_feed):
|
||||
response: Response = client.get(
|
||||
url="/blacklist_preview",
|
||||
params={"feed_url": stub_reader.feed.url, "blacklist_title": "blocked"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200, f"/blacklist_preview failed: {response.text}"
|
||||
assert rendered_titles == ["Allowed note", "Blocked allowed"]
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
def test_blacklist_preview_shows_no_rendered_entries_message_when_all_entries_are_skipped() -> None:
|
||||
@dataclass(slots=True)
|
||||
class DummyFeed:
|
||||
url: str
|
||||
title: str
|
||||
|
||||
@dataclass(slots=True)
|
||||
class DummyEntry:
|
||||
id: str
|
||||
feed: DummyFeed
|
||||
title: str
|
||||
summary: str
|
||||
author: str
|
||||
link: str
|
||||
published: datetime | None
|
||||
content: list[object] = field(default_factory=list)
|
||||
|
||||
class StubReader:
|
||||
def __init__(self) -> None:
|
||||
self.feed = DummyFeed(url="https://example.com/preview.xml", title="Preview Feed")
|
||||
self.entries: list[Entry] = [
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="blocked-only",
|
||||
feed=self.feed,
|
||||
title="Blocked update",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/blocked-only",
|
||||
published=datetime(2024, 1, 1, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="allowed-only",
|
||||
feed=self.feed,
|
||||
title="Allowed note",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/allowed-only",
|
||||
published=datetime(2024, 1, 2, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
cast(
|
||||
"Entry",
|
||||
DummyEntry(
|
||||
id="blocked-and-allowed",
|
||||
feed=self.feed,
|
||||
title="Blocked allowed",
|
||||
summary="Summary",
|
||||
author="Author",
|
||||
link="https://example.com/blocked-and-allowed",
|
||||
published=datetime(2024, 1, 3, tzinfo=UTC),
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
def get_feed(self, _feed_url: str) -> DummyFeed:
|
||||
return self.feed
|
||||
|
||||
def get_entries(self, **_kwargs: object) -> list[Entry]:
|
||||
return self.entries
|
||||
|
||||
def get_tag(self, _resource: object, _key: str, default: object = None) -> object:
|
||||
return default
|
||||
|
||||
stub_reader = StubReader()
|
||||
app.dependency_overrides[get_reader_dependency] = lambda: stub_reader
|
||||
|
||||
try:
|
||||
with patch("discord_rss_bot.main.create_html_for_feed") as create_html_mock:
|
||||
response: Response = client.get(
|
||||
url="/blacklist_preview",
|
||||
params={"feed_url": stub_reader.feed.url, "blacklist_title": "blocked,allowed,note"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200, f"/blacklist_preview failed: {response.text}"
|
||||
create_html_mock.assert_not_called()
|
||||
assert "No entries would be sent with the current rules." in response.text
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
def test_blacklist_preview_uses_50_entry_limit() -> None:
|
||||
@dataclass(slots=True)
|
||||
class DummyContent:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue