# SPDX-FileCopyrightText: 2025 2025 # SPDX-FileContributor: Gergely Polonkai # # SPDX-License-Identifier: GPL-3.0-or-later """Tests for the dice roller""" import pytest from pytest_mock import MockerFixture from gm_assistant.dice import Die def test_parse_empty() -> None: """Test parsing an empty string""" with pytest.raises(ValueError): Die.parse("") with pytest.raises(ValueError): Die.parse(" ") @pytest.mark.parametrize("descriptor", [pytest.param("12", id="nodie")]) def test_invalid(descriptor: str) -> None: """Test parsing invalid descriptors""" with pytest.raises(ValueError): Die.parse(descriptor) @pytest.mark.parametrize( "descriptor,expected", [ pytest.param("d6", ([6], [], 0), id="single"), pytest.param("2d6", ([6, 6], [], 0), id="multiple"), pytest.param("d12 + 1", ([12], [], 1), id="modifiedsingle"), pytest.param("3d12 - 2", ([12, 12, 12], [], -2), id="modifiedmultiple"), pytest.param("2d6+3d8-d4+1", ([6, 6, 8, 8, 8], [4], 1), id="complex"), ], ) def test_valid(descriptor: str, expected: tuple[list[int], list[int], int]) -> None: """Test parsing valid descriptors""" results = Die.parse(descriptor) assert results == expected @pytest.mark.parametrize( "descriptor,expected", [ pytest.param("d6", 1, id="single"), pytest.param("2d6", 2, id="multiple"), pytest.param("d12 + 1", 2, id="modifiedsingle"), pytest.param("3d12 - 2", 1, id="modifiedmultiple"), pytest.param("2d6+3d8-d4+1", 2, id="complex"), ], ) def test_minimum(descriptor: str, expected: int) -> None: """Test the ``minimum`` property""" assert Die(descriptor).minimum == expected @pytest.mark.parametrize( "descriptor,expected", [ pytest.param("d6", 6, id="single"), pytest.param("2d6", 12, id="multiple"), pytest.param("d12 + 1", 13, id="modifiedsingle"), pytest.param("3d12 - 2", 34, id="modifiedmultiple"), pytest.param("2d6+3d8-d4+1", 36, id="complex"), ], ) def test_maximum(descriptor: str, expected: int) -> None: """Test the ``maxiimum`` property""" assert Die(descriptor).maximum == expected def test_roll_single(mocker: MockerFixture) -> None: """Test rolling a single die""" mocked_randint = mocker.patch("gm_assistant.dice.random.randint", return_value=3) die = Die("d6") assert die.roll() == 3 mocked_randint.assert_called_once_with(1, 6) def test_roll_multiple(mocker: MockerFixture) -> None: """Test rolling multiple of the same die""" mocked_randint = mocker.patch("gm_assistant.dice.random.randint", side_effect=[3, 2]) die = Die("2d6") assert die.roll() == 5 assert mocked_randint.call_count == 2 mocked_randint.assert_has_calls([mocker.call(1, 6), mocker.call(1, 6)]) def test_roll_modified_single(mocker: MockerFixture) -> None: """Test rolling a single dice with a modifier""" mocked_randint = mocker.patch("gm_assistant.dice.random.randint", return_value=3) die = Die("d12+1") assert die.roll() == 4 mocked_randint.assert_called_once_with(1, 12) def test_roll_modified_multiple(mocker: MockerFixture) -> None: """Test rolling multiple of the same die with a modifier""" mocked_randint = mocker.patch("gm_assistant.dice.random.randint", side_effect=[3, 2]) die = Die("2d12-1") assert die.roll() == 4 assert mocked_randint.call_count == 2 mocked_randint.assert_has_calls([mocker.call(1, 12), mocker.call(1, 12)]) def test_roll_complex(mocker: MockerFixture) -> None: """Test rolling a complex die heap""" mocked_randint = mocker.patch("gm_assistant.dice.random.randint", side_effect=[3, 2, 5, 8, 1, 2]) die = Die("2d6-1+3d8-d4+2") assert die.roll() == 18 assert mocked_randint.call_count == 6 mocked_randint.assert_has_calls( [ mocker.call(1, 6), mocker.call(1, 6), mocker.call(1, 8), mocker.call(1, 8), mocker.call(1, 8), mocker.call(1, 4), ] )