Skip to content

Commit

Permalink
Added exception handling to the model
Browse files Browse the repository at this point in the history
  • Loading branch information
rofrano committed Feb 4, 2024
1 parent ea19423 commit 2783c57
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 37 deletions.
31 changes: 24 additions & 7 deletions service/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,13 @@ def create(self):
logger.info("Creating %s", self.name)
# id must be none to generate next primary key
self.id = None # pylint: disable=invalid-name
db.session.add(self)
db.session.commit()
try:
db.session.add(self)
db.session.commit()
except Exception as e:
db.session.rollback()
logger.error("Error creating record: %s", self)
raise DataValidationError(e) from e

def update(self):
"""
Expand All @@ -95,13 +100,23 @@ def update(self):
logger.info("Saving %s", self.name)
if not self.id:
raise DataValidationError("Update called with empty ID field")
db.session.commit()
try:
db.session.commit()
except Exception as e:
db.session.rollback()
logger.error("Error updating record: %s", self)
raise DataValidationError(e) from e

def delete(self):
"""Removes a Pet from the data store"""
logger.info("Deleting %s", self.name)
db.session.delete(self)
db.session.commit()
try:
db.session.delete(self)
db.session.commit()
except Exception as e:
db.session.rollback()
logger.error("Error deleting record: %s", self)
raise DataValidationError(e) from e

def serialize(self) -> dict:
"""Serializes a Pet into a dictionary"""
Expand All @@ -111,7 +126,7 @@ def serialize(self) -> dict:
"category": self.category,
"available": self.available,
"gender": self.gender.name, # convert enum to string
"birthday": self.birthday.isoformat()
"birthday": self.birthday.isoformat(),
}

def deserialize(self, data: dict):
Expand All @@ -135,7 +150,9 @@ def deserialize(self, data: dict):
except AttributeError as error:
raise DataValidationError("Invalid attribute: " + error.args[0]) from error
except KeyError as error:
raise DataValidationError("Invalid pet: missing " + error.args[0]) from error
raise DataValidationError(
"Invalid pet: missing " + error.args[0]
) from error
except TypeError as error:
raise DataValidationError(
"Invalid pet: body of request contained bad or no data " + str(error)
Expand Down
29 changes: 29 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import os
import logging
from unittest import TestCase
from unittest.mock import patch
from datetime import date
from wsgi import app
from service.models import Pet, Gender, DataValidationError, db
Expand Down Expand Up @@ -218,6 +219,34 @@ def test_deserialize_bad_gender(self):
self.assertRaises(DataValidationError, pet.deserialize, data)


######################################################################
# T E S T E X C E P T I O N H A N D L E R S
######################################################################
class TestExceptionHandlers(TestCaseBase):
"""Pet Model Exception Handlers"""

@patch("service.models.db.session.commit")
def test_create_exception(self, exception_mock):
"""It should catch a create exception"""
exception_mock.side_effect = Exception()
pet = PetFactory()
self.assertRaises(DataValidationError, pet.create)

@patch("service.models.db.session.commit")
def test_update_exception(self, exception_mock):
"""It should catch a update exception"""
exception_mock.side_effect = Exception()
pet = PetFactory()
self.assertRaises(DataValidationError, pet.update)

@patch("service.models.db.session.commit")
def test_delete_exception(self, exception_mock):
"""It should catch a delete exception"""
exception_mock.side_effect = Exception()
pet = PetFactory()
self.assertRaises(DataValidationError, pet.delete)


######################################################################
# Q U E R Y T E S T C A S E S
######################################################################
Expand Down
74 changes: 44 additions & 30 deletions tests/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
# from unittest.mock import MagicMock, patch
from urllib.parse import quote_plus
from wsgi import app

# from service import create_app
from service.common import status
from service.models import Pet, db
Expand Down Expand Up @@ -79,7 +80,9 @@ def _create_pets(self, count):
test_pet = PetFactory()
response = self.client.post(BASE_URL, json=test_pet.serialize())
self.assertEqual(
response.status_code, status.HTTP_201_CREATED, "Could not create test pet"
response.status_code,
status.HTTP_201_CREATED,
"Could not create test pet",
)
new_pet = response.get_json()
test_pet.id = new_pet["id"]
Expand Down Expand Up @@ -189,8 +192,7 @@ def test_query_pet_list_by_category(self):
test_category = pets[0].category
category_pets = [pet for pet in pets if pet.category == test_category]
response = self.client.get(
BASE_URL,
query_string=f"category={quote_plus(test_category)}"
BASE_URL, query_string=f"category={quote_plus(test_category)}"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = response.get_json()
Expand All @@ -200,9 +202,46 @@ def test_query_pet_list_by_category(self):
self.assertEqual(pet["category"], test_category)

######################################################################
# T E S T S A D P A T H S
# T E S T A C T I O N S
######################################################################

def test_purchase_a_pet(self):
"""It should Purchase a Pet"""
pets = self._create_pets(10)
available_pets = [pet for pet in pets if pet.available is True]
pet = available_pets[0]
response = self.client.put(f"{BASE_URL}/{pet.id}/purchase")
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.get(f"{BASE_URL}/{pet.id}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = response.get_json()
logging.debug("Response data: %s", data)
self.assertEqual(data["available"], False)

def test_purchase_not_available(self):
"""It should not Purchase a Pet that is not available"""
pets = self._create_pets(10)
unavailable_pets = [pet for pet in pets if pet.available is False]
pet = unavailable_pets[0]
response = self.client.put(f"{BASE_URL}/{pet.id}/purchase")
self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)


######################################################################
# T E S T S A D P A T H S
######################################################################
class TestSadPaths(TestCase):
"""Test REST Exception Handling"""

def setUp(self):
"""Runs before each test"""
self.client = app.test_client()

def test_method_not_allowed(self):
"""It should not allow update without a pet id"""
response = self.client.put(BASE_URL)
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)

def test_create_pet_no_data(self):
"""It should not Create a Pet with missing data"""
response = self.client.post(BASE_URL, json={})
Expand Down Expand Up @@ -233,35 +272,10 @@ def test_create_pet_bad_gender(self):
logging.debug(pet)
# change gender to a bad string
test_pet = pet.serialize()
test_pet["gender"] = "male" # wrong case
test_pet["gender"] = "male" # wrong case
response = self.client.post(BASE_URL, json=test_pet)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

######################################################################
# T E S T A C T I O N S
######################################################################

def test_purchase_a_pet(self):
"""It should Purchase a Pet"""
pets = self._create_pets(10)
available_pets = [pet for pet in pets if pet.available is True]
pet = available_pets[0]
response = self.client.put(f"{BASE_URL}/{pet.id}/purchase")
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.get(f"{BASE_URL}/{pet.id}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = response.get_json()
logging.debug("Response data: %s", data)
self.assertEqual(data["available"], False)

def test_purchase_not_available(self):
"""It should not Purchase a Pet that is not available"""
pets = self._create_pets(10)
unavailable_pets = [pet for pet in pets if pet.available is False]
pet = unavailable_pets[0]
response = self.client.put(f"{BASE_URL}/{pet.id}/purchase")
self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)

######################################################################
# T E S T M O C K S
######################################################################
Expand Down

0 comments on commit 2783c57

Please sign in to comment.