Compare commits

..

3 commits

Author SHA1 Message Date
ef00b4c020
Fix feed links
All checks were successful
Deploy to Server / deploy (push) Successful in 19s
2026-03-22 01:12:23 +01:00
867ddf7676
Update game detail template to improve box art alt text 2026-03-22 00:42:55 +01:00
8dfcd118e5
Fix pyright attribute access issues in test_sitemaps and fix import in views 2026-03-22 00:34:01 +01:00
9 changed files with 52 additions and 22 deletions

View file

@ -23,7 +23,7 @@ def test_sitemap_static_contains_expected_links(
youtube apps as well as some misc static files like /robots.txt.
"""
# Ensure deterministic BASE_URL
settings.BASE_URL = "https://ttvdrops.lovinator.space"
settings.BASE_URL = "https://ttvdrops.lovinator.space" # pyright: ignore[reportAttributeAccessIssue]
response = client.get(reverse("sitemap-static"))
assert response.status_code == 200
@ -31,7 +31,7 @@ def test_sitemap_static_contains_expected_links(
locs = _extract_locs(response.content)
base = settings.BASE_URL.rstrip("/")
base = settings.BASE_URL.rstrip("/") # pyright: ignore[reportAttributeAccessIssue]
expected_paths = [
reverse("core:dashboard"),

View file

@ -22,8 +22,8 @@ from django.http import Http404
from django.http import HttpRequest
from django.http import HttpResponse
from django.shortcuts import render
from django.shortcuts import reverse
from django.template.defaultfilters import filesizeformat
from django.urls import reverse
from django.utils import timezone
from kick.models import KickChannel

View file

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

View file

@ -24,7 +24,7 @@
<!-- Game image -->
<div style="margin-right: 16px;">
{% if game.box_art_best_url %}
{% picture game.box_art_best_url alt=game.name width=160 %}
{% picture game.box_art_best_url alt="Box art for "|add:game.get_game_name width=160 %}
{% endif %}
</div>
<!-- Game Title and Details -->

View file

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

View file

@ -5,7 +5,7 @@ from django.db import migrations
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."""
DropCampaign = apps.get_model("twitch", "DropCampaign")
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"])
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)."""
DropCampaign = apps.get_model("twitch", "DropCampaign")
for campaign in DropCampaign.objects.all():

View file

@ -1,7 +1,7 @@
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.
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 &amp;lt;t:UNIX_TIMESTAMP:R&amp;gt; in content, got: {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
def export_games_csv(request: HttpRequest) -> HttpResponse: # noqa: ARG001 # noqa: ARG001
def export_games_csv(request: HttpRequest) -> HttpResponse:
"""Export games to CSV format.
Args:
@ -1923,7 +1923,7 @@ def export_games_csv(request: HttpRequest) -> HttpResponse: # noqa: ARG001 # n
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.
Args:
@ -1958,7 +1958,7 @@ def export_games_json(request: HttpRequest) -> HttpResponse: # noqa: ARG001 #
return response
def export_organizations_csv(request: HttpRequest) -> HttpResponse: # noqa: ARG001
def export_organizations_csv(request: HttpRequest) -> HttpResponse:
"""Export organizations to CSV format.
Args:
@ -1987,7 +1987,7 @@ def export_organizations_csv(request: HttpRequest) -> HttpResponse: # noqa: ARG
return response
def export_organizations_json(request: HttpRequest) -> HttpResponse: # noqa: ARG001
def export_organizations_json(request: HttpRequest) -> HttpResponse:
"""Export organizations to JSON format.
Args: