# SPDX-FileCopyrightText: 2025 2025 # SPDX-FileContributor: Gergely Polonkai # # SPDX-License-Identifier: GPL-3.0-or-later """Tests for the Object Generator Oracle""" from copy import deepcopy from typing import Literal import pytest from pytest_mock import MockerFixture from gm_assistant.dice import Die from gm_assistant.oracle.object_generator import ObjectGeneratorOracle, ObjectGeneratorProperty TEST_PROPERTY_DATA = { "question": "Test Question", "roll-type": "d6", "results": [ {"min-roll": 1, "max-roll": 2, "value": "Value 1-2"}, {"min-roll": 3, "value": "Value 3"}, {"min-roll": 4, "max-roll": 6, "value": "Value 4-6"}, ], } TEST_ORACLE_DATA = { "name": "Test Oracle", "source": "Test Source", "type": "object-generator", "properties": [ TEST_PROPERTY_DATA, { "question": "Second Question", "roll-type": "d2", "results": [ {"min-roll": 1, "value": "Value 1"}, {"min-roll": 2, "value": "Value 2"}, ], }, ], } @pytest.mark.parametrize("missing", ["question", "roll-type", "results", "min-roll", "value"]) def test_property_missing_data(missing: Literal["question", "roll-type", "results", "min-roll", "value"]) -> None: """Test initialising a property with missing data""" prop_data = deepcopy(TEST_PROPERTY_DATA) if missing in ("min-roll", "value"): del prop_data["results"][0][missing] # type: ignore[attr-defined] else: del prop_data[missing] with pytest.raises(ValueError) as ctx: ObjectGeneratorProperty(prop_data) if missing in ("min-roll", "value"): assert str(ctx.value) == f"Missing {missing} value in result 0" else: assert str(ctx.value) == f"Missing {missing} value" def test_property_result_max_smaller_than_min() -> None: """Test initialising a property with a maximum roll smaller than the minimum roll""" prop_data = deepcopy(TEST_PROPERTY_DATA) prop_data["results"][0]["min-roll"] = 3 # type: ignore[index] with pytest.raises(ValueError) as ctx: ObjectGeneratorProperty(prop_data) assert str(ctx.value) == "max-roll cannot be smaller than min-roll in result 0" def test_property_missing_roll_value() -> None: """Test initialising a property with a missing roll value""" prop_data = deepcopy(TEST_PROPERTY_DATA) prop_data["results"] = [prop_data["results"][0]] with pytest.raises(ValueError) as ctx: ObjectGeneratorProperty(prop_data) assert str(ctx.value) == "Not all roll values yield a result for property Test Question" def test_property_multiple_roll_value() -> None: """Test initialising a property with a roll value appearing twice""" prop_data = deepcopy(TEST_PROPERTY_DATA) prop_data["results"].append({"min-roll": 2, "value": "Second Value"}) # type: ignore[attr-defined] with pytest.raises(ValueError) as ctx: ObjectGeneratorProperty(prop_data) assert str(ctx.value) == "Roll value 2 is already registered in result 3" def test_property_init(mocker: MockerFixture) -> None: """Test initialising a property with valid data""" mocked_die = Die("d6") mocked_die_class = mocker.patch("gm_assistant.oracle.object_generator.Die", return_value=mocked_die) prop = ObjectGeneratorProperty(TEST_PROPERTY_DATA) assert prop.question == "Test Question" assert prop.die == mocked_die assert prop.results == { 1: "Value 1-2", 2: "Value 1-2", 3: "Value 3", 4: "Value 4-6", 5: "Value 4-6", 6: "Value 4-6", } mocked_die_class.assert_called_once_with("d6") def test_property_generate(mocker: MockerFixture) -> None: """Test the ObjectGeneratorProperty.generate() method""" mocked_roll = mocker.patch("gm_assistant.oracle.object_generator.Die.roll", return_value=3) prop = ObjectGeneratorProperty(TEST_PROPERTY_DATA) expected_output = """*Test Question* Value 3""" assert prop.generate() == expected_output mocked_roll.assert_called_once_with() def test_oracle_init_missing_properties() -> None: """Test initialising an Oracle with properties missing""" oracle_data = deepcopy(TEST_ORACLE_DATA) del oracle_data["properties"] with pytest.raises(KeyError) as ctx: ObjectGeneratorOracle(oracle_data) assert str(ctx.value) == "'properties'" def test_oracle_init() -> None: """Test initialising an Oracle with valid data""" oracle = ObjectGeneratorOracle(TEST_ORACLE_DATA) assert oracle.properties[0].question == "Test Question" assert oracle.properties[1].question == "Second Question" def test_oracle_generate(mocker: MockerFixture) -> None: """Test the Oracle’s ``generate`` method""" mocked_die1 = Die("d6") mocker.patch.object(mocked_die1, "roll", return_value=5) mocked_die2 = Die("d2") mocker.patch.object(mocked_die2, "roll", return_value=1) mocked_die_class = mocker.patch("gm_assistant.oracle.object_generator.Die", side_effect=[mocked_die1, mocked_die2]) oracle = ObjectGeneratorOracle(TEST_ORACLE_DATA) expected_output = """*Test Question* Value 4-6 *Second Question* Value 1""" assert oracle.generate() == expected_output assert mocked_die_class.call_count == 2 mocked_die_class.assert_has_calls([mocker.call("d6"), mocker.call("d2")]) # pylint: disable=no-member mocked_die1.roll.assert_called_once_with() # type: ignore[attr-defined] mocked_die2.roll.assert_called_once_with() # type: ignore[attr-defined]