paper-dynasty-discord/tests/test_evolution_commands.py
Cal Corum fce9cc5650
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m23s
feat(WP-11): /evo status slash command — closes #76
Add /evo status command showing paginated evolution progress:
- Progress bar with formula value vs next threshold
- Tier display names (Unranked/Initiate/Rising/Ascendant/Evolved)
- Formula shorthands (PA+TB×2, IP+K)
- Filters: card_type, tier, progress="close" (within 80%)
- Pagination at 10 per page
- Evolution cog registered in players_new/__init__.py
- 15 unit tests for pure helper functions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 15:45:41 -05:00

174 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Tests for the evolution status command helpers (WP-11).
Unit tests for progress bar rendering, entry formatting, tier display
names, close-to-tierup filtering, and edge cases. No Discord bot or
API calls required — these test pure functions only.
"""
import pytest
from cogs.players_new.evolution import (
render_progress_bar,
format_evo_entry,
is_close_to_tierup,
TIER_NAMES,
FORMULA_SHORTHANDS,
)
# ---------------------------------------------------------------------------
# render_progress_bar
# ---------------------------------------------------------------------------
class TestRenderProgressBar:
def test_80_percent_filled(self):
"""120/149 should be ~80% filled (8 of 10 chars)."""
result = render_progress_bar(120, 149, width=10)
assert "[========--]" in result
assert "120/149" in result
def test_zero_progress(self):
"""0/37 should be empty bar."""
result = render_progress_bar(0, 37, width=10)
assert "[----------]" in result
assert "0/37" in result
def test_full_progress_not_evolved(self):
"""Value at threshold shows full bar."""
result = render_progress_bar(149, 149, width=10)
assert "[==========]" in result
assert "149/149" in result
def test_fully_evolved(self):
"""next_threshold=None means fully evolved."""
result = render_progress_bar(900, None, width=10)
assert "FULLY EVOLVED" in result
assert "[==========]" in result
def test_over_threshold_capped(self):
"""Value exceeding threshold still caps at 100%."""
result = render_progress_bar(200, 149, width=10)
assert "[==========]" in result
# ---------------------------------------------------------------------------
# format_evo_entry
# ---------------------------------------------------------------------------
class TestFormatEvoEntry:
def test_batter_t1_to_t2(self):
"""Batter at T1 progressing toward T2."""
state = {
"current_tier": 1,
"current_value": 120.0,
"next_threshold": 149,
"fully_evolved": False,
"track": {"card_type": "batter"},
}
result = format_evo_entry(state)
assert "(PA+TB×2)" in result
assert "Initiate → Rising" in result
def test_pitcher_sp(self):
"""SP track shows IP+K formula."""
state = {
"current_tier": 0,
"current_value": 5.0,
"next_threshold": 10,
"fully_evolved": False,
"track": {"card_type": "sp"},
}
result = format_evo_entry(state)
assert "(IP+K)" in result
assert "Unranked → Initiate" in result
def test_fully_evolved_entry(self):
"""Fully evolved card shows T4 — Evolved."""
state = {
"current_tier": 4,
"current_value": 900.0,
"next_threshold": None,
"fully_evolved": True,
"track": {"card_type": "batter"},
}
result = format_evo_entry(state)
assert "FULLY EVOLVED" in result
assert "Evolved" in result
# ---------------------------------------------------------------------------
# is_close_to_tierup
# ---------------------------------------------------------------------------
class TestIsCloseToTierup:
def test_at_80_percent(self):
"""Exactly 80% of threshold counts as close."""
state = {"current_value": 119.2, "next_threshold": 149}
assert is_close_to_tierup(state, threshold_pct=0.80)
def test_below_80_percent(self):
"""Below 80% is not close."""
state = {"current_value": 100, "next_threshold": 149}
assert not is_close_to_tierup(state, threshold_pct=0.80)
def test_fully_evolved_not_close(self):
"""Fully evolved (no next threshold) is not close."""
state = {"current_value": 900, "next_threshold": None}
assert not is_close_to_tierup(state)
def test_zero_threshold(self):
"""Zero threshold edge case returns False."""
state = {"current_value": 0, "next_threshold": 0}
assert not is_close_to_tierup(state)
# ---------------------------------------------------------------------------
# Tier names and formula shorthands
# ---------------------------------------------------------------------------
class TestConstants:
def test_all_tier_names_present(self):
"""All 5 tiers (0-4) have display names."""
assert len(TIER_NAMES) == 5
for i in range(5):
assert i in TIER_NAMES
def test_tier_name_values(self):
assert TIER_NAMES[0] == "Unranked"
assert TIER_NAMES[1] == "Initiate"
assert TIER_NAMES[2] == "Rising"
assert TIER_NAMES[3] == "Ascendant"
assert TIER_NAMES[4] == "Evolved"
def test_formula_shorthands(self):
assert FORMULA_SHORTHANDS["batter"] == "PA+TB×2"
assert FORMULA_SHORTHANDS["sp"] == "IP+K"
assert FORMULA_SHORTHANDS["rp"] == "IP+K"
# ---------------------------------------------------------------------------
# Empty / edge cases
# ---------------------------------------------------------------------------
class TestEdgeCases:
def test_missing_track_defaults(self):
"""State with missing track info still formats without error."""
state = {
"current_tier": 0,
"current_value": 0,
"next_threshold": 37,
"fully_evolved": False,
"track": {},
}
result = format_evo_entry(state)
assert isinstance(result, str)
def test_state_with_no_keys(self):
"""Completely empty state dict doesn't crash."""
state = {}
result = format_evo_entry(state)
assert isinstance(result, str)