Implements comprehensive dice rolling system for gameplay: ## New Features - `/roll` and `!roll` commands for XdY dice notation with multiple roll support - `/ab` and `!atbat` commands for baseball at-bat dice shortcuts (1d6;2d6;1d20) - `/fielding` and `!f` commands for Super Advanced fielding with full position charts ## Technical Implementation - Complete dice command package in commands/dice/ - Full range and error charts for all 8 defensive positions (1B,2B,3B,SS,LF,RF,CF,C) - Pre-populated position choices for user-friendly slash command interface - Backwards compatibility with prefix commands (!roll, !r, !dice, !ab, !atbat, !f, !fielding, !saf) - Type-safe implementation following "Raise or Return" pattern ## Testing & Quality - 30 comprehensive tests with 100% pass rate - Complete test coverage for all dice functionality, parsing, validation, and error handling - Integration with bot.py command loading system - Maintainable data structures replacing verbose original implementation ## User Experience - Consistent embed formatting across all commands - Detailed fielding results with range and error analysis - Support for complex dice combinations and multiple roll formats - Clear error messages for invalid inputs 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
554 lines
21 KiB
Python
554 lines
21 KiB
Python
"""
|
|
Tests for dice rolling commands
|
|
|
|
Validates dice rolling functionality, parsing, and embed creation.
|
|
"""
|
|
import pytest
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
import discord
|
|
from discord.ext import commands
|
|
|
|
from commands.dice.rolls import DiceRollCommands
|
|
|
|
|
|
class TestDiceRollCommands:
|
|
"""Test dice rolling command functionality."""
|
|
|
|
@pytest.fixture
|
|
def bot(self):
|
|
"""Create a mock bot instance."""
|
|
bot = AsyncMock(spec=commands.Bot)
|
|
return bot
|
|
|
|
@pytest.fixture
|
|
def dice_cog(self, bot):
|
|
"""Create DiceRollCommands cog instance."""
|
|
return DiceRollCommands(bot)
|
|
|
|
@pytest.fixture
|
|
def mock_interaction(self):
|
|
"""Create a mock Discord interaction."""
|
|
interaction = AsyncMock(spec=discord.Interaction)
|
|
|
|
# Mock the user
|
|
user = MagicMock(spec=discord.User)
|
|
user.display_name = "TestUser"
|
|
user.display_avatar.url = "https://example.com/avatar.png"
|
|
interaction.user = user
|
|
|
|
# Mock response methods
|
|
interaction.response.defer = AsyncMock()
|
|
interaction.followup.send = AsyncMock()
|
|
|
|
return interaction
|
|
|
|
@pytest.fixture
|
|
def mock_context(self):
|
|
"""Create a mock Discord context for prefix commands."""
|
|
ctx = AsyncMock(spec=commands.Context)
|
|
|
|
# Mock the author (user)
|
|
author = MagicMock(spec=discord.User)
|
|
author.display_name = "TestUser"
|
|
author.display_avatar.url = "https://example.com/avatar.png"
|
|
author.id = 12345 # Add user ID
|
|
ctx.author = author
|
|
|
|
# Mock send method
|
|
ctx.send = AsyncMock()
|
|
|
|
return ctx
|
|
|
|
def test_parse_valid_dice_notation(self, dice_cog):
|
|
"""Test parsing valid dice notation."""
|
|
# Test basic notation
|
|
results = dice_cog._parse_and_roll_multiple_dice("2d6")
|
|
assert len(results) == 1
|
|
result = results[0]
|
|
assert result['num_dice'] == 2
|
|
assert result['die_sides'] == 6
|
|
assert len(result['rolls']) == 2
|
|
assert all(1 <= roll <= 6 for roll in result['rolls'])
|
|
assert result['total'] == sum(result['rolls'])
|
|
|
|
# Test single die
|
|
results = dice_cog._parse_and_roll_multiple_dice("1d20")
|
|
assert len(results) == 1
|
|
result = results[0]
|
|
assert result['num_dice'] == 1
|
|
assert result['die_sides'] == 20
|
|
assert len(result['rolls']) == 1
|
|
assert 1 <= result['rolls'][0] <= 20
|
|
|
|
def test_parse_invalid_dice_notation(self, dice_cog):
|
|
"""Test parsing invalid dice notation."""
|
|
# Invalid formats
|
|
assert dice_cog._parse_and_roll_multiple_dice("invalid") == []
|
|
assert dice_cog._parse_and_roll_multiple_dice("2d") == []
|
|
assert dice_cog._parse_and_roll_multiple_dice("d6") == []
|
|
assert dice_cog._parse_and_roll_multiple_dice("2d6+5") == [] # No modifiers in simple version
|
|
assert dice_cog._parse_and_roll_multiple_dice("") == []
|
|
|
|
# Out of bounds values
|
|
assert dice_cog._parse_and_roll_multiple_dice("0d6") == [] # num_dice < 1
|
|
assert dice_cog._parse_and_roll_multiple_dice("2d1") == [] # die_sides < 2
|
|
assert dice_cog._parse_and_roll_multiple_dice("101d6") == [] # num_dice > 100
|
|
assert dice_cog._parse_and_roll_multiple_dice("1d1001") == [] # die_sides > 1000
|
|
|
|
def test_parse_multiple_dice(self, dice_cog):
|
|
"""Test parsing multiple dice rolls."""
|
|
# Test multiple rolls
|
|
results = dice_cog._parse_and_roll_multiple_dice("1d6;2d8;1d20")
|
|
assert len(results) == 3
|
|
|
|
assert results[0]['dice_notation'] == '1d6'
|
|
assert results[0]['num_dice'] == 1
|
|
assert results[0]['die_sides'] == 6
|
|
|
|
assert results[1]['dice_notation'] == '2d8'
|
|
assert results[1]['num_dice'] == 2
|
|
assert results[1]['die_sides'] == 8
|
|
|
|
assert results[2]['dice_notation'] == '1d20'
|
|
assert results[2]['num_dice'] == 1
|
|
assert results[2]['die_sides'] == 20
|
|
|
|
def test_parse_case_insensitive(self, dice_cog):
|
|
"""Test that dice notation parsing is case insensitive."""
|
|
result_lower = dice_cog._parse_and_roll_multiple_dice("2d6")
|
|
result_upper = dice_cog._parse_and_roll_multiple_dice("2D6")
|
|
|
|
assert len(result_lower) == 1
|
|
assert len(result_upper) == 1
|
|
assert result_lower[0]['num_dice'] == result_upper[0]['num_dice']
|
|
assert result_lower[0]['die_sides'] == result_upper[0]['die_sides']
|
|
|
|
def test_parse_whitespace_handling(self, dice_cog):
|
|
"""Test that whitespace is handled properly."""
|
|
results = dice_cog._parse_and_roll_multiple_dice(" 2d6 ")
|
|
assert len(results) == 1
|
|
assert results[0]['num_dice'] == 2
|
|
assert results[0]['die_sides'] == 6
|
|
|
|
results = dice_cog._parse_and_roll_multiple_dice("2 d 6")
|
|
assert len(results) == 1
|
|
assert results[0]['num_dice'] == 2
|
|
assert results[0]['die_sides'] == 6
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_roll_dice_valid_input(self, dice_cog, mock_interaction):
|
|
"""Test roll_dice command with valid input."""
|
|
await dice_cog.roll_dice.callback(dice_cog, mock_interaction, "2d6")
|
|
|
|
# Verify response was deferred
|
|
mock_interaction.response.defer.assert_called_once()
|
|
|
|
# Verify followup was sent with embed
|
|
mock_interaction.followup.send.assert_called_once()
|
|
call_args = mock_interaction.followup.send.call_args
|
|
assert 'embed' in call_args.kwargs
|
|
|
|
# Verify embed is a Discord embed
|
|
embed = call_args.kwargs['embed']
|
|
assert isinstance(embed, discord.Embed)
|
|
assert embed.title == "🎲 Dice Roll"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_roll_dice_invalid_input(self, dice_cog, mock_interaction):
|
|
"""Test roll_dice command with invalid input."""
|
|
await dice_cog.roll_dice.callback(dice_cog, mock_interaction, "invalid")
|
|
|
|
# Verify response was deferred
|
|
mock_interaction.response.defer.assert_called_once()
|
|
|
|
# Verify error message was sent
|
|
mock_interaction.followup.send.assert_called_once()
|
|
call_args = mock_interaction.followup.send.call_args
|
|
assert "Invalid dice notation" in call_args.args[0]
|
|
assert call_args.kwargs['ephemeral'] is True
|
|
|
|
def test_create_multi_roll_embed_single_die(self, dice_cog, mock_interaction):
|
|
"""Test embed creation for single die roll."""
|
|
roll_results = [
|
|
{
|
|
'dice_notation': '1d20',
|
|
'num_dice': 1,
|
|
'die_sides': 20,
|
|
'rolls': [15],
|
|
'total': 15
|
|
}
|
|
]
|
|
|
|
embed = dice_cog._create_multi_roll_embed("1d20", roll_results, mock_interaction.user)
|
|
|
|
assert embed.title == "🎲 Dice Roll"
|
|
assert embed.author.name == "TestUser"
|
|
assert embed.author.icon_url == "https://example.com/avatar.png"
|
|
|
|
# Check the formatted field content
|
|
assert len(embed.fields) == 1
|
|
assert embed.fields[0].name == 'Result'
|
|
expected_value = "```md\n# 15\nDetails:[1d20 (15)]```"
|
|
assert embed.fields[0].value == expected_value
|
|
|
|
def test_create_multi_roll_embed_multiple_dice(self, dice_cog, mock_interaction):
|
|
"""Test embed creation for multiple dice rolls."""
|
|
roll_results = [
|
|
{
|
|
'dice_notation': '1d6',
|
|
'num_dice': 1,
|
|
'die_sides': 6,
|
|
'rolls': [5],
|
|
'total': 5
|
|
},
|
|
{
|
|
'dice_notation': '2d6',
|
|
'num_dice': 2,
|
|
'die_sides': 6,
|
|
'rolls': [5, 6],
|
|
'total': 11
|
|
},
|
|
{
|
|
'dice_notation': '1d20',
|
|
'num_dice': 1,
|
|
'die_sides': 20,
|
|
'rolls': [13],
|
|
'total': 13
|
|
}
|
|
]
|
|
|
|
embed = dice_cog._create_multi_roll_embed("1d6;2d6;1d20", roll_results, mock_interaction.user)
|
|
|
|
assert embed.title == "🎲 Dice Roll"
|
|
assert embed.author.name == "TestUser"
|
|
|
|
# Check the formatted field content matches the expected format
|
|
assert len(embed.fields) == 1
|
|
assert embed.fields[0].name == 'Result'
|
|
expected_value = "```md\n# 5,11,13\nDetails:[1d6;2d6;1d20 (5 - 5 6 - 13)]```"
|
|
assert embed.fields[0].value == expected_value
|
|
|
|
def test_dice_roll_randomness(self, dice_cog):
|
|
"""Test that dice rolls produce different results."""
|
|
results = []
|
|
for _ in range(20): # Roll 20 times
|
|
result = dice_cog._parse_and_roll_multiple_dice("1d20")
|
|
results.append(result[0]['rolls'][0])
|
|
|
|
# Should have some variation in results (very unlikely all 20 rolls are the same)
|
|
unique_results = set(results)
|
|
assert len(unique_results) > 1, f"All rolls were the same: {results}"
|
|
|
|
def test_dice_boundaries(self, dice_cog):
|
|
"""Test dice rolling at boundaries."""
|
|
# Test maximum allowed dice
|
|
results = dice_cog._parse_and_roll_multiple_dice("100d2")
|
|
assert len(results) == 1
|
|
result = results[0]
|
|
assert len(result['rolls']) == 100
|
|
assert all(roll in [1, 2] for roll in result['rolls'])
|
|
|
|
# Test maximum die size
|
|
results = dice_cog._parse_and_roll_multiple_dice("1d1000")
|
|
assert len(results) == 1
|
|
result = results[0]
|
|
assert 1 <= result['rolls'][0] <= 1000
|
|
|
|
# Test minimum valid values
|
|
results = dice_cog._parse_and_roll_multiple_dice("1d2")
|
|
assert len(results) == 1
|
|
result = results[0]
|
|
assert result['rolls'][0] in [1, 2]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_prefix_command_valid_input(self, dice_cog, mock_context):
|
|
"""Test prefix command with valid input."""
|
|
await dice_cog.roll_dice_prefix.callback(dice_cog, mock_context, dice="2d6")
|
|
|
|
# Verify send was called with embed
|
|
mock_context.send.assert_called_once()
|
|
call_args = mock_context.send.call_args
|
|
# Check if embed was passed as positional or keyword argument
|
|
if call_args.args:
|
|
embed = call_args.args[0]
|
|
else:
|
|
embed = call_args.kwargs.get('embed')
|
|
assert isinstance(embed, discord.Embed)
|
|
assert embed.title == "🎲 Dice Roll"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_prefix_command_invalid_input(self, dice_cog, mock_context):
|
|
"""Test prefix command with invalid input."""
|
|
await dice_cog.roll_dice_prefix.callback(dice_cog, mock_context, dice="invalid")
|
|
|
|
# Verify error message was sent
|
|
mock_context.send.assert_called_once()
|
|
call_args = mock_context.send.call_args
|
|
error_msg = call_args[0][0]
|
|
assert "Invalid dice notation" in error_msg
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_prefix_command_no_input(self, dice_cog, mock_context):
|
|
"""Test prefix command with no input."""
|
|
await dice_cog.roll_dice_prefix.callback(dice_cog, mock_context, dice=None)
|
|
|
|
# Verify usage message was sent
|
|
mock_context.send.assert_called_once()
|
|
call_args = mock_context.send.call_args
|
|
usage_msg = call_args[0][0]
|
|
assert "Please provide dice notation" in usage_msg
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_prefix_command_multiple_dice(self, dice_cog, mock_context):
|
|
"""Test prefix command with multiple dice rolls."""
|
|
await dice_cog.roll_dice_prefix.callback(dice_cog, mock_context, dice="1d6;2d8;1d20")
|
|
|
|
# Verify send was called with embed
|
|
mock_context.send.assert_called_once()
|
|
call_args = mock_context.send.call_args
|
|
# Check if embed was passed as positional or keyword argument
|
|
if call_args.args:
|
|
embed = call_args.args[0]
|
|
else:
|
|
embed = call_args.kwargs.get('embed')
|
|
|
|
assert isinstance(embed, discord.Embed)
|
|
assert embed.title == "🎲 Dice Roll"
|
|
# Should have summary format with 3 totals in field
|
|
assert len(embed.fields) == 1
|
|
assert embed.fields[0].name == 'Result'
|
|
assert embed.fields[0].value.startswith("```md\n#")
|
|
assert "Details:[1d6;2d8;1d20" in embed.fields[0].value
|
|
|
|
def test_prefix_command_attributes(self, dice_cog):
|
|
"""Test that prefix command has correct attributes."""
|
|
# Check command exists and has correct name
|
|
assert hasattr(dice_cog, 'roll_dice_prefix')
|
|
command = dice_cog.roll_dice_prefix
|
|
assert command.name == "roll"
|
|
assert command.aliases == ["r", "dice"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_ab_command_slash(self, dice_cog, mock_interaction):
|
|
"""Test ab slash command."""
|
|
await dice_cog.ab_dice.callback(dice_cog, mock_interaction)
|
|
|
|
# Verify response was deferred
|
|
mock_interaction.response.defer.assert_called_once()
|
|
|
|
# Verify followup was sent with embed
|
|
mock_interaction.followup.send.assert_called_once()
|
|
call_args = mock_interaction.followup.send.call_args
|
|
assert 'embed' in call_args.kwargs
|
|
|
|
# Verify embed has the correct format
|
|
embed = call_args.kwargs['embed']
|
|
assert isinstance(embed, discord.Embed)
|
|
assert embed.title == "At bat roll for TestUser"
|
|
assert len(embed.fields) == 1
|
|
assert "Details:[1d6;2d6;1d20" in embed.fields[0].value
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_ab_command_prefix(self, dice_cog, mock_context):
|
|
"""Test ab prefix command."""
|
|
await dice_cog.ab_dice_prefix.callback(dice_cog, mock_context)
|
|
|
|
# Verify send was called with embed
|
|
mock_context.send.assert_called_once()
|
|
call_args = mock_context.send.call_args
|
|
|
|
# Check if embed was passed as positional or keyword argument
|
|
if call_args.args:
|
|
embed = call_args.args[0]
|
|
else:
|
|
embed = call_args.kwargs.get('embed')
|
|
|
|
assert isinstance(embed, discord.Embed)
|
|
assert embed.title == "At bat roll for TestUser"
|
|
assert len(embed.fields) == 1
|
|
assert "Details:[1d6;2d6;1d20" in embed.fields[0].value
|
|
|
|
def test_ab_command_attributes(self, dice_cog):
|
|
"""Test that ab prefix command has correct attributes."""
|
|
# Check command exists and has correct name
|
|
assert hasattr(dice_cog, 'ab_dice_prefix')
|
|
command = dice_cog.ab_dice_prefix
|
|
assert command.name == "ab"
|
|
assert command.aliases == ["atbat"]
|
|
|
|
def test_ab_command_dice_combination(self, dice_cog):
|
|
"""Test that ab command uses the correct dice combination."""
|
|
dice_notation = "1d6;2d6;1d20"
|
|
results = dice_cog._parse_and_roll_multiple_dice(dice_notation)
|
|
|
|
# Should have 3 dice groups
|
|
assert len(results) == 3
|
|
|
|
# Check each dice type
|
|
assert results[0]['dice_notation'] == '1d6'
|
|
assert results[0]['num_dice'] == 1
|
|
assert results[0]['die_sides'] == 6
|
|
|
|
assert results[1]['dice_notation'] == '2d6'
|
|
assert results[1]['num_dice'] == 2
|
|
assert results[1]['die_sides'] == 6
|
|
|
|
assert results[2]['dice_notation'] == '1d20'
|
|
assert results[2]['num_dice'] == 1
|
|
assert results[2]['die_sides'] == 20
|
|
|
|
# Fielding command tests
|
|
@pytest.mark.asyncio
|
|
async def test_fielding_command_slash(self, dice_cog, mock_interaction):
|
|
"""Test fielding slash command with valid position."""
|
|
# Mock a position choice
|
|
position_choice = MagicMock()
|
|
position_choice.value = '3B'
|
|
|
|
await dice_cog.fielding_roll.callback(dice_cog, mock_interaction, position_choice)
|
|
|
|
# Verify response was deferred
|
|
mock_interaction.response.defer.assert_called_once()
|
|
|
|
# Verify followup was sent with embed
|
|
mock_interaction.followup.send.assert_called_once()
|
|
call_args = mock_interaction.followup.send.call_args
|
|
assert 'embed' in call_args.kwargs
|
|
|
|
# Verify embed has the correct format
|
|
embed = call_args.kwargs['embed']
|
|
assert isinstance(embed, discord.Embed)
|
|
assert embed.title == "SA Fielding roll for TestUser"
|
|
assert len(embed.fields) >= 2 # Range and Error fields
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_fielding_command_prefix_valid(self, dice_cog, mock_context):
|
|
"""Test fielding prefix command with valid position."""
|
|
await dice_cog.fielding_roll_prefix.callback(dice_cog, mock_context, "SS")
|
|
|
|
# Verify send was called with embed
|
|
mock_context.send.assert_called_once()
|
|
call_args = mock_context.send.call_args
|
|
|
|
# Check if embed was passed as positional or keyword argument
|
|
if call_args.args:
|
|
embed = call_args.args[0]
|
|
else:
|
|
embed = call_args.kwargs.get('embed')
|
|
|
|
assert isinstance(embed, discord.Embed)
|
|
assert embed.title == "SA Fielding roll for TestUser"
|
|
assert len(embed.fields) >= 2 # Range and Error fields
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_fielding_command_prefix_no_position(self, dice_cog, mock_context):
|
|
"""Test fielding prefix command with no position."""
|
|
await dice_cog.fielding_roll_prefix.callback(dice_cog, mock_context, None)
|
|
|
|
# Verify error message was sent
|
|
mock_context.send.assert_called_once()
|
|
call_args = mock_context.send.call_args
|
|
error_msg = call_args[0][0]
|
|
assert "Please specify a position" in error_msg
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_fielding_command_prefix_invalid_position(self, dice_cog, mock_context):
|
|
"""Test fielding prefix command with invalid position."""
|
|
await dice_cog.fielding_roll_prefix.callback(dice_cog, mock_context, "INVALID")
|
|
|
|
# Verify error message was sent
|
|
mock_context.send.assert_called_once()
|
|
call_args = mock_context.send.call_args
|
|
error_msg = call_args[0][0]
|
|
assert "Invalid position" in error_msg
|
|
|
|
def test_fielding_command_attributes(self, dice_cog):
|
|
"""Test that fielding prefix command has correct attributes."""
|
|
# Check command exists and has correct name
|
|
assert hasattr(dice_cog, 'fielding_roll_prefix')
|
|
command = dice_cog.fielding_roll_prefix
|
|
assert command.name == "f"
|
|
assert command.aliases == ["fielding", "saf"]
|
|
|
|
def test_fielding_range_charts(self, dice_cog):
|
|
"""Test that fielding range charts work for all positions."""
|
|
# Test infield range (applies to 1B, 2B, 3B, SS)
|
|
infield_result = dice_cog._get_infield_range(10)
|
|
assert isinstance(infield_result, str)
|
|
assert len(infield_result) > 0
|
|
|
|
# Test outfield range (applies to LF, CF, RF)
|
|
outfield_result = dice_cog._get_outfield_range(10)
|
|
assert isinstance(outfield_result, str)
|
|
assert len(outfield_result) > 0
|
|
|
|
# Test catcher range
|
|
catcher_result = dice_cog._get_catcher_range(10)
|
|
assert isinstance(catcher_result, str)
|
|
assert len(catcher_result) > 0
|
|
|
|
def test_fielding_error_charts(self, dice_cog):
|
|
"""Test that error charts work for all positions."""
|
|
# Test all position error methods
|
|
test_total = 10
|
|
|
|
# Test 1B error
|
|
error_1b = dice_cog._get_1b_error(test_total)
|
|
assert isinstance(error_1b, str)
|
|
|
|
# Test 2B error
|
|
error_2b = dice_cog._get_2b_error(test_total)
|
|
assert isinstance(error_2b, str)
|
|
|
|
# Test 3B error
|
|
error_3b = dice_cog._get_3b_error(test_total)
|
|
assert isinstance(error_3b, str)
|
|
|
|
# Test SS error
|
|
error_ss = dice_cog._get_ss_error(test_total)
|
|
assert isinstance(error_ss, str)
|
|
|
|
# Test corner OF error
|
|
error_corner = dice_cog._get_corner_of_error(test_total)
|
|
assert isinstance(error_corner, str)
|
|
|
|
# Test CF error
|
|
error_cf = dice_cog._get_cf_error(test_total)
|
|
assert isinstance(error_cf, str)
|
|
|
|
# Test catcher error
|
|
error_catcher = dice_cog._get_catcher_error(test_total)
|
|
assert isinstance(error_catcher, str)
|
|
|
|
def test_get_error_result_all_positions(self, dice_cog):
|
|
"""Test _get_error_result for all valid positions."""
|
|
test_total = 12
|
|
positions = ['1B', '2B', '3B', 'SS', 'LF', 'RF', 'CF', 'C']
|
|
|
|
for position in positions:
|
|
result = dice_cog._get_error_result(position, test_total)
|
|
assert isinstance(result, str)
|
|
assert len(result) > 0
|
|
|
|
def test_get_error_result_invalid_position(self, dice_cog):
|
|
"""Test _get_error_result with invalid position raises error."""
|
|
with pytest.raises(ValueError, match="Unknown position"):
|
|
dice_cog._get_error_result("INVALID", 10)
|
|
|
|
def test_fielding_dice_combination(self, dice_cog):
|
|
"""Test that fielding uses correct dice combination (1d20;3d6)."""
|
|
dice_notation = "1d20;3d6"
|
|
results = dice_cog._parse_and_roll_multiple_dice(dice_notation)
|
|
|
|
# Should have 2 dice groups
|
|
assert len(results) == 2
|
|
|
|
# Check 1d20
|
|
assert results[0]['dice_notation'] == '1d20'
|
|
assert results[0]['num_dice'] == 1
|
|
assert results[0]['die_sides'] == 20
|
|
|
|
# Check 3d6
|
|
assert results[1]['dice_notation'] == '3d6'
|
|
assert results[1]['num_dice'] == 3
|
|
assert results[1]['die_sides'] == 6 |