181 lines
5.5 KiB
Python
181 lines
5.5 KiB
Python
# 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]
|