From 53765c3b274618380ed0aa0cfb36349ce5e8398d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Helle=C5=9Ben?= Date: Mon, 9 Mar 2026 19:57:13 +0100 Subject: [PATCH] Enhance backfill_image_dimensions command to update image metadata; add tests for edge cases --- .../commands/backfill_image_dimensions.py | 23 +++++++++++---- twitch/tests/test_feeds.py | 28 +++++++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/twitch/management/commands/backfill_image_dimensions.py b/twitch/management/commands/backfill_image_dimensions.py index fb62032..aa72903 100644 --- a/twitch/management/commands/backfill_image_dimensions.py +++ b/twitch/management/commands/backfill_image_dimensions.py @@ -14,17 +14,22 @@ class Command(BaseCommand): help = "Backfill image dimensions for existing cached images" - def handle(self, *args, **options) -> None: # noqa: ARG002 + def handle(self, *args, **options) -> None: # noqa: ARG002, PLR0915 """Execute the command.""" total_updated = 0 # Update Game box art self.stdout.write("Processing Game box_art_file...") for game in Game.objects.exclude(box_art_file=""): - if game.box_art_file and not game.box_art_width: + needs_update = ( + not game.box_art_size_bytes + or not game.box_art_mime_type + or not game.box_art_width + ) + if game.box_art_file and needs_update: try: - # Opening the file and saving triggers dimension calculation - game.box_art_file.open() + if not game.box_art_width: + game.box_art_file.open() # populate size and mime if available with contextlib.suppress(Exception): @@ -42,9 +47,15 @@ class Command(BaseCommand): # Update DropCampaign images self.stdout.write("Processing DropCampaign image_file...") for campaign in DropCampaign.objects.exclude(image_file=""): - if campaign.image_file and not campaign.image_width: + needs_update: bool = ( + not campaign.image_size_bytes + or not campaign.image_mime_type + or not campaign.image_width + ) + if campaign.image_file and needs_update: try: - campaign.image_file.open() + if not campaign.image_width: + campaign.image_file.open() with contextlib.suppress(Exception): campaign.image_size_bytes = campaign.image_file.size diff --git a/twitch/tests/test_feeds.py b/twitch/tests/test_feeds.py index 08005b6..110f388 100644 --- a/twitch/tests/test_feeds.py +++ b/twitch/tests/test_feeds.py @@ -244,6 +244,34 @@ class RSSFeedTestCase(TestCase): assert campaign2.image_mime_type == "image/jpeg" assert game2.box_art_size_bytes == len(b"hello") assert game2.box_art_mime_type == "image/png" + # run again; nothing should error and metadata should still be present + call_command("backfill_image_dimensions") + campaign2.refresh_from_db() + game2.refresh_from_db() + assert campaign2.image_size_bytes == len(b"world") + assert campaign2.image_mime_type == "image/jpeg" + assert game2.box_art_size_bytes == len(b"hello") + assert game2.box_art_mime_type == "image/png" + + # simulate a case where width is already set but mime/size empty; the + # command should still fill size/mime even if width gets cleared by the + # model on save (invalid image data may reset the dimensions). + game2.box_art_width = 999 + game2.box_art_size_bytes = None + game2.box_art_mime_type = "" + game2.save() + campaign2.image_width = 888 + campaign2.image_size_bytes = None + campaign2.image_mime_type = "" + campaign2.save() + + call_command("backfill_image_dimensions") + campaign2.refresh_from_db() + game2.refresh_from_db() + assert campaign2.image_size_bytes == len(b"world") + assert campaign2.image_mime_type == "image/jpeg" + assert game2.box_art_size_bytes == len(b"hello") + assert game2.box_art_mime_type == "image/png" QueryAsserter = Callable[..., AbstractContextManager[object]]