diff --git a/docs/code.md b/docs/code.md index f51c270..227e0a3 100644 --- a/docs/code.md +++ b/docs/code.md @@ -23,6 +23,7 @@ * [db](#db) * [DB](#db.DB) * [\_\_init\_\_](#db.DB.__init__) + * [\_\_del\_\_](#db.DB.__del__) * [insert\_character](#db.DB.insert_character) * [insert\_into\_table](#db.DB.insert_into_table) * [read\_from\_table](#db.DB.read_from_table) @@ -325,6 +326,16 @@ def __init__() -> None Initialize the database connection. + + +#### \_\_del\_\_ + +```python +def __del__() -> None +``` + +Close the database connection when the object is destroyed. + #### insert\_character diff --git a/src/character.py b/src/character.py index d8cdb40..7acb3a1 100644 --- a/src/character.py +++ b/src/character.py @@ -48,11 +48,27 @@ def new_character(self, name: str, race: str, dnd_class: str, stats: dict[str, i self.name = name self.race = race self.dnd_class = dnd_class + + min_stat = 1 + max_stat = 20 + if stats is None: primary_stat = CharacterClass(self.dnd_class).get_primary_stat() worst_stat = CharacterClass(self.dnd_class).get_worst_stat() self.roll_stats(primary_stat, worst_stat) else: + # Validate stats + required_stats = ["Constitution", "Dexterity", "Strength", "Wisdom", "Intelligence", "Charisma"] + for stat in required_stats: + if stat not in stats: + error_message = f"Missing required stat: {stat}" + raise ValueError(error_message) + if not isinstance(stats[stat], int): + error_message = f"Stat value for {stat} must be an integer" + raise TypeError(error_message) + if stats[stat] < min_stat or stats[stat] > max_stat: # D&D stats typically range from 1-20 + error_message = f"Stat value for {stat} must be between {min_stat} and {max_stat}" + raise ValueError(error_message) self.stats = stats self.update_race_details() self.update_skills() diff --git a/src/db.py b/src/db.py index 5aac72d..49fde19 100644 --- a/src/db.py +++ b/src/db.py @@ -1,9 +1,12 @@ """A module for the database connection.""" +import logging import os import mysql.connector +logging.basicConfig(level=logging.INFO) + class DB: """A class to represent a database connection.""" @@ -11,14 +14,24 @@ class DB: def __init__(self) -> None: """Initialize the database connection.""" self.host = "dnd-db" - self.user = os.environ["MYSQL_USER"] - self.password = os.environ["MYSQL_PASSWORD"] - self.db = mysql.connector.connect( - host=self.host, - port=3306, - user=self.user, - password=self.password, - ) + try: + self.user = os.environ["MYSQL_USER"] + self.password = os.environ["MYSQL_PASSWORD"] + self.db = mysql.connector.connect( + host=self.host, + port=3306, + user=self.user, + password=self.password, + ) + except (KeyError, mysql.connector.Error): + logger = logging.getLogger(__name__) + logger.exception("Database connection error") + self.db = None + + def __del__(self) -> None: + """Close the database connection when the object is destroyed.""" + if hasattr(self, "db") and self.db is not None: + self.db.close() def insert_character( self, diff --git a/src/dice.py b/src/dice.py index 273024a..6ab5ceb 100644 --- a/src/dice.py +++ b/src/dice.py @@ -13,7 +13,6 @@ def __init__(self, num_dice: int, num_sides: int, modifier: int) -> None: self.modifier = modifier self.rolls = [] self.total = 0 - self.rolls = [] self.critical_success = False self.critical_fail = False diff --git a/src/page_loader.py b/src/page_loader.py index 5461131..294bae2 100644 --- a/src/page_loader.py +++ b/src/page_loader.py @@ -148,10 +148,9 @@ def left_right_dance( subtitle: str, ) -> str: """Load the left right page.""" - try: - if "submit" in submit: - pass - except TypeError: + if submit is not None and "submit" in submit: + pass + else: right_content = "" return self.load_left_right_page(left_content, right_content, subtitle) diff --git a/tests/test_character.py b/tests/test_character.py index 8a3b8c9..748cd11 100644 --- a/tests/test_character.py +++ b/tests/test_character.py @@ -17,6 +17,12 @@ def fixture_mock_dice() -> None: yield mock_dice_object +def test_character_destruction() -> None: + """Test that the Character class closes the connection when the object is destroyed.""" + character = Character() + del character + + @pytest.fixture(name="_mock_race") def fixture_mock_race() -> None: """Mock the race class.""" @@ -42,6 +48,18 @@ def fixture_mock_character_class() -> None: yield mock_character_class_object +def test_character__stat_validation() -> None: + """Test the validation of the Character class.""" + char = Character() + + with pytest.raises(ValueError, match="Missing required stat: Dexterity"): + char.new_character("Aragorn", "Elf", "Ranger", {"Constitution": 19}) + with pytest.raises(TypeError): + char.new_character("Aragorn", "Elf", "Ranger", {"Constitution": "19"}) + with pytest.raises(ValueError, match="Constitution must be between 1 and 20"): + char.new_character("Aragorn", "Elf", "Ranger", {"Constitution": 200}) + + def test_character_initialization() -> None: """Test the initialization of the Character class.""" character = Character() diff --git a/tests/test_db.py b/tests/test_db.py index f6b28ae..0043bfd 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -23,6 +23,12 @@ def test_db_initialization(mock_connect) -> None: assert db.db is not None +def test_db_destruction() -> None: + """Test that the DB class closes the connection when the object is destroyed.""" + db = DB() + del db + + @patch.dict( os.environ, {"MYSQL_USER": "user", "MYSQL_PASSWORD": "password"}, diff --git a/tests/test_page_loader.py b/tests/test_page_loader.py index b0e5990..07d0634 100644 --- a/tests/test_page_loader.py +++ b/tests/test_page_loader.py @@ -301,12 +301,12 @@ def test_load_create_character( @patch("src.page_loader.Character") def test_load_old_character(mock_character) -> None: """Test the load_old_character method.""" - test_args = {"char_id": "42"} + char_id = 42 mock_test = Mock() mock_character.return_value = mock_test mock_character.load_character_from_db.return_value = None page_loader = PageLoader() - result = page_loader.load_old_character(test_args) + result = page_loader.load_old_character(char_id) assert result == mock_test