diff --git a/app/functions/roll_physical_attack.py b/app/functions/roll_physical_attack.py index bfc5143..aea496b 100644 --- a/app/functions/roll_physical_attack.py +++ b/app/functions/roll_physical_attack.py @@ -23,7 +23,6 @@ def roll_physical_attack(dac, modifier=0, awc=None, ahd=None): # use non-weapon attack lookup table else: # handle error state where neither awc nor ahd are integers (None or other non-integer value) - result["outcome"] = "Attacker Hit Dice is required for Non-Weapon Attacks!" - return result + raise ValueError("Either awc or ahd must be an integer.") return get_attack_roll_outcome(result, modifier) diff --git a/app/routes/physical_attack.py b/app/routes/physical_attack.py index dd7833a..1940eba 100644 --- a/app/routes/physical_attack.py +++ b/app/routes/physical_attack.py @@ -1,4 +1,4 @@ -from flask import request +from flask import request, jsonify from flask_restx import Resource, Namespace, reqparse from app.functions.roll_physical_attack import roll_physical_attack @@ -24,9 +24,12 @@ class PhysicalAttack(Resource): # Request validation if dac is None: return {"error": "'dac' parameter is needed"}, 400 + if (awc is None and ahd is None) or (awc is not None and ahd is not None): return {"error": "Exactly one of 'awc' or 'ahd' parameters must be provided"}, 400 - # Call to business logic after validation, could be placed in try-except block for handling exceptions if any - result = roll_physical_attack(dac, modifier, awc, ahd) - return result, 200 + try: + result = roll_physical_attack(dac, modifier, awc, ahd) + return result, 200 + except ValueError as e: + return jsonify({"error": str(e)}), 400 diff --git a/app/routes/roll_dice.py b/app/routes/roll_dice.py index 89faced..27b1aa8 100644 --- a/app/routes/roll_dice.py +++ b/app/routes/roll_dice.py @@ -15,7 +15,7 @@ parser.add_argument('geometry', type=int, required=True, help='Number of faces o parser.add_argument('discard_lowest', type=str_to_bool, required=True, help='Whether to discard lowest roll') -@namespace.route('/') # resolves to: /dice +@namespace.route('') # resolves to: /dice class RollDice(Resource): @namespace.expect(parser) def get(self): diff --git a/tests/test_functions/__init__.py b/tests/test_functions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_generate_profile.py b/tests/test_functions/test_generate_profile.py similarity index 100% rename from tests/test_generate_profile.py rename to tests/test_functions/test_generate_profile.py diff --git a/tests/test_get_score_list.py b/tests/test_functions/test_get_score_list.py similarity index 100% rename from tests/test_get_score_list.py rename to tests/test_functions/test_get_score_list.py diff --git a/tests/test_roll_dices.py b/tests/test_functions/test_roll_dices.py similarity index 100% rename from tests/test_roll_dices.py rename to tests/test_functions/test_roll_dices.py diff --git a/tests/test_functions/test_split_number.py b/tests/test_functions/test_split_number.py new file mode 100644 index 0000000..87cdd37 --- /dev/null +++ b/tests/test_functions/test_split_number.py @@ -0,0 +1,13 @@ +import pytest +from app.functions.split_number import split_number + + +@pytest.mark.parametrize("n, expected", [ + (4, (2, 2)), # even number split into equal parts + (5, (2, 3)), # odd number split into (n // 2) and (n // 2 + 1) + (0, (0, 0)), # zero case + (-4, (-2, -2)), # negative even number + (-5, (-3, -2)) # negative odd number +]) +def test_split_number(n, expected): + assert split_number(n) == expected diff --git a/tests/test_routes/__init__.py b/tests/test_routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_routes/test_route_dice.py b/tests/test_routes/test_route_dice.py new file mode 100644 index 0000000..ca2928c --- /dev/null +++ b/tests/test_routes/test_route_dice.py @@ -0,0 +1,58 @@ +import pytest +from app import app + +ROUTE = '/dice' + + +@pytest.fixture +def app_instance(): + """Create and configure a new test client instance.""" + app_instance = app + app.config['TESTING'] = True + return app_instance + + +def test_roll_dice_success(app_instance): + with app.test_client() as client: + # successful request + response = client.get(ROUTE, + query_string={'quantity': '3', 'geometry': '6', + 'discard_lowest': 'false'}) + assert response.status_code == 200 + + +def test_quantity_out_of_range_low(app_instance): + with app.test_client() as client: + response = client.get(ROUTE, query_string={'quantity': '0', + 'geometry': '6', 'discard_lowest': 'false'}) + assert response.status_code == 400 + + +def test_quantity_out_of_range_high(app_instance): + with app.test_client() as client: + response = client.get(ROUTE, query_string={'quantity': '101', + 'geometry': '6', 'discard_lowest': 'false'}) + assert response.status_code == 400 + + +def test_geometry_out_of_range_low(app_instance): + with app.test_client() as client: + # geometry out of range + response = client.get(ROUTE, query_string={'quantity': '3', + 'geometry': '2', 'discard_lowest': 'false'}) + assert response.status_code == 400 + + +def test_geometry_out_of_range_high(app_instance): + with app.test_client() as client: + response = client.get(ROUTE, query_string={'quantity': '3', + 'geometry': '101', 'discard_lowest': 'false'}) + assert response.status_code == 400 + + +def test_discard_lowest_success(app_instance): + with app.test_client() as client: + # test discard_lowest + response = client.get(ROUTE, query_string={'quantity': '3', + 'geometry': '6', 'discard_lowest': 'true'}) + assert response.status_code == 200