paper-dynasty-discord/tests/players_refactor/test_paperdex.py
Cal Corum ee80cd72ae fix: apply Black formatting and resolve ruff lint violations
Run Black formatter across 83 files and fix 1514 ruff violations:
- E722: bare except → typed exceptions (17 fixes)
- E711/E712/E721: comparison style fixes with noqa for SQLAlchemy (44 fixes)
- F841: unused variable assignments (70 fixes)
- F541/F401: f-string and import cleanup (1383 auto-fixes)

Remaining 925 errors are all F403/F405 (star imports) — structural,
requires converting to explicit imports in a separate effort.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 11:37:46 -05:00

633 lines
23 KiB
Python

"""
Comprehensive tests for the paperdex.py module.
Tests collection tracking and statistics functionality including:
- Cardset collection checking
- Team/franchise collection checking
- Collection progress tracking
- Missing cards identification
- Duplicate cards handling
"""
import pytest
from unittest.mock import Mock, patch
# Import the module to test (this will need to be updated when modules are moved)
try:
from cogs.players_new.paperdex import Paperdex
except ImportError:
# Create a mock class for testing structure
class Paperdex:
def __init__(self, bot):
self.bot = bot
@pytest.mark.asyncio
class TestPaperdex:
"""Test suite for Paperdex cog functionality."""
@pytest.fixture
def paperdex_cog(self, mock_bot):
"""Create Paperdex cog instance for testing."""
return Paperdex(mock_bot)
@pytest.fixture
def mock_cardset_collection_data(self):
"""Mock cardset collection data for testing."""
return {
"cardset_id": 1,
"cardset_name": "2024 Season",
"total_cards": 100,
"owned_cards": 75,
"completion_percentage": 75.0,
"missing_cards": [
{
"player_id": 10,
"name": "Mike Trout",
"cost": 500,
"rarity": "Legendary",
},
{
"player_id": 20,
"name": "Mookie Betts",
"cost": 400,
"rarity": "All-Star",
},
{
"player_id": 30,
"name": "Ronald Acuña Jr.",
"cost": 450,
"rarity": "All-Star",
},
],
"duplicates": [
{"player_id": 1, "name": "Common Player 1", "quantity": 3},
{"player_id": 2, "name": "Common Player 2", "quantity": 2},
],
"rarity_breakdown": {
"Common": {"owned": 50, "total": 60, "percentage": 83.3},
"All-Star": {"owned": 20, "total": 30, "percentage": 66.7},
"Legendary": {"owned": 5, "total": 10, "percentage": 50.0},
},
}
@pytest.fixture
def mock_franchise_collection_data(self):
"""Mock franchise collection data for testing."""
return {
"franchise_name": "Los Angeles Dodgers",
"total_cards": 15,
"owned_cards": 12,
"completion_percentage": 80.0,
"missing_players": [
{"player_id": 100, "name": "Mookie Betts", "cardset": "2024 Season"},
{"player_id": 101, "name": "Freddie Freeman", "cardset": "2023 Season"},
{"player_id": 102, "name": "Walker Buehler", "cardset": "2022 Season"},
],
"cardsets_represented": [
{"cardset_id": 1, "name": "2024 Season", "owned": 8, "total": 10},
{"cardset_id": 2, "name": "2023 Season", "owned": 3, "total": 4},
{"cardset_id": 3, "name": "2022 Season", "owned": 1, "total": 1},
],
}
async def test_init(self, paperdex_cog, mock_bot):
"""Test cog initialization."""
assert paperdex_cog.bot == mock_bot
@patch("helpers.get_team_by_owner")
@patch("api_calls.db_get")
@patch("helpers.cardset_search")
async def test_paperdex_cardset_success(
self,
mock_cardset_search,
mock_db_get,
mock_get_by_owner,
paperdex_cog,
mock_interaction,
sample_team_data,
mock_cardset_collection_data,
mock_embed,
):
"""Test successful cardset collection checking."""
mock_get_by_owner.return_value = sample_team_data
mock_cardset_search.return_value = {"id": 1, "name": "2024 Season"}
mock_db_get.return_value = mock_cardset_collection_data
async def mock_paperdex_cardset_command(interaction, cardset_name):
await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id)
if not team:
await interaction.followup.send(
"You need a team to check your collection!"
)
return
cardset = await mock_cardset_search(cardset_name)
if not cardset:
await interaction.followup.send(f'Cardset "{cardset_name}" not found.')
return
collection_data = await mock_db_get(
f'paperdex/cardset/{cardset["id"]}', params=[("team_id", team["id"])]
)
if not collection_data:
await interaction.followup.send("Error retrieving collection data.")
return
# Create embed with collection info
embed = mock_embed
embed.title = f'Paperdex - {cardset["name"]}'
embed.description = f'Collection Progress: {collection_data["owned_cards"]}/{collection_data["total_cards"]} ({collection_data["completion_percentage"]:.1f}%)'
# Add missing cards field
if collection_data["missing_cards"]:
missing_list = "\n".join(
[
f"{card['name']} (${card['cost']})"
for card in collection_data["missing_cards"][:10]
]
)
if len(collection_data["missing_cards"]) > 10:
missing_list += (
f"\n... and {len(collection_data['missing_cards']) - 10} more"
)
embed.add_field(name="Missing Cards", value=missing_list, inline=False)
# Add duplicates field
if collection_data["duplicates"]:
duplicate_list = "\n".join(
[
f"{card['name']} x{card['quantity']}"
for card in collection_data["duplicates"][:5]
]
)
if len(collection_data["duplicates"]) > 5:
duplicate_list += (
f"\n... and {len(collection_data['duplicates']) - 5} more"
)
embed.add_field(name="Duplicates", value=duplicate_list, inline=False)
await interaction.followup.send(embed=embed)
await mock_paperdex_cardset_command(mock_interaction, "2024 Season")
mock_interaction.response.defer.assert_called_once()
mock_get_by_owner.assert_called_once_with(mock_interaction.user.id)
mock_cardset_search.assert_called_once_with("2024 Season")
mock_db_get.assert_called_once()
mock_interaction.followup.send.assert_called_once()
@patch("helpers.get_team_by_owner")
async def test_paperdex_cardset_no_team(
self, mock_get_by_owner, paperdex_cog, mock_interaction
):
"""Test cardset collection check when user has no team."""
mock_get_by_owner.return_value = None
async def mock_paperdex_cardset_command(interaction, cardset_name):
await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id)
if not team:
await interaction.followup.send(
"You need a team to check your collection!"
)
return
await mock_paperdex_cardset_command(mock_interaction, "2024 Season")
mock_interaction.followup.send.assert_called_once_with(
"You need a team to check your collection!"
)
@patch("helpers.get_team_by_owner")
@patch("helpers.cardset_search")
async def test_paperdex_cardset_not_found(
self,
mock_cardset_search,
mock_get_by_owner,
paperdex_cog,
mock_interaction,
sample_team_data,
):
"""Test cardset collection check when cardset is not found."""
mock_get_by_owner.return_value = sample_team_data
mock_cardset_search.return_value = None
async def mock_paperdex_cardset_command(interaction, cardset_name):
await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id)
if not team:
await interaction.followup.send(
"You need a team to check your collection!"
)
return
cardset = await mock_cardset_search(cardset_name)
if not cardset:
await interaction.followup.send(f'Cardset "{cardset_name}" not found.')
return
await mock_paperdex_cardset_command(mock_interaction, "Nonexistent Set")
mock_interaction.followup.send.assert_called_once_with(
'Cardset "Nonexistent Set" not found.'
)
@patch("helpers.get_team_by_owner")
@patch("helpers.cardset_search")
@patch("api_calls.db_get")
async def test_paperdex_cardset_api_error(
self,
mock_db_get,
mock_cardset_search,
mock_get_by_owner,
paperdex_cog,
mock_interaction,
sample_team_data,
):
"""Test cardset collection check API error handling."""
mock_get_by_owner.return_value = sample_team_data
mock_cardset_search.return_value = {"id": 1, "name": "2024 Season"}
mock_db_get.return_value = None
async def mock_paperdex_cardset_command(interaction, cardset_name):
await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id)
cardset = await mock_cardset_search(cardset_name)
collection_data = await mock_db_get(
f'paperdex/cardset/{cardset["id"]}', params=[("team_id", team["id"])]
)
if not collection_data:
await interaction.followup.send("Error retrieving collection data.")
return
await mock_paperdex_cardset_command(mock_interaction, "2024 Season")
mock_interaction.followup.send.assert_called_once_with(
"Error retrieving collection data."
)
@patch("helpers.get_team_by_owner")
@patch("api_calls.db_get")
@patch("helpers.fuzzy_search")
async def test_paperdex_team_success(
self,
mock_fuzzy_search,
mock_db_get,
mock_get_by_owner,
paperdex_cog,
mock_interaction,
sample_team_data,
mock_franchise_collection_data,
mock_embed,
):
"""Test successful franchise collection checking."""
mock_get_by_owner.return_value = sample_team_data
mock_fuzzy_search.return_value = "Los Angeles Dodgers"
mock_db_get.return_value = mock_franchise_collection_data
async def mock_paperdex_team_command(interaction, franchise_name):
await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id)
if not team:
await interaction.followup.send(
"You need a team to check your collection!"
)
return
# Fuzzy search for franchise
franchise = mock_fuzzy_search(
franchise_name, ["Los Angeles Dodgers", "New York Yankees"]
)
if not franchise:
await interaction.followup.send(
f'Franchise "{franchise_name}" not found.'
)
return
collection_data = await mock_db_get(
f"paperdex/franchise/{franchise}", params=[("team_id", team["id"])]
)
if not collection_data:
await interaction.followup.send("Error retrieving collection data.")
return
# Create embed with collection info
embed = mock_embed
embed.title = f"Paperdex - {franchise}"
embed.description = f'Collection Progress: {collection_data["owned_cards"]}/{collection_data["total_cards"]} ({collection_data["completion_percentage"]:.1f}%)'
# Add missing players field
if collection_data["missing_players"]:
missing_list = "\n".join(
[
f"{player['name']} ({player['cardset']})"
for player in collection_data["missing_players"][:10]
]
)
embed.add_field(
name="Missing Players", value=missing_list, inline=False
)
# Add cardsets breakdown
cardset_list = "\n".join(
[
f"{cs['name']}: {cs['owned']}/{cs['total']}"
for cs in collection_data["cardsets_represented"]
]
)
embed.add_field(name="Cardsets", value=cardset_list, inline=False)
await interaction.followup.send(embed=embed)
await mock_paperdex_team_command(mock_interaction, "Dodgers")
mock_interaction.response.defer.assert_called_once()
mock_get_by_owner.assert_called_once()
mock_fuzzy_search.assert_called_once()
mock_db_get.assert_called_once()
mock_interaction.followup.send.assert_called_once()
@patch("helpers.get_team_by_owner")
async def test_paperdex_team_no_team(
self, mock_get_by_owner, paperdex_cog, mock_interaction
):
"""Test franchise collection check when user has no team."""
mock_get_by_owner.return_value = None
async def mock_paperdex_team_command(interaction, franchise_name):
await interaction.response.defer()
team = await mock_get_by_owner(interaction.user.id)
if not team:
await interaction.followup.send(
"You need a team to check your collection!"
)
return
await mock_paperdex_team_command(mock_interaction, "Dodgers")
mock_interaction.followup.send.assert_called_once_with(
"You need a team to check your collection!"
)
@patch("helpers.get_team_by_owner")
@patch("helpers.fuzzy_search")
async def test_paperdex_team_not_found(
self,
mock_fuzzy_search,
mock_get_by_owner,
paperdex_cog,
mock_interaction,
sample_team_data,
):
"""Test franchise collection check when franchise is not found."""
mock_get_by_owner.return_value = sample_team_data
mock_fuzzy_search.return_value = None
async def mock_paperdex_team_command(interaction, franchise_name):
await interaction.response.defer()
await mock_get_by_owner(interaction.user.id)
franchise = mock_fuzzy_search(franchise_name, [])
if not franchise:
await interaction.followup.send(
f'Franchise "{franchise_name}" not found.'
)
return
await mock_paperdex_team_command(mock_interaction, "Nonexistent Team")
mock_interaction.followup.send.assert_called_once_with(
'Franchise "Nonexistent Team" not found.'
)
def test_collection_percentage_calculation(self, paperdex_cog):
"""Test collection percentage calculation accuracy."""
test_cases = [
{"owned": 0, "total": 100, "expected": 0.0},
{"owned": 50, "total": 100, "expected": 50.0},
{"owned": 100, "total": 100, "expected": 100.0},
{"owned": 33, "total": 99, "expected": 33.33},
{"owned": 1, "total": 3, "expected": 33.33},
]
def calculate_percentage(owned, total):
return round((owned / total) * 100, 2) if total > 0 else 0.0
for case in test_cases:
result = calculate_percentage(case["owned"], case["total"])
assert (
abs(result - case["expected"]) < 0.01
), f"Expected {case['expected']}, got {result}"
def test_missing_cards_formatting(self, paperdex_cog, mock_cardset_collection_data):
"""Test proper formatting of missing cards lists."""
missing_cards = mock_cardset_collection_data["missing_cards"]
# Test truncation for long lists
def format_missing_cards(cards, limit=10):
if not cards:
return "None! Collection complete!"
formatted_list = "\n".join(
[f"{card['name']} (${card['cost']})" for card in cards[:limit]]
)
if len(cards) > limit:
formatted_list += f"\n... and {len(cards) - limit} more"
return formatted_list
result = format_missing_cards(missing_cards, 2)
lines = result.split("\n")
assert len(lines) == 3 # 2 cards + "... and X more"
assert "Mike Trout" in lines[0]
assert "Mookie Betts" in lines[1]
assert "... and 1 more" in lines[2]
def test_duplicates_formatting(self, paperdex_cog, mock_cardset_collection_data):
"""Test proper formatting of duplicate cards lists."""
duplicates = mock_cardset_collection_data["duplicates"]
def format_duplicates(cards, limit=5):
if not cards:
return "No duplicates found."
formatted_list = "\n".join(
[f"{card['name']} x{card['quantity']}" for card in cards[:limit]]
)
if len(cards) > limit:
formatted_list += f"\n... and {len(cards) - limit} more"
return formatted_list
result = format_duplicates(duplicates, 5)
lines = result.split("\n")
assert len(lines) == 2 # Both cards fit within limit
assert "Common Player 1 x3" in lines[0]
assert "Common Player 2 x2" in lines[1]
def test_rarity_breakdown_formatting(
self, paperdex_cog, mock_cardset_collection_data
):
"""Test proper formatting of rarity breakdown information."""
rarity_breakdown = mock_cardset_collection_data["rarity_breakdown"]
def format_rarity_breakdown(breakdown):
if not breakdown:
return "No rarity data available."
formatted_list = []
for rarity, data in breakdown.items():
percentage = data["percentage"]
formatted_list.append(
f"{rarity}: {data['owned']}/{data['total']} ({percentage:.1f}%)"
)
return "\n".join(formatted_list)
result = format_rarity_breakdown(rarity_breakdown)
lines = result.split("\n")
assert len(lines) == 3
assert "Common: 50/60 (83.3%)" in lines[0]
assert "All-Star: 20/30 (66.7%)" in lines[1]
assert "Legendary: 5/10 (50.0%)" in lines[2]
def test_cardset_list_formatting(
self, paperdex_cog, mock_franchise_collection_data
):
"""Test proper formatting of cardset representation lists."""
cardsets = mock_franchise_collection_data["cardsets_represented"]
def format_cardsets(cardsets):
if not cardsets:
return "No cards owned for this franchise."
formatted_list = []
for cardset in cardsets:
percentage = (cardset["owned"] / cardset["total"]) * 100
formatted_list.append(
f"{cardset['name']}: {cardset['owned']}/{cardset['total']} ({percentage:.1f}%)"
)
return "\n".join(formatted_list)
result = format_cardsets(cardsets)
lines = result.split("\n")
assert len(lines) == 3
assert "2024 Season: 8/10 (80.0%)" in lines[0]
assert "2023 Season: 3/4 (75.0%)" in lines[1]
assert "2022 Season: 1/1 (100.0%)" in lines[2]
@patch("helpers.ALL_MLB_TEAMS")
def test_franchise_fuzzy_search(self, mock_all_teams, paperdex_cog):
"""Test fuzzy search functionality for MLB franchises."""
mock_all_teams.return_value = [
"Los Angeles Dodgers",
"New York Yankees",
"Boston Red Sox",
"Chicago Cubs",
"San Francisco Giants",
]
with patch("helpers.fuzzy_search") as mock_fuzzy:
# Test exact match
mock_fuzzy.return_value = "Los Angeles Dodgers"
result = mock_fuzzy("Los Angeles Dodgers", mock_all_teams.return_value)
assert result == "Los Angeles Dodgers"
# Test partial match
mock_fuzzy.return_value = "Los Angeles Dodgers"
result = mock_fuzzy("Dodgers", mock_all_teams.return_value)
assert result == "Los Angeles Dodgers"
# Test abbreviation match
mock_fuzzy.return_value = "New York Yankees"
result = mock_fuzzy("NYY", mock_all_teams.return_value)
assert result == "New York Yankees"
def test_empty_collection_handling(self, paperdex_cog):
"""Test handling of completely empty collections."""
empty_collection = {
"cardset_id": 1,
"cardset_name": "2024 Season",
"total_cards": 100,
"owned_cards": 0,
"completion_percentage": 0.0,
"missing_cards": [],
"duplicates": [],
"rarity_breakdown": {},
}
# Test that zero completion is handled properly
assert empty_collection["completion_percentage"] == 0.0
assert empty_collection["owned_cards"] == 0
# Test empty lists
assert len(empty_collection["missing_cards"]) == 0
assert len(empty_collection["duplicates"]) == 0
assert len(empty_collection["rarity_breakdown"]) == 0
def test_complete_collection_handling(self, paperdex_cog):
"""Test handling of complete collections."""
complete_collection = {
"cardset_id": 1,
"cardset_name": "2024 Season",
"total_cards": 100,
"owned_cards": 100,
"completion_percentage": 100.0,
"missing_cards": [],
"duplicates": [{"player_id": 1, "name": "Extra Card", "quantity": 2}],
}
assert complete_collection["completion_percentage"] == 100.0
assert complete_collection["owned_cards"] == complete_collection["total_cards"]
assert len(complete_collection["missing_cards"]) == 0
# Complete collections can still have duplicates
assert len(complete_collection["duplicates"]) > 0
@patch("logging.getLogger")
async def test_error_logging(self, mock_logger, paperdx_cog):
"""Test error logging for collection operations."""
mock_logger_instance = Mock()
mock_logger.return_value = mock_logger_instance
# Test API error logging
with patch("api_calls.db_get") as mock_db_get:
mock_db_get.side_effect = Exception("Collection API Error")
try:
await mock_db_get("paperdx/cardset/1")
except Exception:
# In actual implementation, this would be caught and logged
pass
def test_permission_checks(self, paperdx_cog, mock_interaction):
"""Test permission checking for paperdx commands."""
# Test role check
mock_member_with_role = Mock()
mock_member_with_role.roles = [Mock(name="Paper Dynasty")]
mock_interaction.user = mock_member_with_role
# Test channel check
with patch("helpers.legal_channel") as mock_legal_check:
mock_legal_check.return_value = True
result = mock_legal_check(mock_interaction.channel)
assert result is True