From d556159f2a16df95d947df27174ec5073e7acb65 Mon Sep 17 00:00:00 2001 From: Gergely Polonkai Date: Wed, 21 May 2025 23:05:42 +0200 Subject: [PATCH] feat: Create the Random Choice Oracle --- gm_assistant/oracle/random_choice.py | 38 ++++++++++++++++ tests/test_random_choice_oracle.py | 65 ++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 gm_assistant/oracle/random_choice.py create mode 100644 tests/test_random_choice_oracle.py diff --git a/gm_assistant/oracle/random_choice.py b/gm_assistant/oracle/random_choice.py new file mode 100644 index 0000000..0c8d668 --- /dev/null +++ b/gm_assistant/oracle/random_choice.py @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: 2025 2025 +# SPDX-FileContributor: Gergely Polonkai +# +# SPDX-License-Identifier: GPL-3.0-or-later +"""Random Choice Oracle""" + +import random +from typing import Any + +from .base import Oracle + + +class RandomChoiceOracle(Oracle): + """An Oracle that can choose randomly from a list of possibilities""" + + TYPE_MARKER = "random-choice" + + def __init__(self, oracle_data: dict[str, Any]) -> None: + self.choices: list[str] | None = None + + super().__init__(oracle_data) + + def parse_and_validate(self, oracle_data: dict[str, Any]) -> None: + super().parse_and_validate(oracle_data) + + if "choices" not in oracle_data: + raise KeyError("choices is missing from the data") + + if not oracle_data["choices"]: + raise ValueError("Choice list cannot be empty") + + if not isinstance(oracle_data["choices"], list): + raise TypeError("choices must be a list") + + self.choices = oracle_data["choices"] + + def generate(self) -> str: + return random.choice(self.choices or []) diff --git a/tests/test_random_choice_oracle.py b/tests/test_random_choice_oracle.py new file mode 100644 index 0000000..fc2b0d9 --- /dev/null +++ b/tests/test_random_choice_oracle.py @@ -0,0 +1,65 @@ +# SPDX-FileCopyrightText: 2025 2025 +# SPDX-FileContributor: Gergely Polonkai +# +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tests for the Random Choice Oracle""" + +import pytest +from pytest_mock import MockerFixture + +from gm_assistant.oracle.random_choice import RandomChoiceOracle + +TEST_DATA = { + "type": "random-choice", + "name": "Test Oracle", + "source": "Test Source", + "choices": ["One", "Two", "Three"], +} + + +def test_init_missing_choices() -> None: + """Test initalising with missing choices""" + + oracle_data = TEST_DATA.copy() + del oracle_data["choices"] + + with pytest.raises(KeyError): + RandomChoiceOracle(oracle_data) + + +def test_init_empty_choices() -> None: + """Test initialising with an empty choice list""" + + oracle_data = TEST_DATA.copy() + oracle_data["choices"] = [] + + with pytest.raises(ValueError): + RandomChoiceOracle(oracle_data) + + +def test_init_nonlist_choices() -> None: + """Test initialising with a choice list that is not a list""" + + oracle_data = TEST_DATA.copy() + oracle_data["choices"] = {"a": "b"} # type: ignore[assignment] + + with pytest.raises(TypeError): + RandomChoiceOracle(oracle_data) + + +def test_init() -> None: + """Test initialisation""" + + oracle = RandomChoiceOracle(TEST_DATA) + + assert oracle.choices == ["One", "Two", "Three"] + + +def test_generate(mocker: MockerFixture) -> None: + """Test generate()""" + + mocked_choice = mocker.patch("gm_assistant.oracle.random_choice.random.choice", return_value="Result") + oracle = RandomChoiceOracle(TEST_DATA) + + assert oracle.generate() == "Result" + mocked_choice.assert_called_once_with(["One", "Two", "Three"])