523 lines
23 KiB
Python
523 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
|
|
import asyncio
|
|
from unittest.mock import AsyncMock, Mock, patch, MagicMock
|
|
from discord.ext import commands
|
|
from discord import app_commands
|
|
import discord
|
|
|
|
# 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()
|
|
|
|
team = 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 |