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>
877 lines
30 KiB
Python
877 lines
30 KiB
Python
"""
|
|
Comprehensive tests for the gauntlet.py module.
|
|
Tests gauntlet game mode functionality including:
|
|
- Gauntlet status command
|
|
- Start new gauntlet run command
|
|
- Reset gauntlet team command
|
|
- Draft management
|
|
- Progress tracking
|
|
- Gauntlet game logic
|
|
"""
|
|
|
|
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.gauntlet import Gauntlet
|
|
except ImportError:
|
|
# Create a mock class for testing structure
|
|
class Gauntlet:
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
class TestGauntlet:
|
|
"""Test suite for Gauntlet cog functionality."""
|
|
|
|
@pytest.fixture
|
|
def gauntlet_cog(self, mock_bot):
|
|
"""Create Gauntlet cog instance for testing."""
|
|
return Gauntlet(mock_bot)
|
|
|
|
@pytest.fixture
|
|
def mock_active_gauntlet_data(self):
|
|
"""Mock active gauntlet data for testing."""
|
|
return {
|
|
"gauntlet_id": 1,
|
|
"team_id": 31,
|
|
"status": "active",
|
|
"current_round": 3,
|
|
"wins": 2,
|
|
"losses": 0,
|
|
"draft_complete": True,
|
|
"created_at": "2024-08-06T10:00:00Z",
|
|
"roster": [
|
|
{
|
|
"card_id": 1,
|
|
"player_name": "Mike Trout",
|
|
"position": "CF",
|
|
"draft_position": 1,
|
|
},
|
|
{
|
|
"card_id": 2,
|
|
"player_name": "Mookie Betts",
|
|
"position": "RF",
|
|
"draft_position": 2,
|
|
},
|
|
{
|
|
"card_id": 3,
|
|
"player_name": "Ronald Acuña Jr.",
|
|
"position": "LF",
|
|
"draft_position": 3,
|
|
},
|
|
{
|
|
"card_id": 4,
|
|
"player_name": "Juan Soto",
|
|
"position": "DH",
|
|
"draft_position": 4,
|
|
},
|
|
{
|
|
"card_id": 5,
|
|
"player_name": "Freddie Freeman",
|
|
"position": "1B",
|
|
"draft_position": 5,
|
|
},
|
|
{
|
|
"card_id": 6,
|
|
"player_name": "Corey Seager",
|
|
"position": "SS",
|
|
"draft_position": 6,
|
|
},
|
|
{
|
|
"card_id": 7,
|
|
"player_name": "Manny Machado",
|
|
"position": "3B",
|
|
"draft_position": 7,
|
|
},
|
|
{
|
|
"card_id": 8,
|
|
"player_name": "José Altuve",
|
|
"position": "2B",
|
|
"draft_position": 8,
|
|
},
|
|
{
|
|
"card_id": 9,
|
|
"player_name": "Salvador Perez",
|
|
"position": "C",
|
|
"draft_position": 9,
|
|
},
|
|
{
|
|
"card_id": 10,
|
|
"player_name": "Gerrit Cole",
|
|
"position": "SP",
|
|
"draft_position": 10,
|
|
},
|
|
],
|
|
"opponents_defeated": [
|
|
{"opponent": "Arizona Diamondbacks", "round": 1, "score": "8-5"},
|
|
{"opponent": "Atlanta Braves", "round": 2, "score": "6-4"},
|
|
],
|
|
"next_opponent": "Baltimore Orioles",
|
|
"draft_pool_remaining": 0,
|
|
"reward_tier": "Bronze",
|
|
}
|
|
|
|
@pytest.fixture
|
|
def mock_inactive_gauntlet_data(self):
|
|
"""Mock inactive gauntlet data for testing."""
|
|
return {
|
|
"gauntlet_id": None,
|
|
"team_id": 31,
|
|
"status": "inactive",
|
|
"last_completed": "2024-08-05T15:30:00Z",
|
|
"best_run": {
|
|
"wins": 5,
|
|
"losses": 3,
|
|
"reward_tier": "Silver",
|
|
"completed_at": "2024-08-04T12:00:00Z",
|
|
},
|
|
"total_runs": 3,
|
|
"total_wins": 12,
|
|
"total_losses": 9,
|
|
}
|
|
|
|
@pytest.fixture
|
|
def mock_draft_pool(self):
|
|
"""Mock draft pool data for starting a new gauntlet."""
|
|
return {
|
|
"available_cards": [
|
|
{
|
|
"card_id": 101,
|
|
"player_name": "Shohei Ohtani",
|
|
"position": "SP/DH",
|
|
"cost": 500,
|
|
"rarity": "Legendary",
|
|
},
|
|
{
|
|
"card_id": 102,
|
|
"player_name": "Aaron Judge",
|
|
"position": "RF",
|
|
"cost": 450,
|
|
"rarity": "All-Star",
|
|
},
|
|
{
|
|
"card_id": 103,
|
|
"player_name": "Vladimir Guerrero Jr.",
|
|
"position": "1B",
|
|
"cost": 400,
|
|
"rarity": "All-Star",
|
|
},
|
|
{
|
|
"card_id": 104,
|
|
"player_name": "Fernando Tatis Jr.",
|
|
"position": "SS",
|
|
"cost": 380,
|
|
"rarity": "All-Star",
|
|
},
|
|
{
|
|
"card_id": 105,
|
|
"player_name": "Jacob deGrom",
|
|
"position": "SP",
|
|
"cost": 350,
|
|
"rarity": "All-Star",
|
|
},
|
|
],
|
|
"draft_rules": {
|
|
"roster_size": 10,
|
|
"budget_cap": 2000,
|
|
"position_requirements": {
|
|
"C": 1,
|
|
"1B": 1,
|
|
"2B": 1,
|
|
"3B": 1,
|
|
"SS": 1,
|
|
"LF": 1,
|
|
"CF": 1,
|
|
"RF": 1,
|
|
"DH": 1,
|
|
"SP": 1,
|
|
},
|
|
},
|
|
}
|
|
|
|
async def test_init(self, gauntlet_cog, mock_bot):
|
|
"""Test cog initialization."""
|
|
assert gauntlet_cog.bot == mock_bot
|
|
|
|
@patch("helpers.get_team_by_owner")
|
|
@patch("gauntlets.get_gauntlet_status")
|
|
async def test_gauntlet_status_active(
|
|
self,
|
|
mock_get_status,
|
|
mock_get_by_owner,
|
|
gauntlet_cog,
|
|
mock_interaction,
|
|
sample_team_data,
|
|
mock_active_gauntlet_data,
|
|
mock_embed,
|
|
):
|
|
"""Test gauntlet status command with active gauntlet."""
|
|
mock_get_by_owner.return_value = sample_team_data
|
|
mock_get_status.return_value = mock_active_gauntlet_data
|
|
|
|
async def mock_gauntlet_status_command(interaction):
|
|
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 participate in the gauntlet!"
|
|
)
|
|
return
|
|
|
|
gauntlet_data = await mock_get_status(team["id"])
|
|
|
|
if gauntlet_data["status"] == "active":
|
|
embed = mock_embed
|
|
embed.title = "🏟️ Gauntlet Status - Active"
|
|
embed.color = 0x00FF00 # Green for active
|
|
|
|
# Current progress
|
|
embed.add_field(
|
|
name="Progress",
|
|
value=f"Round {gauntlet_data['current_round']} • {gauntlet_data['wins']}-{gauntlet_data['losses']}",
|
|
inline=True,
|
|
)
|
|
|
|
# Next opponent
|
|
if gauntlet_data.get("next_opponent"):
|
|
embed.add_field(
|
|
name="Next Opponent",
|
|
value=gauntlet_data["next_opponent"],
|
|
inline=True,
|
|
)
|
|
|
|
# Roster summary
|
|
roster_summary = "\n".join(
|
|
[
|
|
f"{card['position']}: {card['player_name']}"
|
|
for card in gauntlet_data["roster"][:5]
|
|
]
|
|
)
|
|
if len(gauntlet_data["roster"]) > 5:
|
|
roster_summary += (
|
|
f"\n... and {len(gauntlet_data['roster']) - 5} more"
|
|
)
|
|
|
|
embed.add_field(name="Roster", value=roster_summary, inline=False)
|
|
|
|
await interaction.followup.send(embed=embed)
|
|
else:
|
|
await interaction.followup.send("No active gauntlet run.")
|
|
|
|
await mock_gauntlet_status_command(mock_interaction)
|
|
|
|
mock_interaction.response.defer.assert_called_once()
|
|
mock_get_by_owner.assert_called_once_with(mock_interaction.user.id)
|
|
mock_get_status.assert_called_once_with(sample_team_data["id"])
|
|
mock_interaction.followup.send.assert_called_once()
|
|
|
|
@patch("helpers.get_team_by_owner")
|
|
@patch("gauntlets.get_gauntlet_status")
|
|
async def test_gauntlet_status_inactive(
|
|
self,
|
|
mock_get_status,
|
|
mock_get_by_owner,
|
|
gauntlet_cog,
|
|
mock_interaction,
|
|
sample_team_data,
|
|
mock_inactive_gauntlet_data,
|
|
mock_embed,
|
|
):
|
|
"""Test gauntlet status command with inactive gauntlet."""
|
|
mock_get_by_owner.return_value = sample_team_data
|
|
mock_get_status.return_value = mock_inactive_gauntlet_data
|
|
|
|
async def mock_gauntlet_status_command(interaction):
|
|
await interaction.response.defer()
|
|
|
|
team = await mock_get_by_owner(interaction.user.id)
|
|
gauntlet_data = await mock_get_status(team["id"])
|
|
|
|
if gauntlet_data["status"] == "inactive":
|
|
embed = mock_embed
|
|
embed.title = "🏟️ Gauntlet Status - Inactive"
|
|
embed.color = 0xFF0000 # Red for inactive
|
|
|
|
# Best run stats
|
|
if gauntlet_data.get("best_run"):
|
|
best = gauntlet_data["best_run"]
|
|
embed.add_field(
|
|
name="Best Run",
|
|
value=f"{best['wins']}-{best['losses']} ({best['reward_tier']})",
|
|
inline=True,
|
|
)
|
|
|
|
# Overall stats
|
|
embed.add_field(
|
|
name="Overall Stats",
|
|
value=f"Runs: {gauntlet_data['total_runs']}\nRecord: {gauntlet_data['total_wins']}-{gauntlet_data['total_losses']}",
|
|
inline=True,
|
|
)
|
|
|
|
embed.add_field(
|
|
name="Action",
|
|
value="Use `/gauntlet start` to begin a new run!",
|
|
inline=False,
|
|
)
|
|
|
|
await interaction.followup.send(embed=embed)
|
|
|
|
await mock_gauntlet_status_command(mock_interaction)
|
|
|
|
mock_interaction.followup.send.assert_called_once()
|
|
|
|
@patch("helpers.get_team_by_owner")
|
|
async def test_gauntlet_status_no_team(
|
|
self, mock_get_by_owner, gauntlet_cog, mock_interaction
|
|
):
|
|
"""Test gauntlet status command when user has no team."""
|
|
mock_get_by_owner.return_value = None
|
|
|
|
async def mock_gauntlet_status_command(interaction):
|
|
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 participate in the gauntlet!"
|
|
)
|
|
return
|
|
|
|
await mock_gauntlet_status_command(mock_interaction)
|
|
|
|
mock_interaction.followup.send.assert_called_once_with(
|
|
"You need a team to participate in the gauntlet!"
|
|
)
|
|
|
|
@patch("helpers.get_team_by_owner")
|
|
@patch("gauntlets.get_gauntlet_status")
|
|
@patch("gauntlets.get_draft_pool")
|
|
@patch("gauntlets.start_new_gauntlet")
|
|
async def test_gauntlet_start_success(
|
|
self,
|
|
mock_start_gauntlet,
|
|
mock_get_draft_pool,
|
|
mock_get_status,
|
|
mock_get_by_owner,
|
|
gauntlet_cog,
|
|
mock_interaction,
|
|
sample_team_data,
|
|
mock_inactive_gauntlet_data,
|
|
mock_draft_pool,
|
|
mock_embed,
|
|
):
|
|
"""Test successful gauntlet start command."""
|
|
mock_get_by_owner.return_value = sample_team_data
|
|
mock_get_status.return_value = mock_inactive_gauntlet_data
|
|
mock_get_draft_pool.return_value = mock_draft_pool
|
|
mock_start_gauntlet.return_value = {"gauntlet_id": 2, "status": "draft_phase"}
|
|
|
|
async def mock_gauntlet_start_command(interaction):
|
|
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 participate in the gauntlet!"
|
|
)
|
|
return
|
|
|
|
# Check if already active
|
|
gauntlet_status = await mock_get_status(team["id"])
|
|
if gauntlet_status["status"] == "active":
|
|
await interaction.followup.send(
|
|
"You already have an active gauntlet run! Use `/gauntlet status` to check progress."
|
|
)
|
|
return
|
|
|
|
# Get draft pool
|
|
draft_pool = await mock_get_draft_pool()
|
|
if not draft_pool or not draft_pool.get("available_cards"):
|
|
await interaction.followup.send(
|
|
"No cards available for drafting. Please try again later."
|
|
)
|
|
return
|
|
|
|
# Start new gauntlet
|
|
await mock_start_gauntlet(team["id"], draft_pool)
|
|
|
|
embed = mock_embed
|
|
embed.title = "🏟️ New Gauntlet Started!"
|
|
embed.color = 0x00FF00
|
|
embed.description = "Your gauntlet run has begun! Time to draft your team."
|
|
|
|
embed.add_field(
|
|
name="Draft Rules",
|
|
value=f"• Roster Size: {draft_pool['draft_rules']['roster_size']}\n• Budget Cap: ${draft_pool['draft_rules']['budget_cap']}",
|
|
inline=False,
|
|
)
|
|
|
|
embed.add_field(
|
|
name="Available Cards",
|
|
value=f"{len(draft_pool['available_cards'])} cards in draft pool",
|
|
inline=True,
|
|
)
|
|
|
|
await interaction.followup.send(embed=embed)
|
|
|
|
await mock_gauntlet_start_command(mock_interaction)
|
|
|
|
mock_get_by_owner.assert_called_once()
|
|
mock_get_status.assert_called_once()
|
|
mock_get_draft_pool.assert_called_once()
|
|
mock_start_gauntlet.assert_called_once()
|
|
mock_interaction.followup.send.assert_called_once()
|
|
|
|
@patch("helpers.get_team_by_owner")
|
|
@patch("gauntlets.get_gauntlet_status")
|
|
async def test_gauntlet_start_already_active(
|
|
self,
|
|
mock_get_status,
|
|
mock_get_by_owner,
|
|
gauntlet_cog,
|
|
mock_interaction,
|
|
sample_team_data,
|
|
mock_active_gauntlet_data,
|
|
):
|
|
"""Test gauntlet start command when already active."""
|
|
mock_get_by_owner.return_value = sample_team_data
|
|
mock_get_status.return_value = mock_active_gauntlet_data
|
|
|
|
async def mock_gauntlet_start_command(interaction):
|
|
await interaction.response.defer()
|
|
|
|
team = await mock_get_by_owner(interaction.user.id)
|
|
gauntlet_status = await mock_get_status(team["id"])
|
|
|
|
if gauntlet_status["status"] == "active":
|
|
await interaction.followup.send(
|
|
"You already have an active gauntlet run! Use `/gauntlet status` to check progress."
|
|
)
|
|
return
|
|
|
|
await mock_gauntlet_start_command(mock_interaction)
|
|
|
|
mock_interaction.followup.send.assert_called_once_with(
|
|
"You already have an active gauntlet run! Use `/gauntlet status` to check progress."
|
|
)
|
|
|
|
@patch("helpers.get_team_by_owner")
|
|
@patch("gauntlets.get_gauntlet_status")
|
|
@patch("gauntlets.get_draft_pool")
|
|
async def test_gauntlet_start_no_draft_pool(
|
|
self,
|
|
mock_get_draft_pool,
|
|
mock_get_status,
|
|
mock_get_by_owner,
|
|
gauntlet_cog,
|
|
mock_interaction,
|
|
sample_team_data,
|
|
mock_inactive_gauntlet_data,
|
|
):
|
|
"""Test gauntlet start command when no draft pool available."""
|
|
mock_get_by_owner.return_value = sample_team_data
|
|
mock_get_status.return_value = mock_inactive_gauntlet_data
|
|
mock_get_draft_pool.return_value = {"available_cards": []}
|
|
|
|
async def mock_gauntlet_start_command(interaction):
|
|
await interaction.response.defer()
|
|
|
|
team = await mock_get_by_owner(interaction.user.id)
|
|
await mock_get_status(team["id"])
|
|
draft_pool = await mock_get_draft_pool()
|
|
|
|
if not draft_pool or not draft_pool.get("available_cards"):
|
|
await interaction.followup.send(
|
|
"No cards available for drafting. Please try again later."
|
|
)
|
|
return
|
|
|
|
await mock_gauntlet_start_command(mock_interaction)
|
|
|
|
mock_interaction.followup.send.assert_called_once_with(
|
|
"No cards available for drafting. Please try again later."
|
|
)
|
|
|
|
@patch("helpers.get_team_by_owner")
|
|
@patch("gauntlets.get_gauntlet_status")
|
|
@patch("gauntlets.reset_gauntlet")
|
|
async def test_gauntlet_reset_success(
|
|
self,
|
|
mock_reset_gauntlet,
|
|
mock_get_status,
|
|
mock_get_by_owner,
|
|
gauntlet_cog,
|
|
mock_interaction,
|
|
sample_team_data,
|
|
mock_active_gauntlet_data,
|
|
):
|
|
"""Test successful gauntlet reset command."""
|
|
mock_get_by_owner.return_value = sample_team_data
|
|
mock_get_status.return_value = mock_active_gauntlet_data
|
|
mock_reset_gauntlet.return_value = {
|
|
"success": True,
|
|
"message": "Gauntlet reset successfully",
|
|
}
|
|
|
|
async def mock_gauntlet_reset_command(interaction):
|
|
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 participate in the gauntlet!"
|
|
)
|
|
return
|
|
|
|
gauntlet_status = await mock_get_status(team["id"])
|
|
if gauntlet_status["status"] != "active":
|
|
await interaction.followup.send(
|
|
"You don't have an active gauntlet to reset."
|
|
)
|
|
return
|
|
|
|
# Confirm reset (in real implementation, this would use buttons/confirmation)
|
|
reset_result = await mock_reset_gauntlet(team["id"])
|
|
|
|
if reset_result["success"]:
|
|
await interaction.followup.send(
|
|
"✅ Gauntlet reset successfully! You can start a new run when ready."
|
|
)
|
|
else:
|
|
await interaction.followup.send(
|
|
"❌ Failed to reset gauntlet. Please try again."
|
|
)
|
|
|
|
await mock_gauntlet_reset_command(mock_interaction)
|
|
|
|
mock_get_by_owner.assert_called_once()
|
|
mock_get_status.assert_called_once()
|
|
mock_reset_gauntlet.assert_called_once()
|
|
mock_interaction.followup.send.assert_called_once_with(
|
|
"✅ Gauntlet reset successfully! You can start a new run when ready."
|
|
)
|
|
|
|
@patch("helpers.get_team_by_owner")
|
|
@patch("gauntlets.get_gauntlet_status")
|
|
async def test_gauntlet_reset_no_active(
|
|
self,
|
|
mock_get_status,
|
|
mock_get_by_owner,
|
|
gauntlet_cog,
|
|
mock_interaction,
|
|
sample_team_data,
|
|
mock_inactive_gauntlet_data,
|
|
):
|
|
"""Test gauntlet reset command when no active gauntlet."""
|
|
mock_get_by_owner.return_value = sample_team_data
|
|
mock_get_status.return_value = mock_inactive_gauntlet_data
|
|
|
|
async def mock_gauntlet_reset_command(interaction):
|
|
await interaction.response.defer()
|
|
|
|
team = await mock_get_by_owner(interaction.user.id)
|
|
gauntlet_status = await mock_get_status(team["id"])
|
|
|
|
if gauntlet_status["status"] != "active":
|
|
await interaction.followup.send(
|
|
"You don't have an active gauntlet to reset."
|
|
)
|
|
return
|
|
|
|
await mock_gauntlet_reset_command(mock_interaction)
|
|
|
|
mock_interaction.followup.send.assert_called_once_with(
|
|
"You don't have an active gauntlet to reset."
|
|
)
|
|
|
|
def test_draft_budget_validation(self, gauntlet_cog, mock_draft_pool):
|
|
"""Test draft budget validation logic."""
|
|
|
|
def validate_draft_budget(selected_cards, budget_cap):
|
|
"""Validate that selected cards fit within budget."""
|
|
total_cost = sum(card["cost"] for card in selected_cards)
|
|
return total_cost <= budget_cap, total_cost
|
|
|
|
# Test valid budget
|
|
selected_cards = [
|
|
{"card_id": 101, "cost": 500},
|
|
{"card_id": 102, "cost": 450},
|
|
{"card_id": 103, "cost": 400},
|
|
]
|
|
is_valid, total = validate_draft_budget(selected_cards, 2000)
|
|
assert is_valid is True
|
|
assert total == 1350
|
|
|
|
# Test over budget
|
|
expensive_cards = [
|
|
{"card_id": 101, "cost": 500},
|
|
{"card_id": 102, "cost": 600},
|
|
{"card_id": 103, "cost": 700},
|
|
{"card_id": 104, "cost": 800},
|
|
]
|
|
is_valid, total = validate_draft_budget(expensive_cards, 2000)
|
|
assert is_valid is False
|
|
assert total == 2600
|
|
|
|
def test_position_requirements_validation(self, gauntlet_cog):
|
|
"""Test position requirements validation for draft."""
|
|
|
|
def validate_position_requirements(selected_cards, requirements):
|
|
"""Validate that all required positions are filled."""
|
|
position_counts = {}
|
|
for card in selected_cards:
|
|
pos = card["position"]
|
|
# Handle multi-position players (e.g., "SP/DH")
|
|
if "/" in pos:
|
|
pos = pos.split("/")[0] # Use primary position
|
|
position_counts[pos] = position_counts.get(pos, 0) + 1
|
|
|
|
missing_positions = []
|
|
for pos, required_count in requirements.items():
|
|
if position_counts.get(pos, 0) < required_count:
|
|
missing_positions.append(pos)
|
|
|
|
return len(missing_positions) == 0, missing_positions
|
|
|
|
requirements = {
|
|
"C": 1,
|
|
"1B": 1,
|
|
"2B": 1,
|
|
"3B": 1,
|
|
"SS": 1,
|
|
"LF": 1,
|
|
"CF": 1,
|
|
"RF": 1,
|
|
"DH": 1,
|
|
"SP": 1,
|
|
}
|
|
|
|
# Test valid roster
|
|
complete_roster = [
|
|
{"position": "C"},
|
|
{"position": "1B"},
|
|
{"position": "2B"},
|
|
{"position": "3B"},
|
|
{"position": "SS"},
|
|
{"position": "LF"},
|
|
{"position": "CF"},
|
|
{"position": "RF"},
|
|
{"position": "DH"},
|
|
{"position": "SP"},
|
|
]
|
|
is_valid, missing = validate_position_requirements(
|
|
complete_roster, requirements
|
|
)
|
|
assert is_valid is True
|
|
assert missing == []
|
|
|
|
# Test incomplete roster
|
|
incomplete_roster = [
|
|
{"position": "C"},
|
|
{"position": "1B"},
|
|
{"position": "2B"},
|
|
{"position": "3B"},
|
|
{"position": "SS"},
|
|
{"position": "LF"},
|
|
{"position": "CF"},
|
|
{"position": "RF"},
|
|
]
|
|
is_valid, missing = validate_position_requirements(
|
|
incomplete_roster, requirements
|
|
)
|
|
assert is_valid is False
|
|
assert "DH" in missing
|
|
assert "SP" in missing
|
|
|
|
def test_gauntlet_reward_tiers(self, gauntlet_cog):
|
|
"""Test gauntlet reward tier calculation."""
|
|
|
|
def calculate_reward_tier(wins, losses):
|
|
"""Calculate reward tier based on wins/losses."""
|
|
if wins >= 8:
|
|
return "Legendary"
|
|
elif wins >= 6:
|
|
return "Gold"
|
|
elif wins >= 4:
|
|
return "Silver"
|
|
elif wins >= 2:
|
|
return "Bronze"
|
|
else:
|
|
return "Participation"
|
|
|
|
assert calculate_reward_tier(8, 2) == "Legendary"
|
|
assert calculate_reward_tier(6, 3) == "Gold"
|
|
assert calculate_reward_tier(4, 4) == "Silver"
|
|
assert calculate_reward_tier(2, 6) == "Bronze"
|
|
assert calculate_reward_tier(1, 8) == "Participation"
|
|
|
|
def test_opponent_difficulty_scaling(self, gauntlet_cog):
|
|
"""Test opponent difficulty scaling by round."""
|
|
|
|
def get_opponent_difficulty(round_number):
|
|
"""Get opponent difficulty based on round."""
|
|
if round_number <= 2:
|
|
return "Easy"
|
|
elif round_number <= 5:
|
|
return "Medium"
|
|
elif round_number <= 8:
|
|
return "Hard"
|
|
else:
|
|
return "Expert"
|
|
|
|
assert get_opponent_difficulty(1) == "Easy"
|
|
assert get_opponent_difficulty(3) == "Medium"
|
|
assert get_opponent_difficulty(6) == "Hard"
|
|
assert get_opponent_difficulty(10) == "Expert"
|
|
|
|
def test_gauntlet_statistics_tracking(self, gauntlet_cog):
|
|
"""Test gauntlet statistics tracking calculations."""
|
|
gauntlet_history = [
|
|
{"wins": 5, "losses": 3, "reward_tier": "Silver"},
|
|
{"wins": 3, "losses": 5, "reward_tier": "Bronze"},
|
|
{"wins": 7, "losses": 2, "reward_tier": "Gold"},
|
|
{"wins": 2, "losses": 6, "reward_tier": "Bronze"},
|
|
]
|
|
|
|
# Calculate overall stats
|
|
total_runs = len(gauntlet_history)
|
|
total_wins = sum(run["wins"] for run in gauntlet_history)
|
|
total_losses = sum(run["losses"] for run in gauntlet_history)
|
|
win_percentage = total_wins / (total_wins + total_losses)
|
|
|
|
# Find best run
|
|
best_run = max(gauntlet_history, key=lambda x: x["wins"])
|
|
|
|
assert total_runs == 4
|
|
assert total_wins == 17
|
|
assert total_losses == 16
|
|
assert abs(win_percentage - 0.515) < 0.01 # ~51.5%
|
|
assert best_run["wins"] == 7
|
|
assert best_run["reward_tier"] == "Gold"
|
|
|
|
def test_draft_card_sorting(self, gauntlet_cog, mock_draft_pool):
|
|
"""Test draft card sorting and filtering."""
|
|
available_cards = mock_draft_pool["available_cards"]
|
|
|
|
# Sort by cost descending
|
|
by_cost = sorted(available_cards, key=lambda x: x["cost"], reverse=True)
|
|
assert by_cost[0]["cost"] == 500 # Shohei Ohtani
|
|
|
|
# Sort by position
|
|
by_position = sorted(available_cards, key=lambda x: x["position"])
|
|
positions = [card["position"] for card in by_position]
|
|
assert positions == sorted(positions)
|
|
|
|
# Filter by rarity
|
|
legendary_cards = [
|
|
card for card in available_cards if card["rarity"] == "Legendary"
|
|
]
|
|
all_star_cards = [
|
|
card for card in available_cards if card["rarity"] == "All-Star"
|
|
]
|
|
|
|
assert len(legendary_cards) == 1
|
|
assert len(all_star_cards) == 4
|
|
assert legendary_cards[0]["player_name"] == "Shohei Ohtani"
|
|
|
|
def test_gauntlet_progress_display(self, gauntlet_cog, mock_active_gauntlet_data):
|
|
"""Test gauntlet progress display formatting."""
|
|
|
|
def format_progress_display(gauntlet_data):
|
|
"""Format gauntlet progress for display."""
|
|
progress = {
|
|
"title": f"Round {gauntlet_data['current_round']}",
|
|
"record": f"{gauntlet_data['wins']}-{gauntlet_data['losses']}",
|
|
"status": gauntlet_data["status"].title(),
|
|
"opponents_defeated": len(gauntlet_data.get("opponents_defeated", [])),
|
|
"next_opponent": gauntlet_data.get("next_opponent", "TBD"),
|
|
}
|
|
return progress
|
|
|
|
progress = format_progress_display(mock_active_gauntlet_data)
|
|
|
|
assert progress["title"] == "Round 3"
|
|
assert progress["record"] == "2-0"
|
|
assert progress["status"] == "Active"
|
|
assert progress["opponents_defeated"] == 2
|
|
assert progress["next_opponent"] == "Baltimore Orioles"
|
|
|
|
@patch("logging.getLogger")
|
|
async def test_error_handling_and_logging(self, mock_logger, gauntlet_cog):
|
|
"""Test error handling and logging for gauntlet operations."""
|
|
mock_logger_instance = Mock()
|
|
mock_logger.return_value = mock_logger_instance
|
|
|
|
# Test draft validation error
|
|
with patch("gauntlets.validate_draft") as mock_validate:
|
|
mock_validate.side_effect = ValueError("Invalid draft selection")
|
|
|
|
try:
|
|
mock_validate([])
|
|
except ValueError:
|
|
# In actual implementation, this would be caught and logged
|
|
pass
|
|
|
|
def test_permission_checks(self, gauntlet_cog, mock_interaction):
|
|
"""Test permission checking for gauntlet 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
|
|
|
|
def test_multi_position_player_handling(self, gauntlet_cog):
|
|
"""Test handling of multi-position players in draft."""
|
|
multi_pos_player = {
|
|
"card_id": 201,
|
|
"player_name": "Shohei Ohtani",
|
|
"position": "SP/DH",
|
|
"cost": 500,
|
|
}
|
|
|
|
# Test position parsing
|
|
positions = multi_pos_player["position"].split("/")
|
|
assert "SP" in positions
|
|
assert "DH" in positions
|
|
assert len(positions) == 2
|
|
|
|
# Primary position should be first
|
|
primary_position = positions[0]
|
|
assert primary_position == "SP"
|
|
|
|
def test_gauntlet_elimination_logic(self, gauntlet_cog):
|
|
"""Test gauntlet elimination conditions."""
|
|
|
|
def is_eliminated(wins, losses, max_losses=3):
|
|
"""Check if gauntlet run should be eliminated."""
|
|
return losses >= max_losses
|
|
|
|
def is_completed(wins, losses, max_wins=8):
|
|
"""Check if gauntlet run is completed successfully."""
|
|
return wins >= max_wins
|
|
|
|
# Test active runs
|
|
assert not is_eliminated(5, 2)
|
|
assert not is_completed(5, 2)
|
|
|
|
# Test elimination
|
|
assert is_eliminated(3, 3)
|
|
assert is_eliminated(0, 3)
|
|
|
|
# Test completion
|
|
assert is_completed(8, 0)
|
|
assert is_completed(8, 2)
|
|
|
|
# Edge cases
|
|
assert not is_eliminated(7, 2) # Still active
|
|
assert is_completed(8, 3) # Completed despite losses
|