Repository: SebRut/pygrocy (python)

Issue:
Misnaming in enum
Should be `who-least-did-first`

https://github.com/SebRut/pygrocy/blob/066993de96dcae02661eb169e59dfda852a73c8f/pygrocy/grocy.py#L185

Traceback (most recent call last):
  File "/config/custom_components/grocy/__init__.py", line 227, in async_update_chores
    ] = await self.hass.async_add_executor_job(wrapper)
  File "/usr/local/lib/python3.8/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/grocy/__init__.py", line 223, in wrapper
    return self.client.chores(True)
  File "/usr/local/lib/python3.8/site-packages/pygrocy/grocy.py", line 461, in chores
    chore.get_details(self._api_client)
  File "/usr/local/lib/python3.8/site-packages/pygrocy/grocy.py", line 243, in get_details
    self._init_from_ChoreDetailsResponse(details)
  File "/usr/local/lib/python3.8/site-packages/pygrocy/grocy.py", line 221, in _init_from_ChoreDetailsResponse
    self._assignment_type = Chore.AssignmentType(chore_data.assignment_type)
  File "/usr/local/lib/python3.8/enum.py", line 304, in __call__
    return cls.__new__(cls, value)
  File "/usr/local/lib/python3.8/enum.py", line 595, in __new__
    raise exc
  File "/usr/local/lib/python3.8/enum.py", line 579, in __new__
    result = cls._missing_(value)
  File "/usr/local/lib/python3.8/enum.py", line 608, in _missing_
    raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: 'who-least-did-first' is not a valid AssignmentType

Pull request:
fix issue #112
fixes #112


Patch:
diff --git a/pygrocy/grocy.py b/pygrocy/grocy.py
index 1b344f6..6964abe 100644
--- a/pygrocy/grocy.py
+++ b/pygrocy/grocy.py
@@ -4,14 +4,22 @@ from enum import Enum
 from typing import List, Dict
 
 from .base import DataModel
-from .grocy_api_client import (DEFAULT_PORT_NUMBER, ChoreDetailsResponse,
-                               CurrentChoreResponse, CurrentStockResponse,
-                               GrocyApiClient,
-                               LocationData, MissingProductResponse,
-                               ProductDetailsResponse,
-                               MealPlanResponse,
-                               RecipeDetailsResponse,
-                               ShoppingListItem, TransactionType, UserDto, TaskResponse)
+from .grocy_api_client import (
+    DEFAULT_PORT_NUMBER,
+    ChoreDetailsResponse,
+    CurrentChoreResponse,
+    CurrentStockResponse,
+    GrocyApiClient,
+    LocationData,
+    MissingProductResponse,
+    ProductDetailsResponse,
+    MealPlanResponse,
+    RecipeDetailsResponse,
+    ShoppingListItem,
+    TransactionType,
+    UserDto,
+    TaskResponse,
+)
 
 
 class Product(DataModel):
@@ -172,20 +180,22 @@ class User(DataModel):
         return self._display_name
 
 
-class Chore(DataModel):
-    class PeriodType(str, Enum):
-        MANUALLY = 'manually'
-        DYNAMIC_REGULAR = 'dynamic-regular'
-        DAILY = 'daily'
-        WEEKLY = 'weekly'
-        MONTHLY = 'monthly'
-
-    class AssignmentType(str, Enum):
-        NO_ASSIGNMENT = 'no-assignment'
-        WHO_DID_LEAST_DID_FIRST = 'who-did-least-did-first'
-        RANDOM = 'random'
-        IN_ALPHABETICAL_ORDER = 'in-alphabetical-order'
+class PeriodType(str, Enum):
+    MANUALLY = "manually"
+    DYNAMIC_REGULAR = "dynamic-regular"
+    DAILY = "daily"
+    WEEKLY = "weekly"
+    MONTHLY = "monthly"
+
+
+class AssignmentType(str, Enum):
+    NO_ASSIGNMENT = "no-assignment"
+    WHO_LEAST_DID_FIRST = "who-least-did-first"
+    RANDOM = "random"
+    IN_ALPHABETICAL_ORDER = "in-alphabetical-order"
+
 
+class Chore(DataModel):
     def __init__(self, response):
         if isinstance(response, CurrentChoreResponse):
             self._init_from_CurrentChoreResponse(response)
@@ -208,7 +218,7 @@ class Chore(DataModel):
         self._description = chore_data.description
 
         if chore_data.period_type is not None:
-            self._period_type = Chore.PeriodType(chore_data.period_type)
+            self._period_type = PeriodType(chore_data.period_type)
         else:
             self._period_type = None
 
@@ -218,12 +228,14 @@ class Chore(DataModel):
         self._rollover = chore_data.rollover
 
         if chore_data.assignment_type is not None:
-            self._assignment_type = Chore.AssignmentType(chore_data.assignment_type)
+            self._assignment_type = AssignmentType(chore_data.assignment_type)
         else:
             self._assignment_type = None
 
         self._assignment_config = chore_data.assignment_config
-        self._next_execution_assigned_to_user_id = chore_data.next_execution_assigned_to_user_id
+        self._next_execution_assigned_to_user_id = (
+            chore_data.next_execution_assigned_to_user_id
+        )
         self._userfields = chore_data.userfields
 
         self._last_tracked_time = response.last_tracked
@@ -234,7 +246,9 @@ class Chore(DataModel):
             self._last_done_by = None
         self._track_count = response.track_count
         if response.next_execution_assigned_user is not None:
-            self._next_execution_assigned_user = User(response.next_execution_assigned_user)
+            self._next_execution_assigned_user = User(
+                response.next_execution_assigned_user
+            )
         else:
             self._next_execution_assigned_user = None
 
@@ -285,9 +299,9 @@ class Chore(DataModel):
     @property
     def next_execution_assigned_to_user_id(self) -> int:
         return self._next_execution_assigned_to_user_id
-    
+
     @property
-    def userfields(self) -> Dict[str,str]:
+    def userfields(self) -> Dict[str, str]:
         return self._userfields
 
     @property
@@ -367,7 +381,7 @@ class RecipeItem(DataModel):
 
     def get_picture_url_path(self, width: int = 400):
         if self.picture_file_name:
-            b64name = base64.b64encode(self.picture_file_name.encode('ascii'))
+            b64name = base64.b64encode(self.picture_file_name.encode("ascii"))
             path = "files/recipepictures/" + str(b64name, "utf-8")
 
             return f"{path}?force_serve_as=picture&best_fit_width={width}"
@@ -412,7 +426,9 @@ class MealPlanItem(DataModel):
 
 
 class Grocy(object):
-    def __init__(self, base_url, api_key, port: int = DEFAULT_PORT_NUMBER, verify_ssl=True):
+    def __init__(
+        self, base_url, api_key, port: int = DEFAULT_PORT_NUMBER, verify_ssl=True
+    ):
         self._api_client = GrocyApiClient(base_url, api_key, port, verify_ssl)
 
     def stock(self) -> List[Product]:
@@ -461,20 +477,40 @@ class Grocy(object):
                 chore.get_details(self._api_client)
         return chores
 
-    def execute_chore(self, chore_id: int, done_by: int = None, tracked_time: datetime = datetime.now()):
+    def execute_chore(
+        self,
+        chore_id: int,
+        done_by: int = None,
+        tracked_time: datetime = datetime.now(),
+    ):
         return self._api_client.execute_chore(chore_id, done_by, tracked_time)
 
     def chore(self, chore_id: int) -> Chore:
         resp = self._api_client.get_chore(chore_id)
         return Chore(resp)
 
-    def add_product(self, product_id, amount: float, price: float, best_before_date: datetime = None,
-                    transaction_type: TransactionType = TransactionType.PURCHASE):
-        return self._api_client.add_product(product_id, amount, price, best_before_date, transaction_type)
-
-    def consume_product(self, product_id: int, amount: float = 1, spoiled: bool = False,
-                        transaction_type: TransactionType = TransactionType.CONSUME):
-        return self._api_client.consume_product(product_id, amount, spoiled, transaction_type)
+    def add_product(
+        self,
+        product_id,
+        amount: float,
+        price: float,
+        best_before_date: datetime = None,
+        transaction_type: TransactionType = TransactionType.PURCHASE,
+    ):
+        return self._api_client.add_product(
+            product_id, amount, price, best_before_date, transaction_type
+        )
+
+    def consume_product(
+        self,
+        product_id: int,
+        amount: float = 1,
+        spoiled: bool = False,
+        transaction_type: TransactionType = TransactionType.CONSUME,
+    ):
+        return self._api_client.consume_product(
+            product_id, amount, spoiled, transaction_type
+        )
 
     def shopping_list(self, get_details: bool = False) -> List[ShoppingListProduct]:
         raw_shoppinglist = self._api_client.get_shopping_list()
@@ -488,14 +524,22 @@ class Grocy(object):
     def add_missing_product_to_shopping_list(self, shopping_list_id: int = 1):
         return self._api_client.add_missing_product_to_shopping_list(shopping_list_id)
 
-    def add_product_to_shopping_list(self, product_id: int, shopping_list_id: int = None, amount: int = None):
-        return self._api_client.add_product_to_shopping_list(product_id, shopping_list_id, amount)
+    def add_product_to_shopping_list(
+        self, product_id: int, shopping_list_id: int = None, amount: int = None
+    ):
+        return self._api_client.add_product_to_shopping_list(
+            product_id, shopping_list_id, amount
+        )
 
     def clear_shopping_list(self, shopping_list_id: int = 1):
         return self._api_client.clear_shopping_list(shopping_list_id)
 
-    def remove_product_in_shopping_list(self, product_id: int, shopping_list_id: int = 1, amount: int = 1):
-        return self._api_client.remove_product_in_shopping_list(product_id, shopping_list_id, amount)
+    def remove_product_in_shopping_list(
+        self, product_id: int, shopping_list_id: int = 1, amount: int = 1
+    ):
+        return self._api_client.remove_product_in_shopping_list(
+            product_id, shopping_list_id, amount
+        )
 
     def product_groups(self) -> List[Group]:
         raw_groups = self._api_client.get_product_groups()


Test patch:
diff --git a/test/test_grocy.py b/test/test_grocy.py
index e2a0a4b..2a742cc 100644
--- a/test/test_grocy.py
+++ b/test/test_grocy.py
@@ -8,19 +8,19 @@ import responses
 from requests.exceptions import HTTPError
 
 from pygrocy import Grocy
-from pygrocy.grocy import Chore, Product, Group, ShoppingListProduct
-from pygrocy.grocy_api_client import GrocyApiClient, UserDto, \
-    ProductData
+from pygrocy.grocy import Chore, Product, Group, ShoppingListProduct, AssignmentType
+from pygrocy.grocy_api_client import GrocyApiClient, UserDto, ProductData
 from test.test_const import CONST_BASE_URL, CONST_PORT, CONST_SSL
 
 
 class TestGrocy(TestCase):
-
     def setUp(self):
         self.grocy_regular = Grocy(CONST_BASE_URL, "api_key")
-        self.grocy = Grocy(CONST_BASE_URL, "demo_mode", verify_ssl=CONST_SSL, port=CONST_PORT)
+        self.grocy = Grocy(
+            CONST_BASE_URL, "demo_mode", verify_ssl=CONST_SSL, port=CONST_PORT
+        )
         self.base_url = f"{CONST_BASE_URL}:{CONST_PORT}/api"
-        self.date_test = datetime.strptime("2019-05-04 11:31:04", '%Y-%m-%d %H:%M:%S')
+        self.date_test = datetime.strptime("2019-05-04 11:31:04", "%Y-%m-%d %H:%M:%S")
 
     def test_init(self):
         self.assertIsInstance(self.grocy, Grocy)
@@ -31,7 +31,7 @@ class TestGrocy(TestCase):
 
         assert len(tasks) == 6
         assert tasks[0].id == 1
-        assert tasks[0].name == 'Repair the garage door'
+        assert tasks[0].name == "Repair the garage door"
 
     @unittest.skip("no chores_current table in current demo data")
     def test_get_chores_valid(self):
@@ -60,7 +60,7 @@ class TestGrocy(TestCase):
                 "period_config": null,
                 "track_date_only": "0",
                 "rollover": "0",
-                "assignment_type": null,
+                "assignment_type": "who-least-did-first",
                 "assignment_config": null,
                 "next_execution_assigned_to_user_id": null,
                 "consume_product_on_execution": "0",
@@ -73,9 +73,14 @@ class TestGrocy(TestCase):
             "next_execution_assigned_user": null
         }"""
         details_json = json.loads(details_json)
-        responses.add(responses.GET, f"{self.base_url}/chores/1", json=details_json, status=200)
+        responses.add(
+            responses.GET, f"{self.base_url}/chores/1", json=details_json, status=200
+        )
         chore_details = self.grocy.chore(1)
         self.assertIsInstance(chore_details, Chore)
+        self.assertEqual(
+            chore_details.assignment_type, AssignmentType.WHO_LEAST_DID_FIRST
+        )
 
     @unittest.skip("no stock_current table in current demo data")
     def test_product_get_details_valid(self):
@@ -83,7 +88,9 @@ class TestGrocy(TestCase):
 
         product = stock[0]
 
-        api_client = GrocyApiClient(CONST_BASE_URL, "demo_mode", port=CONST_PORT, verify_ssl=CONST_SSL)
+        api_client = GrocyApiClient(
+            CONST_BASE_URL, "demo_mode", port=CONST_PORT, verify_ssl=CONST_SSL
+        )
         product.get_details(api_client)
 
         self.assertIsInstance(product.name, str)
@@ -134,13 +141,20 @@ class TestGrocy(TestCase):
 
     @responses.activate
     def test_get_shopping_list_invalid_no_data(self):
-        responses.add(responses.GET, f"{self.base_url}/objects/shopping_list", status=400)
+        responses.add(
+            responses.GET, f"{self.base_url}/objects/shopping_list", status=400
+        )
         self.assertRaises(HTTPError, self.grocy.shopping_list)
 
     @responses.activate
     def test_get_shopping_list_invalid_missing_data(self):
         resp = []
-        responses.add(responses.GET, f"{self.base_url}/objects/shopping_list", json=resp, status=200)
+        responses.add(
+            responses.GET,
+            f"{self.base_url}/objects/shopping_list",
+            json=resp,
+            status=200,
+        )
         self.assertEqual(len(self.grocy.shopping_list()), 0)
 
     @unittest.skip("no shopping list existing in current demo data")
@@ -149,7 +163,11 @@ class TestGrocy(TestCase):
 
     @responses.activate
     def test_add_missing_product_to_shopping_list_error(self):
-        responses.add(responses.POST, f"{self.base_url}/stock/shoppinglist/add-missing-products", status=400)
+        responses.add(
+            responses.POST,
+            f"{self.base_url}/stock/shoppinglist/add-missing-products",
+            status=400,
+        )
         self.assertRaises(HTTPError, self.grocy.add_missing_product_to_shopping_list)
 
     @unittest.skip("no shopping list existing in current demo data")
@@ -162,22 +180,34 @@ class TestGrocy(TestCase):
 
     @responses.activate
     def test_clear_shopping_list_valid(self):
-        responses.add(responses.POST, f"{self.base_url}/stock/shoppinglist/clear", status=204)
+        responses.add(
+            responses.POST, f"{self.base_url}/stock/shoppinglist/clear", status=204
+        )
         self.grocy.clear_shopping_list()
 
     @responses.activate
     def test_clear_shopping_list_error(self):
-        responses.add(responses.POST, f"{self.base_url}/stock/shoppinglist/clear", status=400)
+        responses.add(
+            responses.POST, f"{self.base_url}/stock/shoppinglist/clear", status=400
+        )
         self.assertRaises(HTTPError, self.grocy.clear_shopping_list)
 
     @responses.activate
     def test_remove_product_in_shopping_list_valid(self):
-        responses.add(responses.POST, f"{self.base_url}/stock/shoppinglist/remove-product", status=204)
+        responses.add(
+            responses.POST,
+            f"{self.base_url}/stock/shoppinglist/remove-product",
+            status=204,
+        )
         self.grocy.remove_product_in_shopping_list(1)
 
     @responses.activate
     def test_remove_product_in_shopping_list_error(self):
-        responses.add(responses.POST, f"{self.base_url}/stock/shoppinglist/remove-product", status=400)
+        responses.add(
+            responses.POST,
+            f"{self.base_url}/stock/shoppinglist/remove-product",
+            status=400,
+        )
         self.assertRaises(HTTPError, self.grocy.remove_product_in_shopping_list, 1)
 
     @unittest.skip("no userentities table in current demo data")
@@ -195,22 +225,35 @@ class TestGrocy(TestCase):
 
     @responses.activate
     def test_get_product_groups_invalid_no_data(self):
-        responses.add(responses.GET, f"{self.base_url}/objects/product_groups", status=400)
+        responses.add(
+            responses.GET, f"{self.base_url}/objects/product_groups", status=400
+        )
         self.assertRaises(HTTPError, self.grocy.product_groups)
 
     @responses.activate
     def test_get_product_groups_invalid_missing_data(self):
         resp = []
-        responses.add(responses.GET, f"{self.base_url}/objects/product_groups", json=resp, status=200)
+        responses.add(
+            responses.GET,
+            f"{self.base_url}/objects/product_groups",
+            json=resp,
+            status=200,
+        )
         self.assertEqual(len(self.grocy.product_groups()), 0)
 
     @responses.activate
     def test_add_product_pic_valid(self):
         with patch("os.path.exists") as m_exist:
-            with patch("builtins.open", mock_open()) as m_open:
+            with patch("builtins.open", mock_open()):
                 m_exist.return_value = True
-                responses.add(responses.PUT, f"{self.base_url}/files/productpictures/MS5qcGc=", status=204)
-                responses.add(responses.PUT, f"{self.base_url}/objects/products/1", status=204)
+                responses.add(
+                    responses.PUT,
+                    f"{self.base_url}/files/productpictures/MS5qcGc=",
+                    status=204,
+                )
+                responses.add(
+                    responses.PUT, f"{self.base_url}/objects/products/1", status=204
+                )
                 resp = self.grocy.add_product_pic(1, "/somepath/pic.jpg")
                 self.assertIsNone(resp)
 
@@ -218,20 +261,32 @@ class TestGrocy(TestCase):
     def test_add_product_pic_invalid_missing_data(self):
         with patch("os.path.exists") as m_exist:
             m_exist.return_value = False
-            self.assertRaises(FileNotFoundError, self.grocy.add_product_pic, 1, "/somepath/pic.jpg")
+            self.assertRaises(
+                FileNotFoundError, self.grocy.add_product_pic, 1, "/somepath/pic.jpg"
+            )
 
     @responses.activate
     def test_upload_product_picture_error(self):
         with patch("os.path.exists") as m_exist:
-            with patch("builtins.open", mock_open()) as m_open:
+            with patch("builtins.open", mock_open()):
                 m_exist.return_value = True
-                api_client = GrocyApiClient(CONST_BASE_URL, "demo_mode", port=CONST_PORT, verify_ssl=CONST_SSL)
-                responses.add(responses.PUT, f"{self.base_url}/files/productpictures/MS5qcGc=", status=400)
-                self.assertRaises(HTTPError, api_client.upload_product_picture, 1, "/somepath/pic.jpg")
+                api_client = GrocyApiClient(
+                    CONST_BASE_URL, "demo_mode", port=CONST_PORT, verify_ssl=CONST_SSL
+                )
+                responses.add(
+                    responses.PUT,
+                    f"{self.base_url}/files/productpictures/MS5qcGc=",
+                    status=400,
+                )
+                self.assertRaises(
+                    HTTPError, api_client.upload_product_picture, 1, "/somepath/pic.jpg"
+                )
 
     @responses.activate
     def test_update_product_pic_error(self):
-        api_client = GrocyApiClient(CONST_BASE_URL, "demo_mode", port=CONST_PORT, verify_ssl=CONST_SSL)
+        api_client = GrocyApiClient(
+            CONST_BASE_URL, "demo_mode", port=CONST_PORT, verify_ssl=CONST_SSL
+        )
         responses.add(responses.PUT, f"{self.base_url}/objects/products/1", status=400)
         self.assertRaises(HTTPError, api_client.update_product_pic, 1)
 
@@ -247,19 +302,19 @@ class TestGrocy(TestCase):
 
     @responses.activate
     def test_get_expiring_invalid_no_data(self):
-        resp = {
-            "expiring_products": [],
-            "expired_products": [],
-            "missing_products": []
-        }
-        responses.add(responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200)
+        resp = {"expiring_products": [], "expired_products": [], "missing_products": []}
+        responses.add(
+            responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200
+        )
 
         self.grocy.expiring_products(True)
 
     @responses.activate
     def test_get_expiring_invalid_missing_data(self):
         resp = {}
-        responses.add(responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200)
+        responses.add(
+            responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200
+        )
 
     @unittest.skip("no stock_current table in current demo data")
     def test_get_expired_products_valid(self):
@@ -273,19 +328,19 @@ class TestGrocy(TestCase):
 
     @responses.activate
     def test_get_expired_invalid_no_data(self):
-        resp = {
-            "expiring_products": [],
-            "expired_products": [],
-            "missing_products": []
-        }
-        responses.add(responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200)
+        resp = {"expiring_products": [], "expired_products": [], "missing_products": []}
+        responses.add(
+            responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200
+        )
 
         self.grocy.expired_products(True)
 
     @responses.activate
     def test_get_expired_invalid_missing_data(self):
         resp = {}
-        responses.add(responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200)
+        responses.add(
+            responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200
+        )
 
     @unittest.skip("no stock_current table in current demo data")
     def test_get_missing_products_valid(self):
@@ -301,32 +356,31 @@ class TestGrocy(TestCase):
 
     @responses.activate
     def test_get_missing_invalid_no_data(self):
-        resp = {
-            "expiring_products": [],
-            "expired_products": [],
-            "missing_products": []
-        }
-        responses.add(responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200)
+        resp = {"expiring_products": [], "expired_products": [], "missing_products": []}
+        responses.add(
+            responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200
+        )
 
         self.grocy.missing_products(True)
 
     @responses.activate
     def test_get_missing_invalid_missing_data(self):
         resp = {}
-        responses.add(responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200)
+        responses.add(
+            responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200
+        )
 
     @responses.activate
     def test_get_userfields_valid(self):
-        resp = {
-            "uf1": 0,
-            "uf2": "string"
-        }
+        resp = {"uf1": 0, "uf2": "string"}
 
-        responses.add(responses.GET, f"{self.base_url}/userfields/chores/1", json=resp, status=200)
+        responses.add(
+            responses.GET, f"{self.base_url}/userfields/chores/1", json=resp, status=200
+        )
 
         a_chore_uf = self.grocy.get_userfields("chores", 1)
 
-        self.assertEqual(a_chore_uf['uf1'], 0)
+        self.assertEqual(a_chore_uf["uf1"], 0)
 
     @unittest.skip("no userentities table in current demo data")
     def test_get_userfields_invalid_no_data(self):
@@ -340,7 +394,9 @@ class TestGrocy(TestCase):
     @responses.activate
     def test_set_userfields_error(self):
         responses.add(responses.PUT, f"{self.base_url}/userfields/chores/1", status=400)
-        self.assertRaises(HTTPError, self.grocy.set_userfields, "chores", 1, "auserfield", "value")
+        self.assertRaises(
+            HTTPError, self.grocy.set_userfields, "chores", 1, "auserfield", "value"
+        )
 
     def test_get_last_db_changed_valid(self):
 
@@ -351,28 +407,43 @@ class TestGrocy(TestCase):
     @responses.activate
     def test_get_last_db_changed_invalid_no_data(self):
         resp = {}
-        responses.add(responses.GET, f"{self.base_url}/system/db-changed-time", json=resp, status=200)
+        responses.add(
+            responses.GET,
+            f"{self.base_url}/system/db-changed-time",
+            json=resp,
+            status=200,
+        )
 
         self.assertIsNone(self.grocy.get_last_db_changed())
 
     @responses.activate
     def test_add_product_valid(self):
-        responses.add(responses.POST, f"{self.base_url}/stock/products/1/add", status=200)
+        responses.add(
+            responses.POST, f"{self.base_url}/stock/products/1/add", status=200
+        )
         self.assertIsNone(self.grocy.add_product(1, 1.3, 2.44, self.date_test))
 
     @responses.activate
     def test_add_product_error(self):
-        responses.add(responses.POST, f"{self.base_url}/stock/products/1/add", status=400)
-        self.assertRaises(HTTPError, self.grocy.add_product, 1, 1.3, 2.44, self.date_test)
+        responses.add(
+            responses.POST, f"{self.base_url}/stock/products/1/add", status=400
+        )
+        self.assertRaises(
+            HTTPError, self.grocy.add_product, 1, 1.3, 2.44, self.date_test
+        )
 
     @responses.activate
     def test_consume_product_valid(self):
-        responses.add(responses.POST, f"{self.base_url}/stock/products/1/consume", status=200)
+        responses.add(
+            responses.POST, f"{self.base_url}/stock/products/1/consume", status=200
+        )
         self.assertIsNone(self.grocy.consume_product(1, 1.3, self.date_test))
 
     @responses.activate
     def test_consume_product_error(self):
-        responses.add(responses.POST, f"{self.base_url}/stock/products/1/consume", status=400)
+        responses.add(
+            responses.POST, f"{self.base_url}/stock/products/1/consume", status=400
+        )
         self.assertRaises(HTTPError, self.grocy.consume_product, 1, 1.3, self.date_test)
 
     @responses.activate
@@ -387,7 +458,8 @@ class TestGrocy(TestCase):
 
     @responses.activate
     def test_get_meal_plan(self):
-        resp_json = json.loads("""[
+        resp_json = json.loads(
+            """[
               {
                 "id": "1",
                 "day": "2020-08-10",
@@ -401,8 +473,14 @@ class TestGrocy(TestCase):
                 "row_created_timestamp": "2020-08-12 19:59:30",
                 "userfields": null
               }
-          ]""")
-        responses.add(responses.GET, f"{self.base_url}/objects/meal_plan", json=resp_json, status=200)
+          ]"""
+        )
+        responses.add(
+            responses.GET,
+            f"{self.base_url}/objects/meal_plan",
+            json=resp_json,
+            status=200,
+        )
         meal_plan = self.grocy.meal_plan()
         self.assertEqual(len(meal_plan), 1)
         self.assertEqual(meal_plan[0].id, 1)
@@ -410,7 +488,8 @@ class TestGrocy(TestCase):
 
     @responses.activate
     def test_get_recipe(self):
-        resp_json = json.loads("""{
+        resp_json = json.loads(
+            """{
           "id": "1",
           "name": "Pizza",
           "description": "<p>Mix everything</p>",
@@ -422,8 +501,14 @@ class TestGrocy(TestCase):
           "type": "normal",
           "product_id": "",
           "userfields": null
-        }""")
-        responses.add(responses.GET, f"{self.base_url}/objects/recipes/1", json=resp_json, status=200)
+        }"""
+        )
+        responses.add(
+            responses.GET,
+            f"{self.base_url}/objects/recipes/1",
+            json=resp_json,
+            status=200,
+        )
         recipe = self.grocy.recipe(1)
         self.assertEqual(recipe.id, 1)
         self.assertEqual(recipe.name, "Pizza")