diff --git a/.gitignore b/.gitignore index 1b6612c..5822946 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ src/public/static/ import_files/ test_reports/ dashboard_templates/backup_*.zip + +.hypothesis diff --git a/poetry.lock b/poetry.lock index 6dc6cd2..fb60979 100644 --- a/poetry.lock +++ b/poetry.lock @@ -313,6 +313,35 @@ category = "main" optional = false python-versions = ">=3.7" +[[package]] +name = "hypothesis" +version = "6.56.4" +description = "A library for property-based testing" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +attrs = ">=19.2.0" +exceptiongroup = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +sortedcontainers = ">=2.1.0,<3.0.0" + +[package.extras] +all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "django (>=3.2)", "dpcontracts (>=0.4)", "importlib-metadata (>=3.6)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=1.0)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2022.5)"] +cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"] +codemods = ["libcst (>=0.3.16)"] +dateutil = ["python-dateutil (>=1.4)"] +django = ["django (>=3.2)"] +dpcontracts = ["dpcontracts (>=0.4)"] +ghostwriter = ["black (>=19.10b0)"] +lark = ["lark-parser (>=0.6.5)"] +numpy = ["numpy (>=1.9.0)"] +pandas = ["pandas (>=1.0)"] +pytest = ["pytest (>=4.6)"] +pytz = ["pytz (>=2014.1)"] +redis = ["redis (>=3.0.0)"] +zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2022.5)"] + [[package]] name = "identify" version = "2.5.8" @@ -865,7 +894,7 @@ h11 = ">=0.9.0,<1" [metadata] lock-version = "1.1" python-versions = ">=3.10.0, <4" -content-hash = "d566d58d59e11f9bc96abc0c53cc8b00b8b5d6b1c886b82570cc0fb1cdc158fd" +content-hash = "0dc2fab20f892efa2b0acbddd23ae1378d5c5ae24ece6357f11858c625cced68" [metadata.files] ansicon = [ @@ -1187,6 +1216,10 @@ h11 = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] +hypothesis = [ + {file = "hypothesis-6.56.4-py3-none-any.whl", hash = "sha256:67950103ee930c66673494b3671474a083ea71f1ebe8f0ff849ba8ad5317772d"}, + {file = "hypothesis-6.56.4.tar.gz", hash = "sha256:313bc1c0f377ec6c98815d3237a69add7558eadee4effe4ed613d0ba36513a52"}, +] identify = [ {file = "identify-2.5.8-py2.py3-none-any.whl", hash = "sha256:48b7925fe122720088aeb7a6c34f17b27e706b72c61070f27fe3789094233440"}, {file = "identify-2.5.8.tar.gz", hash = "sha256:7a214a10313b9489a0d61467db2856ae8d0b8306fc923e03a9effa53d8aedc58"}, diff --git a/pyproject.toml b/pyproject.toml index 4df59b8..64c4497 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ freezegun = ">=1.1.0" bpython = ">=0.22.1" poetry-deps-scanner = ">=2.0.0" invoke = ">=1.7.3" +hypothesis = ">=6.56.4" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/src/character/models/character.py b/src/character/models/character.py index d7f58d3..c0f3290 100644 --- a/src/character/models/character.py +++ b/src/character/models/character.py @@ -155,6 +155,7 @@ class Character(models.Model): Profile.MagicalStrength.INTELLIGENCE: self.modifier_intelligence, Profile.MagicalStrength.WISDOM: self.modifier_wisdom, Profile.MagicalStrength.CHARISMA: self.modifier_charisma, + Profile.MagicalStrength.NONE: 0, } return self.level + modifier_map.get( diff --git a/src/character/tests/test_character.py b/src/character/tests/test_character.py new file mode 100644 index 0000000..0ce5637 --- /dev/null +++ b/src/character/tests/test_character.py @@ -0,0 +1,86 @@ +import pytest +from hypothesis import given +from hypothesis.strategies import integers + +from character.models.character import Character +from character.tests.utils import ability_values, levels, modifier_test + + +@pytest.mark.parametrize( + "value,expected", + [ + (1, -4), + (2, -4), + (3, -4), + (4, -3), + (5, -3), + (6, -2), + (7, -2), + (8, -1), + (9, -1), + (10, 0), + (11, 0), + (12, 1), + (13, 1), + (14, 2), + (15, 2), + (16, 3), + (17, 3), + (18, 4), + (19, 4), + (20, 5), + (21, 5), + ], +) +@pytest.mark.parametrize( + "ability", + ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"], +) +def test_modifier_values(value, expected, ability): + character = Character() + value_attribute = f"value_{ability}" + setattr(character, value_attribute, value) + modifier_attribute = f"modifier_{ability}" + assert getattr(character, modifier_attribute) == expected + + +@given(ability_values()) +def test_initiative(dex): + assert Character(value_dexterity=dex).initiative == dex + + +@given(level=levels(), strength=ability_values()) +def test_attack_melee(level, strength): + character = Character(level=level, value_strength=strength) + assert character.attack_melee == level + modifier_test(strength) + + +@given(level=levels(), dexterity=ability_values()) +def test_attack_range(level, dexterity): + character = Character(level=level, value_dexterity=dexterity) + assert character.attack_range == level + modifier_test(dexterity) + + +@given(armor=integers(), shield=integers(), dexterity=ability_values(), misc=integers()) +def test_defense(armor, shield, dexterity, misc): + char = Character( + armor=armor, shield=shield, value_dexterity=dexterity, defense_misc=misc + ) + assert char.defense == 10 + armor + shield + modifier_test(dexterity) + misc + + +@given(level=levels(), intelligence=ability_values()) +def test_mana_max(level, intelligence): + char = Character(level=level, value_intelligence=intelligence) + assert char.mana_max == 2 * level + modifier_test(intelligence) + + +@given( + level=levels(), intelligence=ability_values(), mana_consumed=integers(min_value=0) +) +def test_mana_remaining(level, intelligence, mana_consumed): + char = Character( + level=level, value_intelligence=intelligence, mana_consumed=mana_consumed + ) + mana_max = 2 * level + modifier_test(intelligence) + assert char.mana_remaining == mana_max - mana_consumed diff --git a/src/character/tests/utils.py b/src/character/tests/utils.py new file mode 100644 index 0000000..ce97503 --- /dev/null +++ b/src/character/tests/utils.py @@ -0,0 +1,35 @@ +from functools import partial + +from hypothesis.strategies import integers + +VALUE_TO_MODIFIER = { + 1: -4, + 2: -4, + 3: -4, + 4: -3, + 5: -3, + 6: -2, + 7: -2, + 8: -1, + 9: -1, + 10: 0, + 11: 0, + 12: 1, + 13: 1, + 14: 2, + 15: 2, + 16: 3, + 17: 3, + 18: 4, + 19: 4, + 20: 5, + 21: 5, +} + + +def modifier_test(value: int) -> int: + return VALUE_TO_MODIFIER[value] + + +ability_values = partial(integers, min_value=1, max_value=21) +levels = partial(integers, min_value=1)