240 lines
8.5 KiB
Python
240 lines
8.5 KiB
Python
from datetime import timedelta
|
|
from typing import TYPE_CHECKING
|
|
|
|
from django.db import connection
|
|
from django.test import TestCase
|
|
from django.test.utils import CaptureQueriesContext
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from chzzk.models import ChzzkCampaign
|
|
|
|
if TYPE_CHECKING:
|
|
from datetime import datetime
|
|
|
|
from django.test.client import _MonkeyPatchedWSGIResponse
|
|
|
|
|
|
class ChzzkDashboardViewTests(TestCase):
|
|
"""Test cases for the dashboard view of the chzzk app."""
|
|
|
|
def test_dashboard_view_no_n_plus_one_on_rewards(self) -> None:
|
|
"""Test that the dashboard view does not trigger an N+1 query for rewards."""
|
|
now = timezone.now()
|
|
base_kwargs = {
|
|
"category_type": "game",
|
|
"category_id": "1",
|
|
"category_value": "TestGame",
|
|
"service_id": "chzzk",
|
|
"state": "ACTIVE",
|
|
"start_date": now - timedelta(days=1),
|
|
"end_date": now + timedelta(days=1),
|
|
"has_ios_based_reward": False,
|
|
"drops_campaign_not_started": False,
|
|
"source_api": "unit-test",
|
|
}
|
|
reward_kwargs = {
|
|
"reward_type": "ITEM",
|
|
"campaign_reward_type": "Standard",
|
|
"condition_type": "watch",
|
|
"ios_based_reward": False,
|
|
"code_remaining_count": 100,
|
|
}
|
|
|
|
campaign1 = ChzzkCampaign.objects.create(
|
|
campaign_no=9001,
|
|
title="C1",
|
|
**base_kwargs,
|
|
)
|
|
campaign1.rewards.create( # pyright: ignore[reportAttributeAccessIssue]
|
|
reward_no=901,
|
|
title="R1",
|
|
condition_for_minutes=10,
|
|
**reward_kwargs,
|
|
) # pyright: ignore[reportAttributeAccessIssue]
|
|
campaign2 = ChzzkCampaign.objects.create(
|
|
campaign_no=9002,
|
|
title="C2",
|
|
**base_kwargs,
|
|
)
|
|
campaign2.rewards.create( # pyright: ignore[reportAttributeAccessIssue]
|
|
reward_no=902,
|
|
title="R2",
|
|
condition_for_minutes=20,
|
|
**reward_kwargs,
|
|
) # pyright: ignore[reportAttributeAccessIssue]
|
|
|
|
with CaptureQueriesContext(connection) as one_campaign_ctx:
|
|
self.client.get(reverse("chzzk:dashboard"))
|
|
query_count_two = len(one_campaign_ctx)
|
|
|
|
campaign3 = ChzzkCampaign.objects.create(
|
|
campaign_no=9003,
|
|
title="C3",
|
|
**base_kwargs,
|
|
)
|
|
campaign3.rewards.create( # pyright: ignore[reportAttributeAccessIssue]
|
|
reward_no=903,
|
|
title="R3",
|
|
condition_for_minutes=30,
|
|
**reward_kwargs,
|
|
) # pyright: ignore[reportAttributeAccessIssue]
|
|
|
|
with CaptureQueriesContext(connection) as three_campaign_ctx:
|
|
self.client.get(reverse("chzzk:dashboard"))
|
|
query_count_three = len(three_campaign_ctx)
|
|
|
|
# With prefetch_related, adding more campaigns should not add extra queries per campaign.
|
|
assert query_count_two == query_count_three
|
|
|
|
def test_dashboard_view_excludes_testing_state_campaigns(self) -> None:
|
|
"""Test that the dashboard view excludes campaigns in the TESTING state."""
|
|
now: datetime = timezone.now()
|
|
|
|
ChzzkCampaign.objects.create(
|
|
campaign_no=1001,
|
|
title="Testing campaign",
|
|
description="Should be excluded",
|
|
category_type="game",
|
|
category_id="1",
|
|
category_value="TestGame",
|
|
service_id="chzzk",
|
|
state="TESTING",
|
|
start_date=now - timedelta(days=1),
|
|
end_date=now + timedelta(days=1),
|
|
has_ios_based_reward=False,
|
|
drops_campaign_not_started=False,
|
|
source_api="unit-test",
|
|
)
|
|
|
|
included: ChzzkCampaign = ChzzkCampaign.objects.create(
|
|
campaign_no=1002,
|
|
title="Active campaign",
|
|
description="Should be included",
|
|
category_type="game",
|
|
category_id="1",
|
|
category_value="TestGame",
|
|
service_id="chzzk",
|
|
state="ACTIVE",
|
|
start_date=now - timedelta(days=1),
|
|
end_date=now + timedelta(days=1),
|
|
has_ios_based_reward=False,
|
|
drops_campaign_not_started=False,
|
|
source_api="unit-test",
|
|
)
|
|
|
|
response: _MonkeyPatchedWSGIResponse = self.client.get(
|
|
reverse("chzzk:dashboard"),
|
|
)
|
|
assert response.status_code == 200
|
|
campaigns: list[ChzzkCampaign] = list(response.context["active_campaigns"])
|
|
|
|
assert included in campaigns
|
|
assert all(c.state != "TESTING" for c in campaigns)
|
|
|
|
def test_campaign_detail_view_renders_chzzk_campaign_and_rewards(self) -> None:
|
|
"""Test that the campaign detail view correctly renders the details of a chzzk campaign and its rewards."""
|
|
now: datetime = timezone.now()
|
|
|
|
campaign: ChzzkCampaign = ChzzkCampaign.objects.create(
|
|
campaign_no=2001,
|
|
title="Campaign Detail Test",
|
|
description="Detailed campaign description",
|
|
category_type="game",
|
|
category_id="1",
|
|
category_value="TestGame",
|
|
service_id="chzzk",
|
|
state="ACTIVE",
|
|
start_date=now - timedelta(days=1),
|
|
end_date=now + timedelta(days=1),
|
|
has_ios_based_reward=False,
|
|
drops_campaign_not_started=False,
|
|
source_api="unit-test",
|
|
)
|
|
|
|
campaign.rewards.create( # pyright: ignore[reportAttributeAccessIssue]
|
|
reward_no=10,
|
|
title="Reward A",
|
|
reward_type="ITEM",
|
|
campaign_reward_type="Standard",
|
|
condition_type="watch",
|
|
condition_for_minutes=15,
|
|
ios_based_reward=False,
|
|
code_remaining_count=100,
|
|
)
|
|
|
|
response: _MonkeyPatchedWSGIResponse = self.client.get(
|
|
reverse("chzzk:campaign_detail", args=[campaign.campaign_no]),
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
content: str = response.content.decode()
|
|
assert campaign.title in content
|
|
assert "Reward A" in content
|
|
assert "watch" in content
|
|
assert "100" in content
|
|
assert "No" in content # ios_based_reward=False
|
|
|
|
def test_campaign_feed_only_includes_campaigns_with_raw_json(self) -> None:
|
|
"""Test that the RSS feed only includes campaigns with raw JSON data."""
|
|
now: datetime = timezone.now()
|
|
|
|
excluded: ChzzkCampaign = ChzzkCampaign.objects.create(
|
|
campaign_no=3001,
|
|
title="No JSON campaign",
|
|
description="Excluded because no raw JSON",
|
|
category_type="game",
|
|
category_id="1",
|
|
category_value="TestGame",
|
|
service_id="chzzk",
|
|
state="ACTIVE",
|
|
start_date=now - timedelta(days=1),
|
|
end_date=now + timedelta(days=1),
|
|
has_ios_based_reward=False,
|
|
drops_campaign_not_started=False,
|
|
source_api="unit-test",
|
|
)
|
|
|
|
included_v1: ChzzkCampaign = ChzzkCampaign.objects.create(
|
|
campaign_no=3002,
|
|
title="JSON v1 campaign",
|
|
description="Included because raw_json_v1 is present",
|
|
category_type="game",
|
|
category_id="1",
|
|
category_value="TestGame",
|
|
service_id="chzzk",
|
|
state="ACTIVE",
|
|
start_date=now - timedelta(days=1),
|
|
end_date=now + timedelta(days=1),
|
|
has_ios_based_reward=False,
|
|
drops_campaign_not_started=False,
|
|
source_api="unit-test",
|
|
raw_json_v1={"foo": "bar"},
|
|
)
|
|
|
|
included_v2: ChzzkCampaign = ChzzkCampaign.objects.create(
|
|
campaign_no=3003,
|
|
title="JSON v2 campaign",
|
|
description="Included because raw_json_v2 is present",
|
|
category_type="game",
|
|
category_id="1",
|
|
category_value="TestGame",
|
|
service_id="chzzk",
|
|
state="ACTIVE",
|
|
start_date=now - timedelta(days=2),
|
|
end_date=now + timedelta(days=2),
|
|
has_ios_based_reward=False,
|
|
drops_campaign_not_started=False,
|
|
source_api="unit-test",
|
|
raw_json_v2={"foo": "bar"},
|
|
)
|
|
|
|
response: _MonkeyPatchedWSGIResponse = self.client.get(
|
|
reverse("chzzk:campaign_feed"),
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
content: str = response.content.decode()
|
|
assert included_v1.title in content
|
|
assert included_v2.title in content
|
|
assert excluded.title not in content
|