diff --git a/example.json b/example.json new file mode 100644 index 0000000..029e342 --- /dev/null +++ b/example.json @@ -0,0 +1,265 @@ +[ + { + "data": { + "user": { + "id": "17658559", + "dropCampaign": { + "id": "3b965979-ecd2-11f0-876e-0a58a9feac02", + "self": { + "isAccountConnected": true, + "__typename": "DropCampaignSelfEdge" + }, + "allow": { + "channels": null, + "isEnabled": false, + "__typename": "DropCampaignACL" + }, + "accountLinkURL": "https://link.smite2.com/", + "description": "Viewers will receive 50 Wandering Market Coins for each two hours spent viewing participating streams. Watch to earn 7 drops for a total of 350 Wandering Market Coins for the week!", + "detailsURL": "https://www.smite2.com/news/closed-alpha-twitch-drops/", + "endAt": "2026-01-17T10:58:59.999Z", + "eventBasedDrops": [], + "game": { + "id": "2094865572", + "slug": "smite-2", + "displayName": "SMITE 2", + "__typename": "Game" + }, + "imageURL": "https://static-cdn.jtvnw.net/twitch-quests-assets/CAMPAIGN/47db66e8-933c-484f-ab5a-30ba09093098.png", + "name": "Jan Drops Week 2", + "owner": { + "id": "51a157a0-674a-4863-b120-7bb6ee2466a8", + "name": "Hi-Rez Studios", + "__typename": "Organization" + }, + "startAt": "2026-01-10T11:00:00Z", + "status": "ACTIVE", + "timeBasedDrops": [ + { + "id": "933c8f91-ecd2-11f0-b3fd-0a58a9feac02", + "requiredSubs": 0, + "benefitEdges": [ + { + "benefit": { + "id": "ccb3fb7f-e59b-11ef-aef0-0a58a9feac02", + "createdAt": "2025-02-07T21:37:58.881Z", + "entitlementLimit": 1, + "game": { + "id": "2094865572", + "name": "SMITE 2", + "__typename": "Game" + }, + "imageAssetURL": "https://static-cdn.jtvnw.net/twitch-quests-assets/REWARD/903496ad-de97-41ff-ad97-12f099e20ea8.jpeg", + "isIosAvailable": false, + "name": "Market Coins Bundle 1", + "ownerOrganization": { + "id": "51a157a0-674a-4863-b120-7bb6ee2466a8", + "name": "Hi-Rez Studios", + "__typename": "Organization" + }, + "distributionType": "DIRECT_ENTITLEMENT", + "__typename": "DropBenefit" + }, + "entitlementLimit": 1, + "__typename": "DropBenefitEdge" + } + ], + "endAt": "2026-01-17T10:58:59.999Z", + "name": "Market Coins Bundle 1", + "preconditionDrops": null, + "requiredMinutesWatched": 120, + "startAt": "2026-01-10T11:00:00Z", + "__typename": "TimeBasedDrop" + }, + { + "id": "9909373d-ecd2-11f0-92b1-0a58a9feac02", + "requiredSubs": 0, + "benefitEdges": [ + { + "benefit": { + "id": "ccb3fb7f-e59b-11ef-aef0-0a58a9feac02", + "createdAt": "2025-02-07T21:37:58.881Z", + "entitlementLimit": 1, + "game": { + "id": "2094865572", + "name": "SMITE 2", + "__typename": "Game" + }, + "imageAssetURL": "https://static-cdn.jtvnw.net/twitch-quests-assets/REWARD/903496ad-de97-41ff-ad97-12f099e20ea8.jpeg", + "isIosAvailable": false, + "name": "Market Coins Bundle 1", + "ownerOrganization": { + "id": "51a157a0-674a-4863-b120-7bb6ee2466a8", + "name": "Hi-Rez Studios", + "__typename": "Organization" + }, + "distributionType": "DIRECT_ENTITLEMENT", + "__typename": "DropBenefit" + }, + "entitlementLimit": 1, + "__typename": "DropBenefitEdge" + } + ], + "endAt": "2026-01-17T10:58:59.999Z", + "name": "Market Coins Bundle 2", + "preconditionDrops": null, + "requiredMinutesWatched": 240, + "startAt": "2026-01-10T11:00:00Z", + "__typename": "TimeBasedDrop" + }, + { + "id": "a5289489-ecd2-11f0-b098-0a58a9feac02", + "requiredSubs": 0, + "benefitEdges": [ + { + "benefit": { + "id": "ccb3fb7f-e59b-11ef-aef0-0a58a9feac02", + "createdAt": "2025-02-07T21:37:58.881Z", + "entitlementLimit": 1, + "game": { + "id": "2094865572", + "name": "SMITE 2", + "__typename": "Game" + }, + "imageAssetURL": "https://static-cdn.jtvnw.net/twitch-quests-assets/REWARD/903496ad-de97-41ff-ad97-12f099e20ea8.jpeg", + "isIosAvailable": false, + "name": "Market Coins Bundle 1", + "ownerOrganization": { + "id": "51a157a0-674a-4863-b120-7bb6ee2466a8", + "name": "Hi-Rez Studios", + "__typename": "Organization" + }, + "distributionType": "DIRECT_ENTITLEMENT", + "__typename": "DropBenefit" + }, + "entitlementLimit": 1, + "__typename": "DropBenefitEdge" + } + ], + "endAt": "2026-01-17T10:58:59.999Z", + "name": "Market Coins Bundle 3", + "preconditionDrops": null, + "requiredMinutesWatched": 360, + "startAt": "2026-01-10T11:00:00Z", + "__typename": "TimeBasedDrop" + }, + { + "id": "ab5ea171-ecd2-11f0-9e33-0a58a9feac02", + "requiredSubs": 0, + "benefitEdges": [ + { + "benefit": { + "id": "ccb3fb7f-e59b-11ef-aef0-0a58a9feac02", + "createdAt": "2025-02-07T21:37:58.881Z", + "entitlementLimit": 1, + "game": { + "id": "2094865572", + "name": "SMITE 2", + "__typename": "Game" + }, + "imageAssetURL": "https://static-cdn.jtvnw.net/twitch-quests-assets/REWARD/903496ad-de97-41ff-ad97-12f099e20ea8.jpeg", + "isIosAvailable": false, + "name": "Market Coins Bundle 1", + "ownerOrganization": { + "id": "51a157a0-674a-4863-b120-7bb6ee2466a8", + "name": "Hi-Rez Studios", + "__typename": "Organization" + }, + "distributionType": "DIRECT_ENTITLEMENT", + "__typename": "DropBenefit" + }, + "entitlementLimit": 1, + "__typename": "DropBenefitEdge" + } + ], + "endAt": "2026-01-17T10:58:59.999Z", + "name": "Market Coins Bundle 4", + "preconditionDrops": null, + "requiredMinutesWatched": 480, + "startAt": "2026-01-10T11:00:00Z", + "__typename": "TimeBasedDrop" + }, + { + "id": "b19b7afb-ecd2-11f0-bbd3-0a58a9feac02", + "requiredSubs": 0, + "benefitEdges": [ + { + "benefit": { + "id": "ccb3fb7f-e59b-11ef-aef0-0a58a9feac02", + "createdAt": "2025-02-07T21:37:58.881Z", + "entitlementLimit": 1, + "game": { + "id": "2094865572", + "name": "SMITE 2", + "__typename": "Game" + }, + "imageAssetURL": "https://static-cdn.jtvnw.net/twitch-quests-assets/REWARD/903496ad-de97-41ff-ad97-12f099e20ea8.jpeg", + "isIosAvailable": false, + "name": "Market Coins Bundle 1", + "ownerOrganization": { + "id": "51a157a0-674a-4863-b120-7bb6ee2466a8", + "name": "Hi-Rez Studios", + "__typename": "Organization" + }, + "distributionType": "DIRECT_ENTITLEMENT", + "__typename": "DropBenefit" + }, + "entitlementLimit": 1, + "__typename": "DropBenefitEdge" + } + ], + "endAt": "2026-01-17T10:58:59.999Z", + "name": "Market Coins Bundle 5", + "preconditionDrops": null, + "requiredMinutesWatched": 600, + "startAt": "2026-01-10T11:00:00Z", + "__typename": "TimeBasedDrop" + }, + { + "id": "b82db8e0-ecd2-11f0-8c96-0a58a9feac02", + "requiredSubs": 0, + "benefitEdges": [ + { + "benefit": { + "id": "ccb3fb7f-e59b-11ef-aef0-0a58a9feac02", + "createdAt": "2025-02-07T21:37:58.881Z", + "entitlementLimit": 1, + "game": { + "id": "2094865572", + "name": "SMITE 2", + "__typename": "Game" + }, + "imageAssetURL": "https://static-cdn.jtvnw.net/twitch-quests-assets/REWARD/903496ad-de97-41ff-ad97-12f099e20ea8.jpeg", + "isIosAvailable": false, + "name": "Market Coins Bundle 1", + "ownerOrganization": { + "id": "51a157a0-674a-4863-b120-7bb6ee2466a8", + "name": "Hi-Rez Studios", + "__typename": "Organization" + }, + "distributionType": "DIRECT_ENTITLEMENT", + "__typename": "DropBenefit" + }, + "entitlementLimit": 1, + "__typename": "DropBenefitEdge" + } + ], + "endAt": "2026-01-17T10:58:59.999Z", + "name": "Market Coins Bundle 6", + "preconditionDrops": null, + "requiredMinutesWatched": 720, + "startAt": "2026-01-10T11:00:00Z", + "__typename": "TimeBasedDrop" + } + ], + "__typename": "DropCampaign" + }, + "__typename": "User" + } + }, + "extensions": { + "durationMilliseconds": 48, + "operationName": "DropCampaignDetails" + } + } +] diff --git a/twitch/management/commands/better_import_drops.py b/twitch/management/commands/better_import_drops.py index 5820de0..261505f 100644 --- a/twitch/management/commands/better_import_drops.py +++ b/twitch/management/commands/better_import_drops.py @@ -694,7 +694,7 @@ class Command(BaseCommand): ) # Process allowed channels from the campaign's ACL - if drop_campaign.allow and drop_campaign.allow.channels: + if drop_campaign.allow: self._process_allowed_channels( campaign_obj=campaign_obj, allow_schema=drop_campaign.allow, diff --git a/twitch/tests/test_better_import_drops.py b/twitch/tests/test_better_import_drops.py index 0be6036..2fb2ec2 100644 --- a/twitch/tests/test_better_import_drops.py +++ b/twitch/tests/test_better_import_drops.py @@ -1,5 +1,6 @@ from __future__ import annotations +import json from pathlib import Path from typing import TYPE_CHECKING @@ -7,15 +8,16 @@ from django.test import TestCase from twitch.management.commands.better_import_drops import Command from twitch.models import Channel +from twitch.models import DropBenefit from twitch.models import DropCampaign from twitch.models import Game +from twitch.models import Organization +from twitch.models import TimeBasedDrop from twitch.schemas import DropBenefitSchema if TYPE_CHECKING: from debug_toolbar.panels.templates.panel import QuerySet - from twitch.models import DropBenefit - class GetOrUpdateBenefitTests(TestCase): """Tests for the _get_or_update_benefit method in better_import_drops.Command.""" @@ -540,3 +542,68 @@ class GameImportTests(TestCase): game = Game.objects.get(twitch_id="497057") assert game.slug == "destiny-2" assert game.display_name == "Destiny 2" + + +class ExampleJsonImportTests(TestCase): + """Regression tests based on the real-world `example.json` payload.""" + + def test_imports_drop_campaign_details_and_persists_urls(self) -> None: + """Ensure `imageURL` and other URL-ish fields are persisted from DropCampaignDetails.""" + command = Command() + command.pre_fill_cache() + + repo_root: Path = Path(__file__).resolve().parents[2] + example_path: Path = repo_root / "example.json" + + responses = json.loads(example_path.read_text(encoding="utf-8")) + + success, broken_dir = command.process_responses( + responses=responses, + file_path=example_path, + options={}, + ) + + assert success is True + assert broken_dir is None + + campaign: DropCampaign = DropCampaign.objects.get(twitch_id="3b965979-ecd2-11f0-876e-0a58a9feac02") + + # Core campaign fields + assert campaign.name == "Jan Drops Week 2" + assert "Viewers will receive 50 Wandering Market Coins" in campaign.description + assert campaign.details_url == "https://www.smite2.com/news/closed-alpha-twitch-drops/" + assert campaign.account_link_url == "https://link.smite2.com/" + + # The regression: ensure imageURL makes it into DropCampaign.image_url + assert ( + campaign.image_url + == "https://static-cdn.jtvnw.net/twitch-quests-assets/CAMPAIGN/47db66e8-933c-484f-ab5a-30ba09093098.png" + ) + + # Allow ACL normalization + assert campaign.allow_is_enabled is False + assert campaign.allow_channels.count() == 0 + + # Operation name provenance + assert campaign.operation_name == "DropCampaignDetails" + + # Related game/org normalization + game: Game = Game.objects.get(twitch_id="2094865572") + assert game.display_name == "SMITE 2" + assert game.slug == "smite-2" + + org: Organization = Organization.objects.get(twitch_id="51a157a0-674a-4863-b120-7bb6ee2466a8") + assert org.name == "Hi-Rez Studios" + assert game.owners.filter(pk=org.pk).exists() + + # Drops + benefits + assert TimeBasedDrop.objects.filter(campaign=campaign).count() == 6 + first_drop: TimeBasedDrop = TimeBasedDrop.objects.get(twitch_id="933c8f91-ecd2-11f0-b3fd-0a58a9feac02") + assert first_drop.name == "Market Coins Bundle 1" + assert first_drop.required_minutes_watched == 120 + assert DropBenefit.objects.count() == 1 + benefit: DropBenefit = DropBenefit.objects.get(twitch_id="ccb3fb7f-e59b-11ef-aef0-0a58a9feac02") + assert ( + benefit.image_asset_url + == "https://static-cdn.jtvnw.net/twitch-quests-assets/REWARD/903496ad-de97-41ff-ad97-12f099e20ea8.jpeg" + )