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