diff --git a/pyrecipe/__init__.py b/pyrecipe/__init__.py index 74b84b5..943e20b 100644 --- a/pyrecipe/__init__.py +++ b/pyrecipe/__init__.py @@ -1,7 +1,6 @@ __version__ = "0.0.0" from . import configure -from . import cookbook from . import errors from . import storage from . import tradingpost diff --git a/pyrecipe/cookbook/README.md b/pyrecipe/cookbook/README.md deleted file mode 100644 index 6e78e7c..0000000 --- a/pyrecipe/cookbook/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Cookbook Module -## Classes -Each class has an associated **interface** that must be implemented in order to have an expected level of behavior. -- **User**: a class that abstracts the database interaction and provides an interface to the MongoDB **user collection**. -- **Recipe**: a class that abstracts the database interaction and provides an interface to the MongoDB **recipe collection**. - -## Usage -- The database must be initialized so the classes can perform standard database operations. -- The following REPL session will show a few features by example. -```python ->>> from pyrecipe.storage import mongo_setup ->>> from pyrecipe.cookbook import User, Recipe - ->>> mongo_setup.global_init(db_name="pyrecipe_tester") # set db_name="pyrecipe" for actual use - # "pyrecipe_tester" db found in sourcecode - # "tests/testing_db/mongodb/" directory - ->>> user = User.login_user(email="blackknight@mail.com") ->>> user.name -'Black Knight' - ->>> recipes = [Recipe(recipe) for recipe in user.recipes] ->>> recipes[0].name -'hearty spam breakfast skillet' - ->>> new_user = User.create_user(name="Roger the Shrubber", email="roger@mail.com") ->>> new_user._id -"5c3ccd2f8e58582cfbf1cc35" - ->>> len(new_user.recipes) -0 - ->>> new_recipe = Recipe.create_recipe( - name='fried spam', - ingredients={'name':'spam', 'quantity':'1', 'unit':'slice'}, - directions=['Heat skillet on medium heat.', 'Fry spam until brown.', 'Serve warm.'], - ) - ->>> new_user.add_recipe(new_recipe) ->>> len(new_user.recipes) -1 -``` diff --git a/pyrecipe/cookbook/__init__.py b/pyrecipe/cookbook/__init__.py deleted file mode 100644 index e87ec3a..0000000 --- a/pyrecipe/cookbook/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .recipe import RecipeMongo as Recipe -from .user import UserMongo as User diff --git a/pyrecipe/cookbook/recipe.py b/pyrecipe/cookbook/recipe.py deleted file mode 100644 index 5b0eb2a..0000000 --- a/pyrecipe/cookbook/recipe.py +++ /dev/null @@ -1,323 +0,0 @@ -""" -cookbook.recipe interface to Database - -Interfaces: -* RecipeInterface -- Interface to be implemented by Recipe* classes. - -Implementations: -* RecipeMongo -- Recipe class that interacts with MongoDB. -""" - -import abc -import datetime -from typing import List - -import pyrecipe.storage as db - - -############################################################################## -# Interfaces -############################################################################## - - -class RecipeInterface(metaclass=abc.ABCMeta): - """Class for interacting with the PyRecipe Database Recipe Collection.""" - - @classmethod - @abc.abstractmethod - def create_recipe(cls, name: str, ingredients: list, directions: list) -> "Recipe": - """ - Create a new Recipe and insert it into the DB. - - Recipe.create_recipe(name, ingredients, directions) - - :param name: (str) the name of the recipe - :param ingredients: (list(dict)) list of ingredients, where each ingredient - is a dict of the form {'name':'olive oil', 'quantity': '1', 'unit': 'tbsp'} - :param directions: (list) ordered list of strings for each line of the directions. - i.e. ['boil water', 'add rice', 'reduce heat'] - """ - - @classmethod - @abc.abstractmethod - def fetch_recipe(cls, name: str) -> "Recipe": - """ - Fetch a recipe by name. - - Recipe.fetc_recipe(name) - - :param name: (str) the name of the recipe - :returns: (Recipe) the Recipe instance or None - """ - - @abc.abstractmethod - def copy_recipe(self, recipe: "Recipe") -> "Recipe": - """ - Given a Recipe instance, produce a copy of it with a modified name - - :param recipe: (Recipe) the Recipe instance to copy - :returns: a Recipe instance with a modified name - i.e. recipe.name = 'lasagna_COPY' - """ - - @abc.abstractmethod - def update_recipe_data(self, data: dict) -> int: - """ - Update a Recipe instance's given attribute. - - :param data: (dict) key=val to change - i.e. {'name': 'lasagna'} - :returns: (int) number of successfully changed attributes - """ - - @abc.abstractmethod - def delete_recipe(self) -> int: - """ - Given a recipe instance, set the deleted attribute=True - - :returns: (int) 1 for success, 0 for failure - """ - - @abc.abstractmethod - def restore_recipe(self) -> int: - """ - Given a recipe instance, set the deleted attribute=False - - :returns: (int) 1 for success, 0 for failure - """ - - @abc.abstractmethod - def add_tag(self, tag: str) -> int: - """ - Given a recipe instance, add a new tag - - :returns: (int) 1 for success, 0 for failure - """ - - @abc.abstractmethod - def delete_tag(self, tag: str) -> int: - """ - Given a recipe instance, delete a new tag - - :returns: (int) 1 for success, 0 for failure - """ - - -############################################################################## -# Implementations -############################################################################## - - -class RecipeMongo(RecipeInterface): - """ - Each Instance will hold a recipe's data from the database. - - PARAMS: - :param db_recipe: (db.User) an instance from the DB's recipe collection. - - ATTRIBUTES/PROPERTIES: - :attr _id: (str) DB _id of the recipe document. - :attr name: (str) name of the recipe. - :attr ingredients: (list) list of ingredients. - :attr num_ingredients: (int) total number of discrete ingredients. - :attr directions: (list) ordered list of cooking instructions. - :attr prep_time: (float) total # minutes to prep recipe. - :attr cook_time: (float) total # minutes to cook recipe. - :attr servings: (int) number of servings in the recipe. - :attr tags: (list) list of tags given. - :attr pictures: (list) list of filepaths for pictures in recipe. - :attr notes: (list) list of strings for recipe notes. - :attr rating: (int) rating of recipe, if given, [0.0 - 5.0] in .5 increments. - :attr favorite: (bool) recipe tagged as favorite. - :attr deleted: (bool) recipe marked deleted? - :attr created_date: (datetime) UTC time of when recipe was created. - :attr last_modified_date: (datetime) UTC time of when recipe was last modified. - - TODO: - * modify update_user_data to use property.setters - * test recipe creation so multiple recipes cannot exist with same name - i.e. raise RecipeCreationError - """ - - def __init__(self, db_recipe=None): - self._recipe = db_recipe - - @classmethod - def create_recipe(cls, name: str, ingredients: list, directions: dict) -> "Recipe": - # see RecipeInterface docstring - _recipe = db.Recipe() - _recipe.name = name.lower() - - _recipe.ingredients = [] - for ingredient in ingredients: - i = db.Ingredient() - i.name = ingredient["name"].lower() - i.quantity = ingredient["quantity"].lower() - i.unit = ingredient["unit"].lower() - _recipe.ingredients.append(i) - - _recipe.directions = directions - _recipe.num_ingredients = len(_recipe.ingredients) - _recipe.save() - return cls(_recipe) - - @classmethod - def fetch_recipe(cls, name: str) -> "Recipe": - # see RecipeInterface docstring - _recipe = db.Recipe.objects().filter(name=name).first() - if not _recipe: - return None - return cls(_recipe) - - @classmethod - def copy_recipe(cls, recipe: "Recipe") -> "Recipe": - # see RecipeInterface docstring - _recipe = db.Recipe() - _recipe.name = recipe.name + "_COPY" - _recipe.ingredients = recipe.ingredients - _recipe.directions = recipe.directions - _recipe.tags = recipe.tags - _recipe.pictures = recipe.pictures - _recipe.notes = recipe.notes - _recipe.rating = recipe.rating - _recipe.favorite = recipe.favorite - _recipe.save() - return cls(_recipe) - - def update_recipe_data(self, data: dict) -> int: - # see RecipeInterface docstring - count = 0 - for key, val in data.items(): - if key in ( - "name", - "prep_time", - "cook_time", - "tags", - "pictures", - "notes", - "rating", - "favorite", - ): - setattr(self._recipe, key, val) - count += 1 - elif key in ("ingredients"): - setattr(self._recipe, key, val) - self._recipe.num_ingredients = len(val) - count += 1 - self._recipe.save() - self._update_last_mod_date() - self._recipe = self._refresh_recipe() - return count - - def delete_recipe(self) -> int: - # see RecipeInterface docstring - result = self._recipe.update(deleted=True) - self._update_last_mod_date() - self._recipe = self._refresh_recipe() - return result - - def restore_recipe(self) -> int: - # see RecipeInterface docstring - result = self._recipe.update(deleted=False) - self._update_last_mod_date() - self._recipe = self._refresh_recipe() - return result - - def add_tag(self, tag: str) -> int: - # see RecipeInterface docstring - result = self._recipe.update(add_to_set__tags=tag.lower()) - self._update_last_mod_date() - self._recipe = self._refresh_recipe() - return result - - def delete_tag(self, tag: str) -> int: - # see RecipeInterface docstring - result = self._recipe.update(pull__tags=tag.lower()) - self._update_last_mod_date() - self._recipe = self._refresh_recipe() - return result - - @property - def _id(self) -> str: - return str(self._recipe.id) - - @property - def name(self) -> str: - return self._recipe.name - - @property - def ingredients(self) -> List["Ingredient"]: - return self._recipe.ingredients - - @property - def num_ingredients(self) -> int: - return self._recipe.num_ingredients - - @property - def directions(self) -> List[str]: - return self._recipe.directions - - @property - def prep_time(self) -> float: - return self._recipe.prep_time - - @property - def servings(self) -> int: - return self._recipe.servings - - @property - def cook_time(self) -> float: - return self._recipe.cook_time - - @property - def tags(self) -> List["tags"]: - return self._recipe.tags - - @property - def pictures(self) -> List["filepaths"]: - return self._recipe.pictures - - @property - def notes(self) -> List[str]: - return self._recipe.notes - - @property - def rating(self) -> float: - return self._recipe.rating - - @property - def favorite(self) -> bool: - return self._recipe.favorite - - @property - def deleted(self) -> bool: - return self._recipe.deleted - - @property - def created_date(self) -> datetime.datetime: - return self._recipe.created_date - - @property - def last_modified_date(self) -> datetime.datetime: - return self._recipe.last_modified_date - - def _refresh_recipe(self) -> db.Recipe: - """ - Ensure recipe information is refreshed after saving/updating to the DB. - - recipe._refresh_user() - - :returns: (db.Recipe) refreshed recipe document after updating the DB. - """ - return db.Recipe.objects().filter(id=self._id).first() - - def _update_last_mod_date(self) -> int: - """ - Updates the recipe's "last_updated_date" attribute in the DB. - - recipe._update_last_mod_date() - - :returns: (int) 1 for success, 0 if unsuccessful - """ - result = self._recipe.update(last_modified_date=datetime.datetime.utcnow()) - return result diff --git a/pyrecipe/cookbook/user.py b/pyrecipe/cookbook/user.py deleted file mode 100644 index f7b86de..0000000 --- a/pyrecipe/cookbook/user.py +++ /dev/null @@ -1,238 +0,0 @@ -""" -cookbook.user interface to Database - -Interfaces: -* UserInterface -- Interface to be implemented by User* classes. - -Implementations: -* UserMongo -- User class that interacts with MongoDB. -""" - -import abc -import datetime -from typing import List - -import pyrecipe.storage as db -from .recipe import RecipeMongo as Recipe -from pyrecipe.errors import UserNotFoundError -from pyrecipe.errors import UserCreationError - - -############################################################################## -# Interfaces -############################################################################## - - -class UserInterface(metaclass=abc.ABCMeta): - """Class for interacting with the PyRecipe Database User Collection.""" - - @classmethod - @abc.abstractmethod - def create_user(cls, name: str, email: str) -> "User": - """ - Create a new User and insert into DB. - - User.create_user(name, email) - - :param name: (str) name of the user. - :param email: (str) email address of the user. - :returns: (User) the new user. - :raises: UserCreationError if a user with the same email address - already exists. - """ - - @classmethod - @abc.abstractmethod - def login_user(cls, name: str, email: str) -> "User": - """ - Logs in and returns the user. - - User.login_user() - - :param name: (str) the user's name. - :param email: (str) the user's email address. - :returns: (User) the user. - :raises: UserNotFoundError if user with name and email doesn't - exist. - """ - - @staticmethod - @abc.abstractmethod - def list_users() -> List["User"]: - """ - Returns a list of all Users. - - User.list_users() - - :returns: list(User) - """ - - @abc.abstractmethod - def update_user_data(self, data: dict) -> int: - """ - Updates a user's data. - - User.update_user_data(self, data) - - :param data: (dict) data fields to change. - i.e. {"name": "Bob", "email": "newAddress@here.com"} - :returns: (int) total number of successfully changed fields. - i.e. 2 for the above example - """ - - @abc.abstractmethod - def add_recipe(self, recipe: Recipe) -> int: - """ - Adds a recipe reference to the user's recipes. - - User.add_recipe(self, recipe) - - :param recipe: (Recipe) an instance of a cookbook.Recipe class. - :returns: (int) 1 for success, 0 if unsuccessful. - """ - - -############################################################################## -# Implementations -############################################################################## - - -class UserMongo(UserInterface): - """ - Each Instance will hold a user's data from the database. - - PARAMS: - :param db_user: (db.User) an instance from the DB's user collection. - - ATTRIBUTES/PROPERTIES: - :attr _id: (str) DB _id of the user document. - :attr name: (str) the user's name. - :attr email: (str) the user's email address. - :attr created_date: (datetime) date the user was created. - :attr last_modified_date: (datetime) date the user was last modified. - :attr recipe: (list(Recipe)) list of recipe's owned by user. - :attr shared_recipe: (list(Recipe)) list of recipe's shared with user. - :attr view: (str) default recipe view [list, grid]. - :attr page_size: (str) max number of recipes to display before paginating. - :attr email_distros: (dict) email distros for emailing recipes to. - i.e. {"family": "email1, email2, email3"} - :attr _id: (str) user id in the database. - - TODO: - modify update_user_data to use property.setters - """ - - def __init__(self, db_user=None): - """ - Instantiate a User class, typically done via the login_user or - create_user class methods. - - :param db_user: (db.User) a user record from the DB. - """ - self._user = db_user - - @classmethod - def create_user(cls, name: str = None, email: str = None) -> "User": - # see UserInterface docstring. - if db.User.objects().filter(email=email).count(): - raise UserCreationError(email=email) - _user = db.User() - _user.name = name - _user.email = email - _user.save() - return cls(_user) - - @classmethod - def login_user(cls, email: str) -> "User": - # see UserInterface docstring. - _user = db.User.objects().filter(email=email).first() - if not _user: - raise UserNotFoundError(email) - return cls(_user) - - @staticmethod - def list_users() -> List["User"]: - # see UserInterface docstring. - users = db.User.objects() - return list(users) - - def update_user_data(self, data: dict) -> int: - # see UserInterface docstring. - count = 0 - for key, val in data.items(): - if key in ("name", "email", "view", "page_size", "email_distros"): - setattr(self._user, key, val) - count += 1 - self._user.save() - self._update_last_mod_date() - self._user = self._refresh_user() - return count - - def add_recipe(self, recipe: Recipe) -> int: - # see UserInterface docstring. - result = self._user.update(add_to_set__recipes=recipe.id) - if result: - self._update_last_mod_date() - self._user = self._refresh_user() - return result - - @property - def _id(self): - return str(self._user.id) - - @property - def name(self): - return self._user.name - - @property - def email(self): - return self._user.email - - @property - def created_date(self): - return self._user.created_date - - @property - def last_modified_date(self): - return self._user.last_modified_date - - @property - def recipes(self): - return self._user.recipe_ids - - @property - def shared_recipes(self): - return self._user.shared_recipe_ids - - @property - def view(self): - return self._user.view - - @property - def page_size(self): - return self._user.page_size - - @property - def email_distros(self): - return self._user.email_distros - - def _refresh_user(self) -> db.User: - """ - Ensure user information is refreshed after saving/updating to the DB. - - user._refresh_user() - - :returns: (db.User) refreshed user document after updating the DB. - """ - return db.User.objects().filter(id=self._id).first() - - def _update_last_mod_date(self) -> int: - """ - Updates the user's "last_updated_date" attribute in the DB. - - user._update_last_mod_date() - - :returns: (int) 1 for success, 0 if unsuccessful - """ - result = self._user.update(last_modified_date=datetime.datetime.utcnow()) - return result diff --git a/pyrecipe/storage/recipe.py b/pyrecipe/storage/recipe.py index 1425999..9fdc168 100644 --- a/pyrecipe/storage/recipe.py +++ b/pyrecipe/storage/recipe.py @@ -5,6 +5,7 @@ """ import datetime +from typing import List import mongoengine @@ -73,3 +74,97 @@ class Recipe(mongoengine.Document): "ingredients.name", ], } + + def __repr__(self): + """Repr of instance for quick debugging purposes.""" + return "".format(self.name) + + @staticmethod + def find_recipes(search_string: str) -> List["Recipe"]: + """ + Returns a match of all recipes for the search_string using a case insensitive + regex match in the Recipe.name field + + recipes = Recipe.find_recipes() + + :param search_string: (str) string to search + :returns: List["Recipe"] a list of all recipes that match + """ + recipes = Recipe.objects().filter(name__icontains=search_string) + return list(recipes) + + def copy_recipe(self) -> "Recipe": + """ + Given a Recipe instance, produce a copy of it with a modified name + + new_recipe = recipe.copy_recipe() + + :param recipe: (Recipe) the Recipe instance to copy + :returns: a Recipe instance with a modified name + i.e. recipe.name = 'lasagna_COPY' + """ + recipe = Recipe() + recipe.name = self.name + "_COPY" + recipe.num_ingredients = self.num_ingredients + recipe.ingredients = self.ingredients + recipe.directions = self.directions + recipe.prep_time = self.prep_time + recipe.cook_time = self.cook_time + recipe.servings = self.servings + recipe.tags = self.tags + recipe.pictures = self.pictures + recipe.notes = self.notes + recipe.rating = self.rating + recipe.save() + return recipe + + def add_tag(self, tag: str) -> int: + """ + Given a recipe instance, add a new tag + + recipe.add_tag("tag") + + :returns: (int) 1 for success, 0 for failure + """ + result = self.update(add_to_set__tags=tag.lower()) + self.reload() + return result + + def delete_tag(self, tag: str) -> int: + """ + Given a recipe instance, delete a new tag + + recipe.delete_tag("tag") + + :returns: (int) 1 for success, 0 for failure + """ + result = self.update(pull__tags=tag.lower()) + self.reload() + return result + + + def _update_last_mod_date(self) -> int: + """ + Updates the recipe's "last_updated_date" attribute in the DB. + + recipe._update_last_mod_date() + + :returns: (int) 1 for success, 0 if unsuccessful + """ + result = self.update(last_modified_date=datetime.datetime.utcnow()) + self.reload() + return result + + def save(self) -> int: + """ + Save the recipe's current state in the DB. First refreshes the last + modified date if it's already a DB record, before delegating to the + built-in/inherited save() method. + + recipe.save() + + :returns: (int) 1 for success, 0 if unsuccessful + """ + if self.id: + self._update_last_mod_date() + return super().save() diff --git a/pyrecipe/storage/user.py b/pyrecipe/storage/user.py index 6c7a6ae..36517a8 100644 --- a/pyrecipe/storage/user.py +++ b/pyrecipe/storage/user.py @@ -57,14 +57,12 @@ class User(mongoengine.Document): meta = {"db_alias": "core", "collection": "users", "indexes": ["name", "email"]} - def __repr__(self): """Repr of instance for quick debugging purposes.""" return "".format(self.username, self.email) - @staticmethod - def login(email:str, password_hash:str) -> "User": + def login(email: str, password_hash: str) -> "User": """ Logs in and returns the user. @@ -79,10 +77,9 @@ def login(email:str, password_hash:str) -> "User": if not user: raise UserNotFoundError(email) if password_hash != user.password_hash: - raise UserLoginError('incorrect password') + raise UserLoginError("incorrect password") return user - @staticmethod def list_users() -> List["User"]: """ @@ -94,7 +91,6 @@ def list_users() -> List["User"]: """ return list(User.objects()) - def add_recipe(self, recipe: Recipe) -> int: """ Adds a recipe reference to the user's recipes. @@ -107,9 +103,9 @@ def add_recipe(self, recipe: Recipe) -> int: result = self.update(add_to_set__recipe_ids=recipe.id) if result: self._update_last_mod_date() + self.reload() return result - def _update_last_mod_date(self) -> int: """ Updates the user's "last_updated_date" attribute in the DB. @@ -118,8 +114,9 @@ def _update_last_mod_date(self) -> int: :returns: (int) 1 for success, 0 if unsuccessful """ - return self.update(last_modified_date=datetime.datetime.utcnow()) - + result = self.update(last_modified_date=datetime.datetime.utcnow()) + self.reload() + return result def save(self) -> int: """ diff --git a/pyrecipe/tradingpost/export_doc.py b/pyrecipe/tradingpost/export_doc.py index 719cdf0..bc0df79 100644 --- a/pyrecipe/tradingpost/export_doc.py +++ b/pyrecipe/tradingpost/export_doc.py @@ -103,7 +103,7 @@ def create_doc(self, recipes: List["Recipe"]) -> int: flowables.append(self._add_section("Directions", "Heading2")) for direction in recipe.directions: - flowables.append(self._add_sequence(recipe._id, direction)) + flowables.append(self._add_sequence(recipe.id, direction)) flowables.append(Spacer(0, 5)) if recipe.notes: diff --git a/tests/conftest.py b/tests/conftest.py index ed61123..e5952d2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,10 +5,10 @@ import pytest import mongoengine +from pyrecipe.storage import Recipe +from pyrecipe.storage import Ingredient +from pyrecipe.storage import User -############################################################################## -# test_storage_*.py fixtures -############################################################################## @pytest.fixture(scope='session') def mongodb(request): @@ -22,102 +22,34 @@ def mongodb(request): db.close() -############################################################################## -# test_cookbook_*.py fixtures -############################################################################## - -class Fake_User: - """Fake storage.User for testing.""" - def __init__(self, _id=123, name='Tester', email='tester@here.com', - created_date=datetime.datetime.utcnow(), last_modified_date=datetime.datetime.utcnow(), - recipes=[], view='list', page_size=100, email_distros={}): - self.id = _id - self._id = _id - self.name = name - self.email = email - self.created_date = created_date - self.last_modified_date = last_modified_date - self.recipe_ids = recipes - self.shared_recipe_ids = [] - self.view = view - self.page_size = page_size - self.email_distros = email_distros - - def save(self): - pass - - def update(self, add_to_set__recipes=None, last_modified_date=None): - if add_to_set__recipes == 'good' or isinstance(last_modified_date, datetime.datetime): - return 1 - else: - return 0 - -class Fake_Ingredient: - """Fake storage.Ingredient for testing.""" - def __init__(self, name='Test_Ingr', quantity='1', unit='tsp', preparation=''): - self.name = name - self.quantity = quantity - self.unit = unit - self.preparation = preparation - - -class Fake_Recipe: - """Fake storage.Recipe for testing.""" - def __init__(self): - self.id = "123" - self.name = "Test_Rec" - self.ingredients = [Fake_Ingredient(name='Ingr1'), Fake_Ingredient(name='Igr2')] - self.num_ingredients = 2 - self.directions = ['cook'] - self.prep_time = 100.0 - self.cook_time = 110.0 - self.servings = 6 - self.tags = ['tag1', 'tag2'] - self.pictures = ['filepath1'] - self.notes = ['test note'] - self.rating = 4.5 - self.favorite = True - self.deleted = False - self.created_date = datetime.datetime.utcnow() - self.last_modified_date = datetime.datetime.utcnow() - - def save(self): - pass - - def update(self, add_to_set__tags=None, pull__tags=None, last_modified_date=None, deleted=None): - if add_to_set__tags == 'good': - self.tags.append('good') - return 1 - elif pull__tags == 'good': - try: - self.tags.remove('good') - except ValueError: - pass - return 1 - elif isinstance(last_modified_date, datetime.datetime): - return 1 - elif deleted == True: - self.deleted = True - return 1 - elif deleted == False: - self.deleted = False - return 1 - else: - return 0 +@pytest.fixture(scope="function") +def recipe_setup(mongodb): + """ + Fixture to get the DB up and return a recipe. Delete + the recipe upon test completion. + """ + db = mongodb + recipe = Recipe() + yield recipe + recipe.delete() @pytest.fixture(scope='function') -def get_user(): - """Return an instance of Fake_User for testing.""" - return Fake_User() +def get_ingredients(): + ingredient = Ingredient() + ingredient.name = 'test ingredient' + ingredient.quantity = '1' + ingredient.unit = 'tsp' + return ingredient @pytest.fixture(scope='function') -def get_recipe(): - """Return an instance of Fake_Recipe for testing.""" - return Fake_Recipe() - -@pytest.fixture(scope='function') -def get_ingredient(): - """Return an instance of Fake_Recipe for testing.""" - return Fake_Ingredient() +def user_setup(mongodb): + """ + Fixture to get the DB up and return a user. Delete + the user upon test completion. + """ + db = mongodb + user = User() + yield user + user.delete() diff --git a/tests/test_cookbook_recipe.py b/tests/test_cookbook_recipe.py deleted file mode 100644 index 30b07c3..0000000 --- a/tests/test_cookbook_recipe.py +++ /dev/null @@ -1,245 +0,0 @@ -""" -Tests for the pyrecipe.cookbook.recipe.py module. - -"get_recipe" is a fixture defined in conftest.py -"get_ingredient" is a fixture defined in conftest.py -""" - -import collections.abc -import datetime - -import pytest - -from pyrecipe.cookbook.recipe import RecipeMongo as Recipe -from pyrecipe.cookbook.recipe import db - - -def test_recipe_init(get_recipe): - """ - GIVEN a Recipe instance - WHEN instantiated - THEN assert it is correctly created - """ - recipe = Recipe(get_recipe) - assert recipe._id == "123" - assert recipe.name == "Test_Rec" - assert isinstance(recipe.ingredients, collections.abc.MutableSequence) - assert recipe.num_ingredients == 2 - assert recipe.directions == ['cook'] - assert recipe.prep_time == 100.0 - assert recipe.cook_time == 110.0 - assert recipe.servings == 6 - assert recipe.tags == ['tag1', 'tag2'] - assert recipe.pictures == ['filepath1'] - assert recipe.notes == ['test note'] - assert recipe.rating == 4.5 - assert recipe.favorite == True - assert recipe.deleted == False - assert isinstance(recipe.created_date, datetime.datetime) - assert isinstance(recipe.last_modified_date, datetime.datetime) - - -def test_create_recipe(get_recipe, get_ingredient, mocker): - """ - GIVEN a need to create a Recipe - WHEN Recipe.create_recipe is called with valid params - THEN assert a Recipe is returned - """ - db_rec_mock = mocker.patch.object(db, 'Recipe') - db_rec_mock.return_value = get_recipe - - db_igr_mock = mocker.patch.object(db, 'Ingredient') - db_igr_mock.return_value = get_ingredient - - name = 'TESTER' - ingredients = [{'name': 'TEST', 'quantity': '1', 'unit': 'TSP'}] - directions = ['Cook'] - - recipe = Recipe.create_recipe(name, ingredients, directions) - assert isinstance(recipe, Recipe) - assert recipe.name == 'tester' - assert recipe.ingredients[0].name == 'test' - assert recipe.ingredients[0].quantity == '1' - assert recipe.ingredients[0].unit == 'tsp' - assert recipe.num_ingredients == 1 - assert recipe.directions == ['Cook'] - - -def test_fetch_recipe_exists(get_recipe, mocker): - """ - GIVEN a need to fetch a recipe by name - WHEN Recipe.fetch_recipe is called with an existing recipe.name - THEN assert a Recipe is returned - """ - db_mock = mocker.patch.object(db, 'Recipe') - db_mock.objects.return_value.filter.return_value.first.return_value = get_recipe - - recipe = Recipe.fetch_recipe(name='test') - assert isinstance(recipe, Recipe) - - - -def test_fetch_recipe_NOTexists(get_recipe, mocker): - """ - GIVEN a need to fetch a recipe by name - WHEN Recipe.fetch_recipe is called with a non-existing recipe.name - THEN assert None is returned - """ - db_mock = mocker.patch.object(db, 'Recipe') - db_mock.objects.return_value.filter.return_value.first.return_value = None - - recipe = Recipe.fetch_recipe(name='test') - assert recipe is None - - -def test_copy_recipe(get_recipe, mocker): - """ - GIVEN a need to copy a recipe - WHEN Recipe.copy_recipe is called with a recipe instance - THEN assert a copy is returned with a new name - """ - db_mock = mocker.patch.object(db, 'Recipe') - db_mock.return_value = get_recipe - - rec_to_copy = get_recipe - rec_to_copy.name = 'unique name' - - recipe = Recipe.copy_recipe(rec_to_copy) - assert isinstance(recipe, Recipe) - assert recipe.name == 'unique name_COPY' - assert recipe.ingredients == rec_to_copy.ingredients - assert recipe.directions == rec_to_copy.directions - assert recipe.prep_time == rec_to_copy.prep_time - assert recipe.cook_time == rec_to_copy.cook_time - assert recipe.servings == rec_to_copy.servings - assert recipe.tags == rec_to_copy.tags - assert recipe.pictures == rec_to_copy.pictures - assert recipe.notes == rec_to_copy.notes - assert recipe.rating == rec_to_copy.rating - assert recipe.favorite == rec_to_copy.favorite - - -@pytest.mark.parametrize('attr, value', [ - ('name', 'new_name'), - ('prep_time', 105.0), - ('cook_time', 110.0), - ('tags', ['t1']), - ('pictures', ['path1']), - ('notes', ['note1']), - ('rating', 3.5), - ('favorite', False), - ('ingredients', ['ingredient']), -]) -def test_update_recipe_data(get_recipe, attr, value, mocker): - """ - GIVEN a Recipe instance - WHEN recipe.update_recipe_data is called with a valid data dict - THEN assert correct return value is returned - """ - update_mock = mocker.patch.object(Recipe, '_update_last_mod_date') - refresh_mock = mocker.patch.object(Recipe, '_refresh_recipe') - - recipe = Recipe(get_recipe) - - result = recipe.update_recipe_data({attr: value}) - assert result == 1 - assert update_mock.call_count == 1 - assert refresh_mock.call_count == 1 - - -def test_delete_recipe(get_recipe, mocker): - """ - GIVEN a Recipe instance - WHEN recipe.delete_recipe is called - THEN assert it is set to deleted and 1 is returned - """ - update_mock = mocker.patch.object(Recipe, '_update_last_mod_date') - refresh_mock = mocker.patch.object(Recipe, '_refresh_recipe') - - recipe = Recipe(get_recipe) - - result = recipe.delete_recipe() - assert result == 1 - #assert recipe.deleted == True - assert update_mock.call_count == 1 - assert refresh_mock.call_count == 1 - - -def test_restore_recipe(get_recipe, mocker): - """ - GIVEN a Recipe instance - WHEN recipe.restore_recipe is called - THEN assert it is set to deleted and 1 is returned - """ - update_mock = mocker.patch.object(Recipe, '_update_last_mod_date') - refresh_mock = mocker.patch.object(Recipe, '_refresh_recipe') - - recipe = Recipe(get_recipe) - - result = recipe.restore_recipe() - assert result == 1 - #assert recipe.deleted == False - assert update_mock.call_count == 1 - assert refresh_mock.call_count == 1 - - -def test_add_tag(get_recipe, mocker): - """ - GIVEN a Recipe instance - WHEN recipe.add_tag is called - THEN assert it is added and 1 is returned - """ - update_mock = mocker.patch.object(Recipe, '_update_last_mod_date') - refresh_mock = mocker.patch.object(Recipe, '_refresh_recipe') - - recipe = Recipe(get_recipe) - - result = recipe.add_tag('good') - assert result == 1 - #assert 'good' in recipe.tags - assert update_mock.call_count == 1 - assert refresh_mock.call_count == 1 - - -def test_delete_tag(get_recipe, mocker): - """ - GIVEN a Recipe instance - WHEN recipe.delete_tag is called - THEN assert it is removed and 1 is returned - """ - update_mock = mocker.patch.object(Recipe, '_update_last_mod_date') - refresh_mock = mocker.patch.object(Recipe, '_refresh_recipe') - - recipe = Recipe(get_recipe) - - result = recipe.delete_tag('good') - assert result == 1 - #assert 'good' not in recipe.tags - assert update_mock.call_count == 1 - assert refresh_mock.call_count == 1 - - -def test_refresh_recipe(get_recipe, mocker): - """ - GIVEN a Recipe instance - WHEN recipe._refresh_recipe is called - THEN assert a mocked DB recipe document is returned - """ - db_mock = mocker.patch.object(db, 'Recipe') - db_mock.objects.return_value.filter.return_value.first.return_value = get_recipe - - recipe = Recipe(get_recipe) - - result = recipe._refresh_recipe() - assert result.__class__.__name__ == 'Fake_Recipe' - - -def test_update_last_mod_date(get_recipe, mocker): - """ - GIVEN a Recipe instance - WHEN recipe._update_last_mod_date is called and successfully entered in DB - THEN assert return=1 - """ - recipe = Recipe(get_recipe) - result = recipe._update_last_mod_date() - assert result == 1 diff --git a/tests/test_cookbook_user.py b/tests/test_cookbook_user.py deleted file mode 100644 index f44fbf1..0000000 --- a/tests/test_cookbook_user.py +++ /dev/null @@ -1,181 +0,0 @@ -""" -Tests for the pyrecipe.cookbook.user.py module. - -"get_user" is a fixture defined in conftest.py -""" - -import collections.abc -import datetime - -import pytest - -from pyrecipe.cookbook.user import UserMongo as User -from pyrecipe.cookbook.user import db -from pyrecipe.errors import UserNotFoundError -from pyrecipe.errors import UserCreationError - - -def test_user_init(get_user): - """ - GIVEN a User instance - WHEN instantiated - THEN assert it is correctly created - """ - user = User(get_user) - assert user._id == '123' - assert user.name == 'Tester' - assert user.email == 'tester@here.com' - assert isinstance(user.created_date, datetime.datetime) - assert isinstance(user.last_modified_date, datetime.datetime) - assert user.recipes == [] - assert user.shared_recipes == [] - assert user.view == 'list' - assert user.page_size == 100 - assert user.email_distros == {} - -def test_create_user(get_user, mocker): - """ - GIVEN a need to create a User - WHEN User.create_user is called with valid params - THEN assert a User is returned - """ - db_mock = mocker.patch.object(db, 'User') - db_mock.return_value = get_user - db_mock.objects.return_value.filter.return_value.count.return_value = 0 - - user = User.create_user(name='test', email='fake') - assert isinstance(user, User) - - -def test_create_user_raisesExc(mocker): - """ - GIVEN a need to create a user - WHEN User.create_user is called with an email address already in use - THEN assert UserCreationError exception is raised - """ - db_mock = mocker.patch.object(db, 'User') - db_mock.objects.return_value.filter.return_value.count.return_value = 1 - - with pytest.raises(UserCreationError): - user = User.create_user() - -def test_login_user(get_user, mocker): - """ - GIVEN a need to login a user - WHEN User.login_user is called with valid params - THEN assert a User is returned - """ - db_mock = mocker.patch.object(db, 'User') - db_mock.objects.return_value.filter.return_value.first.return_value = get_user - - user = User.login_user(email='fake') - assert isinstance(user, User) - -def test_login_user_raisesExc(mocker): - """ - GIVEN a need to login a user - WHEN User.login_user is called with valid params but not found in the DB - THEN assert a UserNotFoundError is raised - """ - db_mock = mocker.patch.object(db, 'User') - db_mock.objects.return_value.filter.return_value.first.return_value = None - - with pytest.raises(UserNotFoundError): - user = User.login_user(email='fake') - -def test_list_users(mocker): - """ - GIVEN a need to list all users - WHEN User.list_users() is called - THEN assert a list is returned - """ - db_mock = mocker.patch.object(db, 'User') - db_mock.objects.return_value = [] - - users = User.list_users() - assert isinstance(users, collections.abc.MutableSequence) - - -def test_update_user_data(get_user, mocker): - """ - GIVEN a User instance - WHEN user.udpate_user_data(dict) is called - THEN assert correct calls are made and correct count is returned - """ - update_mock = mocker.patch.object(User, '_update_last_mod_date') - refresh_mock = mocker.patch.object(User, '_refresh_user') - - user = User(get_user) - - data = {'name': 'new_name', 'email': 'new_email', 'view': 'new_view', - 'page_size': 'new_page', 'email_distros': 'new_distros'} - - count = user.update_user_data(data) - assert count == 5 - assert update_mock.call_count == 1 - assert refresh_mock.call_count == 1 - - -def test_add_recipe_successful(get_user, mocker): - """ - GIVEN a User instance - WHEN user.add_recipe(Recipe) is called and successfully inserts into the DB - THEN assert return=1 and correct calls are made - """ - update_mock = mocker.patch.object(User, '_update_last_mod_date') - refresh_mock = mocker.patch.object(User, '_refresh_user') - - user = User(get_user) - - class Recipe: - id = 'good' - - result = user.add_recipe(Recipe()) - assert result == 1 - assert refresh_mock.call_count == 1 - assert update_mock.call_count == 1 - - -def test_add_recipe_unsuccessful(get_user, mocker): - """ - GIVEN a User instance - WHEN user.add_recipe(Recipe) is called and unsuccessfully inserts into the DB - THEN assert return=0 and correct calls are made - """ - update_mock = mocker.patch.object(User, '_update_last_mod_date') - refresh_mock = mocker.patch.object(User, '_refresh_user') - - user = User(get_user) - - class Recipe: - id = 'bad' - - result = user.add_recipe(Recipe()) - assert result == 0 - assert refresh_mock.call_count == 0 - assert update_mock.call_count == 0 - - -def test_refresh_user(get_user, mocker): - """ - GIVEN a User instance - WHEN user._refresh_user is called - THEN assert a mocked DB user document is returned - """ - db_mock = mocker.patch.object(db, 'User') - db_mock.objects.return_value.filter.return_value.first.return_value = get_user - - user = User(get_user) - result = user._refresh_user() - assert result.__class__.__name__ == 'Fake_User' - - -def test_update_last_mod_date(get_user, mocker): - """ - GIVEN a User instance - WHEN user._update_last_mod_date is called and successfully entered in DB - THEN assert return=1 - """ - user = User(get_user) - result = user._update_last_mod_date() - assert result == 1 diff --git a/tests/test_storage_recipe.py b/tests/test_storage_recipe.py index f899265..34635fa 100644 --- a/tests/test_storage_recipe.py +++ b/tests/test_storage_recipe.py @@ -1,36 +1,29 @@ """ Tests for the pyrecipe.storage.recipe.py module. -"mongodb" is a fixture defined in conftest.py +Fixtures found in conftest.py +1. mongodb +2. recipe_setup +3. get_ingredients """ import datetime -import pytest import mongoengine +import pytest from pyrecipe.storage.recipe import Recipe from pyrecipe.storage.recipe import Ingredient -@pytest.fixture(scope='function') -def get_ingredients(): - ingredient = Ingredient() - ingredient.name = 'test ingredient' - ingredient.quantity = '1' - ingredient.unit = 'tsp' - return ingredient - - -def test_recipe_creation_defaults(get_ingredients, mongodb): +def test_recipe_creation_defaults(recipe_setup, get_ingredients): """ GIVEN a mongodb instance WHEN a Recipe is added with only required values set, just using default values for non-required fields THEN assert a correct Recipe is added """ - db = mongodb - r = Recipe() + r = recipe_setup r.name = 'Yummy' r.ingredients = [get_ingredients] r.num_ingredients = 1 @@ -56,23 +49,119 @@ def test_recipe_creation_defaults(get_ingredients, mongodb): assert isinstance(r.last_modified_date, datetime.datetime) -@pytest.mark.parametrize('name, ingredients, num_ingredients, directions',[ - (None, get_ingredients, 1, ['cook']), - ('Yummy', None, 1, ['cook']), - ('Yummy', get_ingredients, None, ['cook']), - ('Yummy', get_ingredients, 1, None), -]) -def test_recipe_creation_raisesExc(name, ingredients, num_ingredients, directions, mongodb): +def test_recipe_repr(capsys, mongodb): + """ + GIVEN a mongodb instance + WHEN repr(recipe) is called on a Recipe instance + THEN assert the correct output prints + """ + db = mongodb + recipe = Recipe.objects().filter().first() + print(repr(recipe)) + out, err = capsys.readouterr() + assert "" in out + + +def test_recipe_find_recipes(mongodb): """ GIVEN a mongodb instance - WHEN a Recipe is added leaving a required field empty - THEN assert mongoengine.errors.ValidationError is raised + WHEN a search query is initiated with Recipe.find_recipes() + THEN assert the correct number of recipes are returned """ db = mongodb - r = Recipe() - r.name = name - r.ingredients = ingredients - r.num_ingredients = num_ingredients - r.directions = directions - with pytest.raises(mongoengine.errors.ValidationError): - r.save() + search1 = Recipe.find_recipes(search_string="spam") + search2 = Recipe.find_recipes(search_string="SPAM") + search3 = Recipe.find_recipes(search_string="egg") + + assert len(search1) == 9 + assert len(search2) == 9 + assert len(search3) == 1 + + +def test_recipe_copy_recipe(mongodb): + """ + GIVEN a mongodb instance + WHEN recipe.copy_recipe() is called + THEN assert a copy is returned with all the correct attributes + """ + db = mongodb + recipe = Recipe.objects().filter().first() + new_recipe = recipe.copy_recipe() + + assert new_recipe.id != recipe.id + assert new_recipe.name == recipe.name + "_COPY" + assert new_recipe.num_ingredients == recipe.num_ingredients + assert new_recipe.ingredients == recipe.ingredients + assert new_recipe.directions == recipe.directions + assert new_recipe.prep_time == recipe.prep_time + assert new_recipe.cook_time == recipe.cook_time + assert new_recipe.servings == recipe.servings + assert new_recipe.tags == recipe.tags + assert new_recipe.pictures == recipe.pictures + assert new_recipe.notes == recipe.notes + assert new_recipe.rating == recipe.rating + assert new_recipe.created_date != recipe.created_date + assert new_recipe.last_modified_date != recipe.last_modified_date + new_recipe.delete() + + +def test_recipe_add_delete_tag(mongodb): + """ + GIVEN a mongodb instance + WHEN a tag is added and then deleted + THEN assert it was added then later deleted + """ + db = mongodb + recipe = Recipe.objects().filter().first() + + assert "test_tag" not in recipe.tags + + result = recipe.add_tag("test_tag") + assert "test_tag" in recipe.tags + assert result == 1 + + result = recipe.delete_tag("test_tag") + assert "test_tag" not in recipe.tags + assert result == 1 + + +def test_recipe_update_last_mod_date(mongodb): + """ + GIVEN a mongodb instance + WHEN recipe._update_last_mod_date is called + THEN assert the a new date is inserted + """ + db = mongodb + recipe = Recipe.objects().filter().first() + + result = recipe._update_last_mod_date() + assert result == 1 + assert isinstance(recipe.last_modified_date, datetime.datetime) + + +def test_recipe_save_goodID(mongodb, mocker): + """ + GIVEN a mongodb instance + WHEN recipe.save() is called on an already existing record + THEN assert the record is saved with an updated modification date + """ + date_mock = mocker.patch.object(Recipe, '_update_last_mod_date') + recipe = Recipe.objects().filter().first() + recipe.save() + assert date_mock.call_count == 1 + + +def test_recipe_save_noID(recipe_setup, get_ingredients, mocker): + """ + GIVEN a mongodb instance + WHEN recipe.save() is called on an newly created record + THEN assert the record is saved without an updated modification date + """ + date_mock = mocker.patch.object(Recipe, '_update_last_mod_date') + recipe = recipe_setup + recipe.name = 'test' + recipe.ingredients = [get_ingredients] + recipe.num_ingredients = 1 + recipe.directions = ['do a test!'] + recipe.save() + assert date_mock.call_count == 0 diff --git a/tests/test_storage_user.py b/tests/test_storage_user.py index e8ba9bd..0ba15dc 100644 --- a/tests/test_storage_user.py +++ b/tests/test_storage_user.py @@ -1,7 +1,9 @@ """ Tests for the pyrecipe.storage.user.py module. -"mongodb" is a fixture defined in conftest.py +Fixtures found in conftest.py +1. mongodb +2. user_setup """ import datetime @@ -9,22 +11,11 @@ import pytest from pyrecipe.storage.user import User +from pyrecipe.storage import Recipe from pyrecipe.errors import UserNotFoundError from pyrecipe.errors import UserLoginError -@pytest.fixture() -def user_setup(mongodb): - """ - Fixture to get the DB up and return a user. Delete - the user upon test completion. - """ - db = mongodb - user = User() - yield user - user.delete() - - def test_user_creation_defaults(user_setup): """ GIVEN a mongodb instance @@ -130,7 +121,7 @@ def test_user_list_users(mongodb): assert len(users) == 3 -def test_add_recipe(mongodb, mocker): +def test_user_add_recipe(mongodb, mocker): """ GIVEN a mongodb instance WHEN user.add_recipe is called @@ -140,19 +131,21 @@ def test_add_recipe(mongodb, mocker): date_mock = mocker.patch.object(User, '_update_last_mod_date') db = mongodb - users = User.list_users() + user = User.objects().filter().first() + recipe = list(Recipe.objects())[-1] - num_recipes = len(users[0].recipe_ids) + num_recipes = len(user.recipe_ids) - result = users[0].add_recipe(users[1].recipe_ids[0]) + result = user.add_recipe(recipe) assert result == 1 - #assert len(users[0].recipe_ids) == num_recipes + 1 + assert len(user.recipe_ids) == num_recipes + 1 assert date_mock.call_count == 1 - del users[0].recipe_ids[-1] - users[0].save() + user.recipe_ids.pop() + assert len(user.recipe_ids) == num_recipes + user.save() -def test_update_last_mod_date(mongodb): +def test_user_update_last_mod_date(mongodb): """ GIVEN a mongodb instance WHEN user._update_last_mod_date is called @@ -166,7 +159,7 @@ def test_update_last_mod_date(mongodb): assert isinstance(user.last_modified_date, datetime.datetime) -def test_save_goodID(mongodb, mocker): +def test_user_save_goodID(mongodb, mocker): """ GIVEN a mongodb instance WHEN user.save() is called on an already existing record @@ -178,7 +171,7 @@ def test_save_goodID(mongodb, mocker): assert date_mock.call_count == 1 -def test_save_noID(mongodb, mocker): +def test_user_save_noID(mongodb, mocker): """ GIVEN a mongodb instance WHEN user.save() is called on an newly created record diff --git a/tests/test_a_tradingpost_export_doc.py b/tests/test_tradingpost_export_doc.py similarity index 80% rename from tests/test_a_tradingpost_export_doc.py rename to tests/test_tradingpost_export_doc.py index a2ef5cf..4f254ab 100644 --- a/tests/test_a_tradingpost_export_doc.py +++ b/tests/test_tradingpost_export_doc.py @@ -1,16 +1,19 @@ -"""Tests for the pyrecipe.tradingpost.export_doc.py module.""" +""" +Tests for the pyrecipe.tradingpost.export_doc.py module. + +"mongodb" is a fixture defined in conftest.py +""" import datetime import pathlib -import mongoengine import pytest import pyrecipe.tradingpost.export_doc from pyrecipe.tradingpost.export_doc import FileWriter from pyrecipe.tradingpost.export_doc import export_to_pdf from pyrecipe.tradingpost.export_doc import SimpleDocTemplate, getSampleStyleSheet -from pyrecipe.cookbook import User, Recipe +from pyrecipe.storage import User, Recipe def test_fw_init_mocked(mocker): @@ -40,17 +43,16 @@ def test_fw_init(): assert isinstance(fw.styles, getSampleStyleSheet().__class__) -def test_fw_create_doc(tmpdir): +def test_fw_create_doc(mongodb, tmpdir): """ GIVEN a db with users and recipes WHEN a user's selected recipes are selected for export THEN assert FileWriter writes a pdf file """ - db = mongoengine.connect(db='pyrecipe_tester', alias='core', host='mongodb://localhost') + db = mongodb - user = User.login_user(email="blackknight@mail.com") - recipes = [Recipe(r) for r in user.recipes] - db.close() + user = User.login(email="blackknight@mail.com", password_hash="Not Implemented") + recipes = user.recipe_ids testdir = pathlib.Path(tmpdir).absolute() testfile = testdir.joinpath('pyrecipe_test1.pdf') @@ -78,17 +80,16 @@ def test_export_to_pdf_mocked(mocker): assert meta_mock.call_count == 1 -def test_export_to_pdf(tmpdir): +def test_export_to_pdf(mongodb, tmpdir): """ GIVEN a call to export_to_pdf WHEN supplied with the correct params THEN assert the file is written and the correct return values returned """ - db = mongoengine.connect(db='pyrecipe_tester', alias='core', host='mongodb://localhost') + db = mongodb - user = User.login_user(email="blackknight@mail.com") - recipes = [Recipe(r) for r in user.recipes] - db.close() + user = User.login(email="blackknight@mail.com", password_hash="Not Implemented") + recipes = user.recipe_ids testdir = pathlib.Path(tmpdir).absolute() testfile = testdir.joinpath('pyrecipe_test1.pdf') diff --git a/tests/test_b_tradingpost_import_doc.py b/tests/test_tradingpost_import_doc.py similarity index 87% rename from tests/test_b_tradingpost_import_doc.py rename to tests/test_tradingpost_import_doc.py index b12949e..4482d38 100644 --- a/tests/test_b_tradingpost_import_doc.py +++ b/tests/test_tradingpost_import_doc.py @@ -1,7 +1,9 @@ """ Tests for the pyrecipe.tradingpost.import_doc.py module. -"get_recipe" is a fixture defined in conftest.py +Fixtures found in conftest.py +1. recipe_setup +2. get_ingredients """ import pathlib @@ -12,6 +14,7 @@ import pyrecipe.tradingpost.import_doc from pyrecipe.tradingpost.import_doc import import_from_pdf from pyrecipe.tradingpost.import_doc import _import_recipe +from pyrecipe.storage import Recipe @@ -73,21 +76,28 @@ def test_import_from_pdf_verbose(mocker, capsys): assert imp_rec_mock.call_count == 3 -def test_import_recipes_verbose(get_recipe, mocker, capsys): +def test_import_recipes_verbose(recipe_setup, get_ingredients, mocker, capsys): """ GIVEN a recipe in a pdf metadata WHEN import_recipe is called with verbose=True THEN assert the assert the correct sequence is called, the correct output prints, and the functions returns the correct value """ + recipe = recipe_setup + recipe.name = 'test' + recipe.ingredients = [get_ingredients] + recipe.num_ingredients = 1 + recipe.directions = ['do a test!'] + recipe.save() + rec_mock = mocker.patch.object(pyrecipe.tradingpost.import_doc, "Recipe") - rec_mock.return_value = get_recipe + rec_mock.return_value = recipe metadata = pikepdf.open(TESTFILE).open_metadata() result = _import_recipe(metadata, 1, verbose=True) out, err = capsys.readouterr() - assert result == "123" + assert result == recipe.id assert "+ Adding Recipe 1: hearty spam breakfast skillet" in out assert "" in out assert "" in out diff --git a/tests/testing_data/mongodb/recipes.bson b/tests/testing_data/mongodb/recipes.bson index e0c0608..49a930a 100644 Binary files a/tests/testing_data/mongodb/recipes.bson and b/tests/testing_data/mongodb/recipes.bson differ diff --git a/tests/testing_data/mongodb/recipes.metadata.json b/tests/testing_data/mongodb/recipes.metadata.json index 32c19bc..a70823b 100644 --- a/tests/testing_data/mongodb/recipes.metadata.json +++ b/tests/testing_data/mongodb/recipes.metadata.json @@ -1 +1 @@ -{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"pyrecipe_tester.recipes"},{"v":2,"key":{"name":1},"name":"name_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"num_ingredients":1},"name":"num_ingredients_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"prep_time":1},"name":"prep_time_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"cook_time":1},"name":"cook_time_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"tags":1},"name":"tags_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"favorite":1},"name":"favorite_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"deleted":1},"name":"deleted_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"ingredients.name":1},"name":"ingredients.name_1","ns":"pyrecipe_tester.recipes","background":false}],"uuid":"c95c0eb7591a4be8be550a5e86ab3f03"} \ No newline at end of file +{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"pyrecipe_tester.recipes"},{"v":2,"key":{"name":1},"name":"name_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"num_ingredients":1},"name":"num_ingredients_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"prep_time":1},"name":"prep_time_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"cook_time":1},"name":"cook_time_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"servings":1},"name":"servings_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"tags":1},"name":"tags_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"rating":1},"name":"rating_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"favorite":1},"name":"favorite_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"deleted":1},"name":"deleted_1","ns":"pyrecipe_tester.recipes","background":false},{"v":2,"key":{"ingredients.name":1},"name":"ingredients.name_1","ns":"pyrecipe_tester.recipes","background":false}],"uuid":"1b99807e074c41c2b0076dfb6b14512a"} \ No newline at end of file diff --git a/tests/testing_data/mongodb/users.bson b/tests/testing_data/mongodb/users.bson index bb91d29..44bfb9e 100644 Binary files a/tests/testing_data/mongodb/users.bson and b/tests/testing_data/mongodb/users.bson differ diff --git a/tests/testing_data/mongodb/users.metadata.json b/tests/testing_data/mongodb/users.metadata.json index 5ad634e..ad09497 100644 --- a/tests/testing_data/mongodb/users.metadata.json +++ b/tests/testing_data/mongodb/users.metadata.json @@ -1 +1 @@ -{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"pyrecipe_tester.users"},{"v":2,"key":{"name":1},"name":"name_1","ns":"pyrecipe_tester.users","background":false},{"v":2,"key":{"email":1},"name":"email_1","ns":"pyrecipe_tester.users","background":false}],"uuid":"daea5282cf5b4eaea0adfa93ef32f0a7"} \ No newline at end of file +{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"pyrecipe_tester.users"},{"v":2,"key":{"name":1},"name":"name_1","ns":"pyrecipe_tester.users","background":false},{"v":2,"key":{"email":1},"name":"email_1","ns":"pyrecipe_tester.users","background":false}],"uuid":"ed93c01021764085919187674683e2ba"} \ No newline at end of file