Fix feed links
All checks were successful
Deploy to Server / deploy (push) Successful in 19s

This commit is contained in:
Joakim Hellsén 2026-03-22 01:12:23 +01:00
commit ef00b4c020
Signed by: Joakim Hellsén
SSH key fingerprint: SHA256:/9h/CsExpFp+PRhsfA0xznFx2CGfTT5R/kpuFfUgEQk
6 changed files with 48 additions and 18 deletions

View file

@ -5,14 +5,20 @@ description = "Get notified when a new drop is available on Twitch."
readme = "README.md" readme = "README.md"
requires-python = ">=3.14" requires-python = ">=3.14"
dependencies = [ dependencies = [
"celery[redis]",
"colorama", "colorama",
"dateparser", "dateparser",
"django-auto-prefetch", "django-auto-prefetch",
"django-celery-beat",
"django-celery-results",
"django-debug-toolbar", "django-debug-toolbar",
"django-silk", "django-silk",
"django", "django",
"flower",
"gunicorn", "gunicorn",
"hiredis",
"httpx", "httpx",
"index-now-for-python",
"json-repair", "json-repair",
"pillow", "pillow",
"platformdirs", "platformdirs",
@ -20,17 +26,11 @@ dependencies = [
"pydantic", "pydantic",
"pygments", "pygments",
"python-dotenv", "python-dotenv",
"redis",
"sentry-sdk", "sentry-sdk",
"setproctitle", "setproctitle",
"tqdm",
"index-now-for-python",
"sitemap-parser", "sitemap-parser",
"celery[redis]", "tqdm",
"django-celery-results",
"django-celery-beat",
"flower",
"hiredis",
"redis",
] ]
@ -41,9 +41,9 @@ dev = [
"hypothesis[django]", "hypothesis[django]",
"pytest-cov", "pytest-cov",
"pytest-django", "pytest-django",
"pytest-randomly",
"pytest-xdist[psutil]", "pytest-xdist[psutil]",
"pytest", "pytest",
"pytest-randomly",
] ]
[tool.pytest.ini_options] [tool.pytest.ini_options]

View file

@ -692,7 +692,7 @@ class OrganizationRSSFeed(TTVDropsBaseFeed):
feed_type = BrowserFriendlyRss201rev2Feed feed_type = BrowserFriendlyRss201rev2Feed
title: str = "TTVDrops Twitch Organizations" title: str = "TTVDrops Twitch Organizations"
link: str = "/organizations/" link: str = "/twitch/organizations/"
description: str = "Latest organizations on TTVDrops" description: str = "Latest organizations on TTVDrops"
_limit: int | None = None _limit: int | None = None
@ -921,7 +921,7 @@ class DropCampaignFeed(TTVDropsBaseFeed):
"""RSS feed for latest drop campaigns.""" """RSS feed for latest drop campaigns."""
title: str = "Twitch Drop Campaigns" title: str = "Twitch Drop Campaigns"
link: str = "/campaigns/" link: str = "/"
description: str = "Latest Twitch drop campaigns on TTVDrops" description: str = "Latest Twitch drop campaigns on TTVDrops"
item_guid_is_permalink = True item_guid_is_permalink = True

View file

@ -5,7 +5,7 @@ from django.db import migrations
from django.db import models from django.db import models
def migrate_operation_name_to_list(apps, schema_editor) -> None: # noqa: ANN001, ARG001 def migrate_operation_name_to_list(apps, schema_editor) -> None: # noqa: ANN001
"""Convert operation_name string values to operation_names list.""" """Convert operation_name string values to operation_names list."""
DropCampaign = apps.get_model("twitch", "DropCampaign") DropCampaign = apps.get_model("twitch", "DropCampaign")
for campaign in DropCampaign.objects.all(): for campaign in DropCampaign.objects.all():
@ -14,7 +14,7 @@ def migrate_operation_name_to_list(apps, schema_editor) -> None: # noqa: ANN001
campaign.save(update_fields=["operation_names"]) campaign.save(update_fields=["operation_names"])
def reverse_operation_names_to_string(apps, schema_editor) -> None: # noqa: ARG001, ANN001 def reverse_operation_names_to_string(apps, schema_editor) -> None: # noqa: ANN001
"""Convert operation_names list back to operation_name string (first item only).""" """Convert operation_names list back to operation_name string (first item only)."""
DropCampaign = apps.get_model("twitch", "DropCampaign") DropCampaign = apps.get_model("twitch", "DropCampaign")
for campaign in DropCampaign.objects.all(): for campaign in DropCampaign.objects.all():

View file

@ -1,7 +1,7 @@
from django.db import migrations from django.db import migrations
def mark_all_drops_fully_imported(apps, schema_editor) -> None: # noqa: ANN001, ARG001 def mark_all_drops_fully_imported(apps, schema_editor) -> None: # noqa: ANN001
"""Marks all existing DropCampaigns as fully imported. """Marks all existing DropCampaigns as fully imported.
This was needed to ensure that the Twitch API view only returns campaigns that are ready for display. This was needed to ensure that the Twitch API view only returns campaigns that are ready for display.

View file

@ -1410,3 +1410,33 @@ class DiscordFeedTestCase(TestCase):
f"Expected Discord timestamp format <t:UNIX_TIMESTAMP:R> in content, got: {content}" f"Expected Discord timestamp format <t:UNIX_TIMESTAMP:R> in content, got: {content}"
) )
assert "()" not in content assert "()" not in content
def test_feed_links_return_200(self) -> None:
"""Test that all links in the feeds return 200 OK."""
feed_urls: list[str] = [
reverse("core:organization_feed"),
reverse("core:game_feed"),
reverse("core:organization_feed_atom"),
]
for feed_url in feed_urls:
response: _MonkeyPatchedWSGIResponse = self.client.get(feed_url)
assert response.status_code == 200, f"Feed {feed_url} did not return 200."
# Extract all links from the feed content
content: str = response.content.decode("utf-8")
links: list[str] = re.findall(r'href=["\'](https?://.*?)["\']', content)
for link in links:
skip_links: list[str] = [
"rss_styles.xslt",
"twitch.tv",
]
if any(skip in link for skip in skip_links):
# Skip testing rss_styles.xslt, external Twitch links, and internal links that may not exist in test environment
continue
link_response: _MonkeyPatchedWSGIResponse = self.client.get(link)
msg: str = f"Link {link} in feed {feed_url} did not return 200, got {link_response.status_code}."
assert link_response.status_code == 200, msg

View file

@ -1883,7 +1883,7 @@ def export_campaigns_json(request: HttpRequest) -> HttpResponse:
return response return response
def export_games_csv(request: HttpRequest) -> HttpResponse: # noqa: ARG001 # noqa: ARG001 def export_games_csv(request: HttpRequest) -> HttpResponse:
"""Export games to CSV format. """Export games to CSV format.
Args: Args:
@ -1923,7 +1923,7 @@ def export_games_csv(request: HttpRequest) -> HttpResponse: # noqa: ARG001 # n
return response return response
def export_games_json(request: HttpRequest) -> HttpResponse: # noqa: ARG001 # noqa: ARG001 def export_games_json(request: HttpRequest) -> HttpResponse:
"""Export games to JSON format. """Export games to JSON format.
Args: Args:
@ -1958,7 +1958,7 @@ def export_games_json(request: HttpRequest) -> HttpResponse: # noqa: ARG001 #
return response return response
def export_organizations_csv(request: HttpRequest) -> HttpResponse: # noqa: ARG001 def export_organizations_csv(request: HttpRequest) -> HttpResponse:
"""Export organizations to CSV format. """Export organizations to CSV format.
Args: Args:
@ -1987,7 +1987,7 @@ def export_organizations_csv(request: HttpRequest) -> HttpResponse: # noqa: ARG
return response return response
def export_organizations_json(request: HttpRequest) -> HttpResponse: # noqa: ARG001 def export_organizations_json(request: HttpRequest) -> HttpResponse:
"""Export organizations to JSON format. """Export organizations to JSON format.
Args: Args: