fix: rename evolution/ to refractor/ endpoint and remove misplaced notifs module
All checks were successful
Ruff Lint / lint (pull_request) Successful in 14s
All checks were successful
Ruff Lint / lint (pull_request) Successful in 14s
- Change `evolution/evaluate-game/` API call to `refractor/evaluate-game/` in complete_game() hook (was calling the wrong endpoint path) - Update all test assertions in test_complete_game_hook.py to match the corrected endpoint path and update docstrings to "refractor" naming - Remove helpers/evolution_notifs.py and tests/test_evolution_notifications.py from this PR — they belong to PR #112 (WP-14 tier notifications). The notify_tier_completion stub in logic_gameplay.py remains as the WP-14 integration target. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2c57fbcdf5
commit
29f2a8683f
@ -4336,7 +4336,6 @@ async def complete_game(
|
|||||||
await roll_back(db_game["id"], plays=True, decisions=True)
|
await roll_back(db_game["id"], plays=True, decisions=True)
|
||||||
log_exception(e, msg="Unable to post decisions to API, rolling back")
|
log_exception(e, msg="Unable to post decisions to API, rolling back")
|
||||||
|
|
||||||
|
|
||||||
# Post game rewards (gauntlet and main team)
|
# Post game rewards (gauntlet and main team)
|
||||||
try:
|
try:
|
||||||
win_reward, loss_reward = await post_game_rewards(
|
win_reward, loss_reward = await post_game_rewards(
|
||||||
@ -4360,25 +4359,25 @@ async def complete_game(
|
|||||||
await roll_back(db_game["id"], plays=True, decisions=True)
|
await roll_back(db_game["id"], plays=True, decisions=True)
|
||||||
log_exception(e, msg="Error while posting game rewards")
|
log_exception(e, msg="Error while posting game rewards")
|
||||||
|
|
||||||
# Post-game evolution processing (non-blocking)
|
# Post-game refractor processing (non-blocking)
|
||||||
# WP-13: update season stats then evaluate evolution milestones for all
|
# WP-13: update season stats then evaluate refractor milestones for all
|
||||||
# participating players. Wrapped in try/except so any failure here is
|
# participating players. Wrapped in try/except so any failure here is
|
||||||
# non-fatal — the game is already saved and evolution will catch up on the
|
# non-fatal — the game is already saved and refractor will catch up on the
|
||||||
# next evaluate call.
|
# next evaluate call.
|
||||||
try:
|
try:
|
||||||
await db_post(f"season-stats/update-game/{db_game['id']}")
|
await db_post(f"season-stats/update-game/{db_game['id']}")
|
||||||
evo_result = await db_post(f"evolution/evaluate-game/{db_game['id']}")
|
evo_result = await db_post(f"refractor/evaluate-game/{db_game['id']}")
|
||||||
if evo_result and evo_result.get("tier_ups"):
|
if evo_result and evo_result.get("tier_ups"):
|
||||||
for tier_up in evo_result["tier_ups"]:
|
for tier_up in evo_result["tier_ups"]:
|
||||||
# WP-14 will implement full Discord notification; stub for now
|
# WP-14 will implement full Discord notification; stub for now
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Evolution tier-up for player {tier_up.get('player_id')}: "
|
f"Refractor tier-up for player {tier_up.get('player_id')}: "
|
||||||
f"{tier_up.get('old_tier')} -> {tier_up.get('new_tier')} "
|
f"{tier_up.get('old_tier')} -> {tier_up.get('new_tier')} "
|
||||||
f"(game {db_game['id']})"
|
f"(game {db_game['id']})"
|
||||||
)
|
)
|
||||||
await notify_tier_completion(interaction.channel, tier_up)
|
await notify_tier_completion(interaction.channel, tier_up)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Post-game evolution processing failed (non-fatal): {e}")
|
logger.warning(f"Post-game refractor processing failed (non-fatal): {e}")
|
||||||
|
|
||||||
session.delete(this_play)
|
session.delete(this_play)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|||||||
@ -1,107 +0,0 @@
|
|||||||
"""
|
|
||||||
Evolution Tier Completion Notifications
|
|
||||||
|
|
||||||
Builds and sends Discord embeds when a player completes an evolution tier
|
|
||||||
during post-game evaluation. Each tier-up event gets its own embed.
|
|
||||||
|
|
||||||
Notification failures are non-fatal: the send is wrapped in try/except so
|
|
||||||
a Discord API hiccup never disrupts game flow.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
import discord
|
|
||||||
|
|
||||||
logger = logging.getLogger("discord_app")
|
|
||||||
|
|
||||||
# Human-readable display names for each tier number.
|
|
||||||
TIER_NAMES = {
|
|
||||||
0: "Unranked",
|
|
||||||
1: "Initiate",
|
|
||||||
2: "Rising",
|
|
||||||
3: "Ascendant",
|
|
||||||
4: "Evolved",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Tier-specific embed colors.
|
|
||||||
TIER_COLORS = {
|
|
||||||
1: 0x2ECC71, # green
|
|
||||||
2: 0xF1C40F, # gold
|
|
||||||
3: 0x9B59B6, # purple
|
|
||||||
4: 0x1ABC9C, # teal (fully evolved)
|
|
||||||
}
|
|
||||||
|
|
||||||
FOOTER_TEXT = "Paper Dynasty Evolution"
|
|
||||||
|
|
||||||
|
|
||||||
def build_tier_up_embed(tier_up: dict) -> discord.Embed:
|
|
||||||
"""Build a Discord embed for a tier-up event.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
tier_up:
|
|
||||||
Dict with keys: player_name, old_tier, new_tier, current_value, track_name.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
discord.Embed
|
|
||||||
A fully configured embed ready to send to a channel.
|
|
||||||
"""
|
|
||||||
player_name: str = tier_up["player_name"]
|
|
||||||
new_tier: int = tier_up["new_tier"]
|
|
||||||
track_name: str = tier_up["track_name"]
|
|
||||||
|
|
||||||
tier_name = TIER_NAMES.get(new_tier, f"Tier {new_tier}")
|
|
||||||
color = TIER_COLORS.get(new_tier, 0x2ECC71)
|
|
||||||
|
|
||||||
if new_tier >= 4:
|
|
||||||
# Fully evolved — special title and description.
|
|
||||||
embed = discord.Embed(
|
|
||||||
title="FULLY EVOLVED!",
|
|
||||||
description=(
|
|
||||||
f"**{player_name}** has reached maximum evolution on the **{track_name}** track"
|
|
||||||
),
|
|
||||||
color=color,
|
|
||||||
)
|
|
||||||
embed.add_field(
|
|
||||||
name="Rating Boosts",
|
|
||||||
value="Rating boosts coming in a future update!",
|
|
||||||
inline=False,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
embed = discord.Embed(
|
|
||||||
title="Evolution Tier Up!",
|
|
||||||
description=(
|
|
||||||
f"**{player_name}** reached **Tier {new_tier} ({tier_name})** on the **{track_name}** track"
|
|
||||||
),
|
|
||||||
color=color,
|
|
||||||
)
|
|
||||||
|
|
||||||
embed.set_footer(text=FOOTER_TEXT)
|
|
||||||
return embed
|
|
||||||
|
|
||||||
|
|
||||||
async def notify_tier_completion(channel, tier_up: dict) -> None:
|
|
||||||
"""Send a tier-up notification embed to the given channel.
|
|
||||||
|
|
||||||
Non-fatal: any exception during send is caught and logged so that a
|
|
||||||
Discord API failure never interrupts game evaluation.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
channel:
|
|
||||||
A discord.TextChannel (or any object with an async ``send`` method).
|
|
||||||
tier_up:
|
|
||||||
Dict with keys: player_name, old_tier, new_tier, current_value, track_name.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
embed = build_tier_up_embed(tier_up)
|
|
||||||
await channel.send(embed=embed)
|
|
||||||
except Exception as exc:
|
|
||||||
logger.error(
|
|
||||||
"Failed to send tier-up notification for %s (tier %s): %s",
|
|
||||||
tier_up.get("player_name", "unknown"),
|
|
||||||
tier_up.get("new_tier"),
|
|
||||||
exc,
|
|
||||||
)
|
|
||||||
@ -4,13 +4,13 @@ Tests for the WP-13 post-game callback integration hook.
|
|||||||
These tests verify that after a game is saved to the API, two additional
|
These tests verify that after a game is saved to the API, two additional
|
||||||
POST requests are fired in the correct order:
|
POST requests are fired in the correct order:
|
||||||
1. POST season-stats/update-game/{game_id} — update player_season_stats
|
1. POST season-stats/update-game/{game_id} — update player_season_stats
|
||||||
2. POST evolution/evaluate-game/{game_id} — evaluate evolution milestones
|
2. POST refractor/evaluate-game/{game_id} — evaluate refractor milestones
|
||||||
|
|
||||||
Key design constraints being tested:
|
Key design constraints being tested:
|
||||||
- Season stats MUST be updated before evolution is evaluated (ordering).
|
- Season stats MUST be updated before refractor is evaluated (ordering).
|
||||||
- Failure of either evolution call must NOT propagate — the game result has
|
- Failure of either refractor call must NOT propagate — the game result has
|
||||||
already been committed; evolution will self-heal on the next evaluate pass.
|
already been committed; refractor will self-heal on the next evaluate pass.
|
||||||
- Tier-up dicts returned by the evolution endpoint are passed to
|
- Tier-up dicts returned by the refractor endpoint are passed to
|
||||||
notify_tier_completion so WP-14 can present them to the player.
|
notify_tier_completion so WP-14 can present them to the player.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ async def _run_hook(db_post_mock, db_game_id: int = 42):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
await db_post_mock(f"season-stats/update-game/{db_game['id']}")
|
await db_post_mock(f"season-stats/update-game/{db_game['id']}")
|
||||||
evo_result = await db_post_mock(f"evolution/evaluate-game/{db_game['id']}")
|
evo_result = await db_post_mock(f"refractor/evaluate-game/{db_game['id']}")
|
||||||
if evo_result and evo_result.get("tier_ups"):
|
if evo_result and evo_result.get("tier_ups"):
|
||||||
for tier_up in evo_result["tier_ups"]:
|
for tier_up in evo_result["tier_ups"]:
|
||||||
await notify_tier_completion(channel, tier_up)
|
await notify_tier_completion(channel, tier_up)
|
||||||
@ -64,10 +64,10 @@ async def _run_hook(db_post_mock, db_game_id: int = 42):
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_hook_posts_to_both_endpoints_in_order():
|
async def test_hook_posts_to_both_endpoints_in_order():
|
||||||
"""
|
"""
|
||||||
Both evolution endpoints are called, and season-stats comes first.
|
Both refractor endpoints are called, and season-stats comes first.
|
||||||
|
|
||||||
The ordering is critical: player_season_stats must be populated before the
|
The ordering is critical: player_season_stats must be populated before the
|
||||||
evolution engine tries to read them for milestone evaluation.
|
refractor engine tries to read them for milestone evaluation.
|
||||||
"""
|
"""
|
||||||
db_post_mock = AsyncMock(return_value={})
|
db_post_mock = AsyncMock(return_value={})
|
||||||
|
|
||||||
@ -77,8 +77,8 @@ async def test_hook_posts_to_both_endpoints_in_order():
|
|||||||
calls = db_post_mock.call_args_list
|
calls = db_post_mock.call_args_list
|
||||||
# First call must be season-stats
|
# First call must be season-stats
|
||||||
assert calls[0] == call("season-stats/update-game/42")
|
assert calls[0] == call("season-stats/update-game/42")
|
||||||
# Second call must be evolution evaluate
|
# Second call must be refractor evaluate
|
||||||
assert calls[1] == call("evolution/evaluate-game/42")
|
assert calls[1] == call("refractor/evaluate-game/42")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@ -86,11 +86,11 @@ async def test_hook_is_nonfatal_when_db_post_raises():
|
|||||||
"""
|
"""
|
||||||
A failure inside the hook must not raise to the caller.
|
A failure inside the hook must not raise to the caller.
|
||||||
|
|
||||||
The game result is already persisted when the hook runs. If the evolution
|
The game result is already persisted when the hook runs. If the refractor
|
||||||
API is down or returns an error, we log a warning and continue — the game
|
API is down or returns an error, we log a warning and continue — the game
|
||||||
completion flow must not be interrupted.
|
completion flow must not be interrupted.
|
||||||
"""
|
"""
|
||||||
db_post_mock = AsyncMock(side_effect=Exception("evolution API unavailable"))
|
db_post_mock = AsyncMock(side_effect=Exception("refractor API unavailable"))
|
||||||
|
|
||||||
# Should not raise
|
# Should not raise
|
||||||
try:
|
try:
|
||||||
@ -102,7 +102,7 @@ async def test_hook_is_nonfatal_when_db_post_raises():
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_hook_processes_tier_ups_from_evo_result():
|
async def test_hook_processes_tier_ups_from_evo_result():
|
||||||
"""
|
"""
|
||||||
When the evolution endpoint returns tier_ups, each entry is forwarded to
|
When the refractor endpoint returns tier_ups, each entry is forwarded to
|
||||||
notify_tier_completion.
|
notify_tier_completion.
|
||||||
|
|
||||||
This confirms the data path between the API response and the WP-14
|
This confirms the data path between the API response and the WP-14
|
||||||
@ -114,7 +114,7 @@ async def test_hook_processes_tier_ups_from_evo_result():
|
|||||||
]
|
]
|
||||||
|
|
||||||
async def fake_db_post(endpoint):
|
async def fake_db_post(endpoint):
|
||||||
if "evolution" in endpoint:
|
if "refractor" in endpoint:
|
||||||
return {"tier_ups": tier_ups}
|
return {"tier_ups": tier_ups}
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ async def test_hook_processes_tier_ups_from_evo_result():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
await db_post_mock(f"season-stats/update-game/{db_game['id']}")
|
await db_post_mock(f"season-stats/update-game/{db_game['id']}")
|
||||||
evo_result = await db_post_mock(f"evolution/evaluate-game/{db_game['id']}")
|
evo_result = await db_post_mock(f"refractor/evaluate-game/{db_game['id']}")
|
||||||
if evo_result and evo_result.get("tier_ups"):
|
if evo_result and evo_result.get("tier_ups"):
|
||||||
for tier_up in evo_result["tier_ups"]:
|
for tier_up in evo_result["tier_ups"]:
|
||||||
await mock_notify(channel, tier_up)
|
await mock_notify(channel, tier_up)
|
||||||
@ -146,14 +146,14 @@ async def test_hook_processes_tier_ups_from_evo_result():
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_hook_no_tier_ups_does_not_call_notify():
|
async def test_hook_no_tier_ups_does_not_call_notify():
|
||||||
"""
|
"""
|
||||||
When the evolution response has no tier_ups (empty list or missing key),
|
When the refractor response has no tier_ups (empty list or missing key),
|
||||||
notify_tier_completion is never called.
|
notify_tier_completion is never called.
|
||||||
|
|
||||||
Avoids spurious Discord messages for routine game completions.
|
Avoids spurious Discord messages for routine game completions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def fake_db_post(endpoint):
|
async def fake_db_post(endpoint):
|
||||||
if "evolution" in endpoint:
|
if "refractor" in endpoint:
|
||||||
return {"tier_ups": []}
|
return {"tier_ups": []}
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ async def test_hook_no_tier_ups_does_not_call_notify():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
await db_post_mock(f"season-stats/update-game/{db_game['id']}")
|
await db_post_mock(f"season-stats/update-game/{db_game['id']}")
|
||||||
evo_result = await db_post_mock(f"evolution/evaluate-game/{db_game['id']}")
|
evo_result = await db_post_mock(f"refractor/evaluate-game/{db_game['id']}")
|
||||||
if evo_result and evo_result.get("tier_ups"):
|
if evo_result and evo_result.get("tier_ups"):
|
||||||
for tier_up in evo_result["tier_ups"]:
|
for tier_up in evo_result["tier_ups"]:
|
||||||
await mock_notify(channel, tier_up)
|
await mock_notify(channel, tier_up)
|
||||||
|
|||||||
@ -1,154 +0,0 @@
|
|||||||
"""
|
|
||||||
Tests for evolution tier completion notification embeds (WP-14).
|
|
||||||
|
|
||||||
These are pure unit tests — no database or Discord bot connection required.
|
|
||||||
Each test constructs embeds and asserts on title, description, color, and
|
|
||||||
footer to verify the notification design spec is met.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import discord
|
|
||||||
|
|
||||||
from utilities.evolution_notifications import (
|
|
||||||
TIER_COLORS,
|
|
||||||
build_tier_embeds,
|
|
||||||
tier_up_embed,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestTierUpEmbed:
|
|
||||||
"""Unit tests for tier_up_embed() — standard (T1–T3) and fully-evolved (T4) paths."""
|
|
||||||
|
|
||||||
def test_tier_up_title(self):
|
|
||||||
"""Standard tier-up embeds must use the 'Evolution Tier Up!' title."""
|
|
||||||
embed = tier_up_embed(
|
|
||||||
"Mike Trout", tier=2, tier_name="Rising", track_name="Batter"
|
|
||||||
)
|
|
||||||
assert embed.title == "Evolution Tier Up!"
|
|
||||||
|
|
||||||
def test_tier_up_description_format(self):
|
|
||||||
"""Description must include player name, tier number, tier name, and track name."""
|
|
||||||
embed = tier_up_embed(
|
|
||||||
"Mike Trout", tier=2, tier_name="Rising", track_name="Batter"
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
embed.description
|
|
||||||
== "Mike Trout reached Tier 2 (Rising) on the Batter track"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_tier_up_color_matches_tier(self):
|
|
||||||
"""Each tier must map to its specified embed color."""
|
|
||||||
for tier, expected_color in TIER_COLORS.items():
|
|
||||||
if tier == 4:
|
|
||||||
continue # T4 handled in fully-evolved tests
|
|
||||||
embed = tier_up_embed(
|
|
||||||
"Test Player", tier=tier, tier_name="Name", track_name="Batter"
|
|
||||||
)
|
|
||||||
assert embed.color.value == expected_color, f"Tier {tier} color mismatch"
|
|
||||||
|
|
||||||
def test_tier_up_no_footer_for_standard_tiers(self):
|
|
||||||
"""Standard tier-up embeds (T1–T3) must not have a footer."""
|
|
||||||
for tier in (1, 2, 3):
|
|
||||||
embed = tier_up_embed(
|
|
||||||
"Test Player", tier=tier, tier_name="Name", track_name="Batter"
|
|
||||||
)
|
|
||||||
assert embed.footer.text is None
|
|
||||||
|
|
||||||
|
|
||||||
class TestFullyEvolvedEmbed:
|
|
||||||
"""Unit tests for the fully-evolved (T4) embed — distinct title, description, and footer."""
|
|
||||||
|
|
||||||
def test_fully_evolved_title(self):
|
|
||||||
"""T4 embeds must use the 'FULLY EVOLVED!' title."""
|
|
||||||
embed = tier_up_embed(
|
|
||||||
"Mike Trout", tier=4, tier_name="Legendary", track_name="Batter"
|
|
||||||
)
|
|
||||||
assert embed.title == "FULLY EVOLVED!"
|
|
||||||
|
|
||||||
def test_fully_evolved_description(self):
|
|
||||||
"""T4 description must indicate maximum evolution without mentioning tier number."""
|
|
||||||
embed = tier_up_embed(
|
|
||||||
"Mike Trout", tier=4, tier_name="Legendary", track_name="Batter"
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
embed.description
|
|
||||||
== "Mike Trout has reached maximum evolution on the Batter track"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_fully_evolved_footer(self):
|
|
||||||
"""T4 embeds must include the Phase 2 teaser footer."""
|
|
||||||
embed = tier_up_embed(
|
|
||||||
"Mike Trout", tier=4, tier_name="Legendary", track_name="Batter"
|
|
||||||
)
|
|
||||||
assert embed.footer.text == "Rating boosts coming in a future update!"
|
|
||||||
|
|
||||||
def test_fully_evolved_color(self):
|
|
||||||
"""T4 embed color must be teal."""
|
|
||||||
embed = tier_up_embed(
|
|
||||||
"Mike Trout", tier=4, tier_name="Legendary", track_name="Batter"
|
|
||||||
)
|
|
||||||
assert embed.color.value == TIER_COLORS[4]
|
|
||||||
|
|
||||||
|
|
||||||
class TestBuildTierEmbeds:
|
|
||||||
"""Unit tests for build_tier_embeds() — list construction and edge cases."""
|
|
||||||
|
|
||||||
def test_no_tier_ups_returns_empty_list(self):
|
|
||||||
"""When no tier-ups occurred, build_tier_embeds must return an empty list."""
|
|
||||||
result = build_tier_embeds([])
|
|
||||||
assert result == []
|
|
||||||
|
|
||||||
def test_single_tier_up_returns_one_embed(self):
|
|
||||||
"""A single tier-up event must produce exactly one embed."""
|
|
||||||
tier_ups = [
|
|
||||||
{
|
|
||||||
"player_name": "Mike Trout",
|
|
||||||
"tier": 2,
|
|
||||||
"tier_name": "Rising",
|
|
||||||
"track_name": "Batter",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
result = build_tier_embeds(tier_ups)
|
|
||||||
assert len(result) == 1
|
|
||||||
assert isinstance(result[0], discord.Embed)
|
|
||||||
|
|
||||||
def test_multiple_tier_ups_return_separate_embeds(self):
|
|
||||||
"""Multiple tier-up events in one game must produce one embed per event."""
|
|
||||||
tier_ups = [
|
|
||||||
{
|
|
||||||
"player_name": "Mike Trout",
|
|
||||||
"tier": 2,
|
|
||||||
"tier_name": "Rising",
|
|
||||||
"track_name": "Batter",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"player_name": "Sandy Koufax",
|
|
||||||
"tier": 3,
|
|
||||||
"tier_name": "Elite",
|
|
||||||
"track_name": "Starter",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
result = build_tier_embeds(tier_ups)
|
|
||||||
assert len(result) == 2
|
|
||||||
assert (
|
|
||||||
result[0].description
|
|
||||||
== "Mike Trout reached Tier 2 (Rising) on the Batter track"
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
result[1].description
|
|
||||||
== "Sandy Koufax reached Tier 3 (Elite) on the Starter track"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_fully_evolved_in_batch(self):
|
|
||||||
"""A T4 event in a batch must produce a fully-evolved embed, not a standard one."""
|
|
||||||
tier_ups = [
|
|
||||||
{
|
|
||||||
"player_name": "Babe Ruth",
|
|
||||||
"tier": 4,
|
|
||||||
"tier_name": "Legendary",
|
|
||||||
"track_name": "Batter",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
result = build_tier_embeds(tier_ups)
|
|
||||||
assert len(result) == 1
|
|
||||||
assert result[0].title == "FULLY EVOLVED!"
|
|
||||||
assert result[0].footer.text == "Rating boosts coming in a future update!"
|
|
||||||
Loading…
Reference in New Issue
Block a user