class TestGrocy(TestCase): def setUp(self): self.grocy = Grocy(CONST_BASE_URL, "api_key") self.grocy = None self.grocy = Grocy(CONST_BASE_URL, "demo_mode", verify_ssl = CONST_SSL, port = CONST_PORT) def test_init(self): assert isinstance(self.grocy, Grocy) def test_get_chores_valid(self): chores = self.grocy.chores(get_details=True) assert isinstance(chores, list) assert len(chores) == 6 assert chores[0].id == 1 assert chores[1].id == 2 assert chores[2].id == 3 assert chores[3].id == 4 assert chores[4].id == 5 assert chores[5].id == 6 def test_product_get_details_valid(self): stock = self.grocy.stock() product = stock[0] api_client = GrocyApiClient(CONST_BASE_URL, "demo_mode", port = CONST_PORT, verify_ssl = CONST_SSL) product.get_details(api_client) assert isinstance(product.name, str) assert isinstance(product.product_group_id, int) @responses.activate def test_product_get_details_invalid_no_data(self): current_stock_response = CurrentStockResponse({ "product_id": 0, "amount": "0.33", "best_before_date": "2019-05-02" }) product = Product(current_stock_response) api_client = GrocyApiClient(CONST_BASE_URL, "demo_mode", port = CONST_PORT, verify_ssl = CONST_SSL) responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/products/0", status=200) product.get_details(api_client) assert product.name is None def test_get_stock_valid(self): stock = self.grocy.stock() assert isinstance(stock, list) assert len(stock) >= 10 for prod in stock: assert isinstance(prod, Product) @responses.activate def test_get_stock_invalid_no_data(self): responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock", status=200) assert self.grocy.stock() is None @responses.activate def test_get_stock_invalid_missing_data(self): resp = [ { } ] responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock", json=resp, status=200) def test_get_shopping_list_valid(self): shopping_list = self.grocy.shopping_list(True) assert isinstance(shopping_list, list) assert len(shopping_list) >= 1 for item in shopping_list: assert isinstance(item, ShoppingListProduct) @responses.activate def test_get_shopping_list_invalid_no_data(self): responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/objects/shopping_list", status=400) assert self.grocy.shopping_list() is None @responses.activate def test_get_shopping_list_invalid_missing_data(self): resp = [ { } ] responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/objects/shopping_list", json=resp, status=200) def test_add_missing_product_to_shopping_list_valid(self): assert self.grocy.add_missing_product_to_shopping_list().status_code == 204 @responses.activate def test_add_missing_product_to_shopping_list_error(self): responses.add(responses.POST, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/shoppinglist/add-missing-products", status=400) assert self.grocy.add_missing_product_to_shopping_list().status_code != 204 def test_add_product_to_shopping_list_valid(self): assert self.grocy.add_product_to_shopping_list(22).status_code == 204 def test_add_product_to_shopping_list_error(self): assert self.grocy.add_product_to_shopping_list(3000).status_code != 204 @responses.activate def test_clear_shopping_list_valid(self): responses.add(responses.POST, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/shoppinglist/clear", status=204) assert self.grocy.clear_shopping_list().status_code == 204 @responses.activate def test_clear_shopping_list_error(self): responses.add(responses.POST, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/shoppinglist/clear", status=400) assert self.grocy.clear_shopping_list().status_code != 204 @responses.activate def test_remove_product_in_shopping_list_valid(self): responses.add(responses.POST, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/shoppinglist/remove-product", status=204) assert self.grocy.remove_product_in_shopping_list(1).status_code == 204 @responses.activate def test_remove_product_in_shopping_list_error(self): responses.add(responses.POST, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/shoppinglist/remove-product", status=400) assert self.grocy.remove_product_in_shopping_list(1).status_code != 204 def test_get_product_groups_valid(self): product_groups_list = self.grocy.product_groups() assert isinstance(product_groups_list, list) assert len(product_groups_list) >= 1 for item in product_groups_list: assert isinstance(item, Group) @responses.activate def test_get_product_groups_invalid_no_data(self): responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/objects/product_groups", status=400) assert self.grocy.product_groups() is None @responses.activate def test_get_product_groups_invalid_missing_data(self): resp = [ { } ] responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/objects/product_groups", json=resp, status=200) @responses.activate def test_upload_product_picture_valid(self): with patch("os.path.exists" ) as m_exist: with patch("builtins.open", mock_open()) as m_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, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/files/productpictures/MS5qcGc=", status=204) assert api_client.upload_product_picture(1,"/somepath/pic.jpg").status_code == 204 @responses.activate def test_upload_product_picture_invalid_missing_data(self): with patch("os.path.exists" ) as m_exist: m_exist.return_value = False api_client = GrocyApiClient(CONST_BASE_URL, "demo_mode", port = CONST_PORT, verify_ssl = CONST_SSL) responses.add(responses.PUT, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/files/productpictures/MS5qcGc=", status=204) assert api_client.upload_product_picture(1,"/somepath/pic.jpg") is None @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: m_exist.return_value = True api_client = GrocyApiClient(CONST_BASE_URL, "demo_mode", port = CONST_PORT, verify_ssl = CONST_SSL) responses.add(responses.PUT, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/files/productpictures/MS5qcGc=", status=400) assert api_client.upload_product_picture(1,"/somepath/pic.jpg").status_code != 204 @responses.activate def test_update_product_pic_valid(self): api_client = GrocyApiClient(CONST_BASE_URL, "demo_mode", port = CONST_PORT, verify_ssl = CONST_SSL) responses.add(responses.PUT, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/objects/products/1", status=204) assert api_client.update_product_pic(1).status_code == 204 @responses.activate def test_update_product_pic_error(self): api_client = GrocyApiClient(CONST_BASE_URL, "demo_mode", port = CONST_PORT, verify_ssl = CONST_SSL) responses.add(responses.PUT, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/objects/products/1", status=400) assert api_client.update_product_pic(1).status_code != 204 def test_get_expiring_products_valid(self): expiring_product = self.grocy.expiring_products(True) assert isinstance(expiring_product, list) assert len(expiring_product) >= 1 for prod in expiring_product: assert isinstance(prod, Product) @responses.activate def test_get_expiring_invalid_no_data(self): resp = { "expiring_products": [], "expired_products": [], "missing_products": [] } responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/volatile", json=resp, status=200) assert not self.grocy.expiring_products(True) @responses.activate def test_get_expiring_invalid_missing_data(self): resp = {} responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/volatile", json=resp, status=200) def test_get_expired_products_valid(self): expired_product = self.grocy.expired_products(True) assert isinstance(expired_product, list) assert len(expired_product) >= 1 for prod in expired_product: assert isinstance(prod, Product) @responses.activate def test_get_expired_invalid_no_data(self): resp = { "expiring_products": [], "expired_products": [], "missing_products": [] } responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/volatile", json=resp, status=200) assert not self.grocy.expired_products(True) @responses.activate def test_get_expired_invalid_missing_data(self): resp = {} responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/volatile", json=resp, status=200) def test_get_missing_products_valid(self): missing_product = self.grocy.missing_products(True) assert isinstance(missing_product, list) assert len(missing_product) >= 1 for prod in missing_product: assert isinstance(prod, Product) @responses.activate def test_get_missing_invalid_no_data(self): resp = { "expiring_products": [], "expired_products": [], "missing_products": [] } responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/volatile", json=resp, status=200) assert not self.grocy.missing_products(True) @responses.activate def test_get_missing_invalid_missing_data(self): resp = {} responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/stock/volatile", json=resp, status=200) @responses.activate def test_get_userfields_valid(self): resp = { "uf1": 0, "uf2": "string" } responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/userfields/chores/1", json=resp, status=200) a_chore_uf = self.grocy.get_userfields("chores",1) assert a_chore_uf['uf1'] == 0 def test_get_userfields_invalid_no_data(self): assert not self.grocy.get_userfields("chores",1) @responses.activate def test_set_userfields_valid(self): responses.add(responses.PUT, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/userfields/chores/1", status=204) assert self.grocy.set_userfields("chores",1,"auserfield","value").status_code == 204 @responses.activate def test_set_userfields_error(self): responses.add(responses.PUT, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/userfields/chores/1", status=400) assert self.grocy.set_userfields("chores",1,"auserfield","value").status_code != 204 def test_get_last_db_changed_valid(self): timestamp = self.grocy.get_last_db_changed() assert isinstance(timestamp, datetime) @responses.activate def test_get_last_db_changed_invalid_no_data(self): resp = {} responses.add(responses.GET, '{}:{}'.format(CONST_BASE_URL,CONST_PORT) + "/api/system/db-changed-time", json=resp ,status=200) assert self.grocy.get_last_db_changed() is None
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.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") def test_init(self): self.assertIsInstance(self.grocy, Grocy) @unittest.skip("no tasks_current table in current demo data") def test_get_tasks_valid(self): tasks = self.grocy.tasks() assert len(tasks) == 6 assert tasks[0].id == 1 assert tasks[0].name == "Repair the garage door" @unittest.skip("no chores_current table in current demo data") def test_get_chores_valid(self): chores = self.grocy.chores(get_details=True) self.assertIsInstance(chores, list) self.assertGreaterEqual(len(chores), 1) for chore in chores: self.assertIsInstance(chore, Chore) self.assertIsInstance(chore.id, int) self.assertIsInstance(chore.last_tracked_time, datetime) self.assertIsInstance(chore.next_estimated_execution_time, datetime) self.assertIsInstance(chore.name, str) self.assertIsInstance(chore.last_done_by, UserDto) @responses.activate def test_get_chore_details_valid(self): details_json = """{ "chore": { "id": "1", "name": "Changed towels in the bathroom", "description": null, "period_type": "manually", "period_days": "5", "row_created_timestamp": "2020-03-16 00:50:14", "period_config": null, "track_date_only": "0", "rollover": "0", "assignment_type": "who-least-did-first", "assignment_config": null, "next_execution_assigned_to_user_id": null, "consume_product_on_execution": "0", "product_id": null, "product_amount": null, "period_interval": "1" }, "tracked_count": 3, "next_estimated_execution_time": "2999-12-31 23:59:59", "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) 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): stock = self.grocy.stock() product = stock[0] 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) self.assertIsInstance(product.id, int) self.assertIsInstance(product.available_amount, float) self.assertIsInstance(product.best_before_date, datetime) if product.barcodes: self.assertIsInstance(product.barcodes, (list, str)) self.assertIsInstance(product.product_group_id, int) @responses.activate def test_product_get_details_invalid_no_data(self): responses.add(responses.GET, f"{self.base_url}/stock/products/0", status=200) product = self.grocy.product(0) self.assertIsNone(product) @unittest.skip("no stock_current table in current demo data") def test_get_stock_valid(self): stock = self.grocy.stock() self.assertIsInstance(stock, list) self.assertGreaterEqual(len(stock), 10) for prod in stock: self.assertIsInstance(prod, Product) @responses.activate def test_get_stock_invalid_missing_data(self): resp = [] responses.add(responses.GET, f"{self.base_url}/stock", json=resp, status=200) self.assertEqual(len(self.grocy.stock()), 0) @unittest.skip("no userentities table in current demo data") def test_get_shopping_list_valid(self): shopping_list = self.grocy.shopping_list(True) self.assertIsInstance(shopping_list, list) self.assertGreaterEqual(len(shopping_list), 1) for item in shopping_list: self.assertIsInstance(item, ShoppingListProduct) self.assertIsInstance(item.id, int) if item.product_id: self.assertIsInstance(item.product_id, int) self.assertIsInstance(item.product, ProductData) self.assertIsInstance(item.product.id, int) self.assertIsInstance(item.amount, float) if item.note: self.assertIsInstance(item.note, str) @responses.activate def test_get_shopping_list_invalid_no_data(self): 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, ) self.assertEqual(len(self.grocy.shopping_list()), 0) @unittest.skip("no shopping list existing in current demo data") def test_add_missing_product_to_shopping_list_valid(self): self.assertIsNone(self.grocy.add_missing_product_to_shopping_list()) @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, ) self.assertRaises(HTTPError, self.grocy.add_missing_product_to_shopping_list) @unittest.skip("no shopping list existing in current demo data") def test_add_product_to_shopping_list_valid(self): self.grocy.add_product_to_shopping_list(3) @unittest.skip("no shopping list existing in current demo data") def test_add_product_to_shopping_list_error(self): self.assertRaises(HTTPError, self.grocy.add_product_to_shopping_list, 3000) @responses.activate def test_clear_shopping_list_valid(self): 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) 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, ) 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, ) self.assertRaises(HTTPError, self.grocy.remove_product_in_shopping_list, 1) @unittest.skip("no userentities table in current demo data") def test_get_product_groups_valid(self): product_groups_list = self.grocy.product_groups() self.assertIsInstance(product_groups_list, list) self.assertGreaterEqual(len(product_groups_list), 1) for group in product_groups_list: self.assertIsInstance(group, Group) self.assertIsInstance(group.id, int) self.assertIsInstance(group.name, str) if group.description: self.assertIsInstance(group.description, str) @responses.activate def test_get_product_groups_invalid_no_data(self): 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, ) 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()): 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) resp = self.grocy.add_product_pic(1, "/somepath/pic.jpg") self.assertIsNone(resp) @responses.activate 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") @responses.activate def test_upload_product_picture_error(self): with patch("os.path.exists") as m_exist: 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") @responses.activate def test_update_product_pic_error(self): 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) @unittest.skip("no stock_current table in current demo data") def test_get_expiring_products_valid(self): expiring_product = self.grocy.expiring_products(True) self.assertIsInstance(expiring_product, list) self.assertGreaterEqual(len(expiring_product), 1) for prod in expiring_product: self.assertIsInstance(prod, Product) @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) 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) @unittest.skip("no stock_current table in current demo data") def test_get_expired_products_valid(self): expired_product = self.grocy.expired_products(True) self.assertIsInstance(expired_product, list) self.assertGreaterEqual(len(expired_product), 1) for prod in expired_product: self.assertIsInstance(prod, Product) @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) 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) @unittest.skip("no stock_current table in current demo data") def test_get_missing_products_valid(self): missing_product = self.grocy.missing_products(True) self.assertIsInstance(missing_product, list) self.assertGreaterEqual(len(missing_product), 1) for prod in missing_product: self.assertIsInstance(prod, Product) self.assertIsInstance(prod.amount_missing, float) self.assertIsInstance(prod.is_partly_in_stock, bool) @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) 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.activate def test_get_userfields_valid(self): resp = {"uf1": 0, "uf2": "string"} 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) @unittest.skip("no userentities table in current demo data") def test_get_userfields_invalid_no_data(self): self.assertRaises(HTTPError, self.grocy.get_userfields("chores", 1)) @responses.activate def test_set_userfields_valid(self): responses.add(responses.PUT, f"{self.base_url}/userfields/chores/1", status=204) self.grocy.set_userfields("chores", 1, "auserfield", "value") @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") def test_get_last_db_changed_valid(self): timestamp = self.grocy.get_last_db_changed() self.assertIsInstance(timestamp, datetime) @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, ) 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) 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.activate def test_consume_product_valid(self): 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) self.assertRaises(HTTPError, self.grocy.consume_product, 1, 1.3, self.date_test) @responses.activate def test_execute_chore_valid(self): responses.add(responses.POST, f"{self.base_url}/chores/1/execute", status=200) self.assertIsNone(self.grocy.execute_chore(1, 1, self.date_test)) @responses.activate def test_execute_chore_error(self): responses.add(responses.POST, f"{self.base_url}/chores/1/execute", status=400) self.assertRaises(HTTPError, self.grocy.execute_chore, 1, 1, self.date_test) @responses.activate def test_get_meal_plan(self): resp_json = json.loads("""[ { "id": "1", "day": "2020-08-10", "type": "recipe", "recipe_id": "1", "recipe_servings": "1", "note": null, "product_id": null, "product_amount": "0.0", "product_qu_id": null, "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, ) meal_plan = self.grocy.meal_plan() self.assertEqual(len(meal_plan), 1) self.assertEqual(meal_plan[0].id, 1) self.assertEqual(meal_plan[0].recipe_id, 1) @responses.activate def test_get_recipe(self): resp_json = json.loads("""{ "id": "1", "name": "Pizza", "description": "<p>Mix everything</p>", "row_created_timestamp": "2020-08-12 11:37:34", "picture_file_name": "51si0q0wsiq5imo4f8wbIMG_5709.jpeg", "base_servings": "4", "desired_servings": "4", "not_check_shoppinglist": "0", "type": "normal", "product_id": "", "userfields": null }""") 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") self.assertEqual(recipe.base_servings, 4) self.assertIsInstance(recipe.get_picture_url_path(400), str)
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.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.add_generic_data = {"name": "This is a task"} @responses.activate def test_get_shopping_list_invalid_no_data(self): responses.add(responses.GET, f"{self.base_url}/objects/shopping_list", status=400) self.assertRaises(GrocyError, 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, ) self.assertEqual(len(self.grocy.shopping_list()), 0) @responses.activate def test_clear_shopping_list_error(self): responses.add(responses.POST, f"{self.base_url}/stock/shoppinglist/clear", status=400) self.assertRaises(GrocyError, self.grocy.clear_shopping_list) @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, ) self.assertRaises(GrocyError, self.grocy.remove_product_in_shopping_list, 1) @responses.activate def test_get_product_groups_invalid_no_data(self): responses.add(responses.GET, f"{self.base_url}/objects/product_groups", status=400) self.assertRaises(GrocyError, 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, ) self.assertEqual(len(self.grocy.product_groups()), 0) @responses.activate 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") @responses.activate def test_upload_product_picture_error(self): with patch("os.path.exists") as m_exist: 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( GrocyError, 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) responses.add(responses.PUT, f"{self.base_url}/objects/products/1", status=400) self.assertRaises(GrocyError, api_client.update_product_pic, 1) @responses.activate def test_get_due_invalid_no_data(self): resp = { "due_products": [], "expired_products": [], "missing_products": [] } responses.add(responses.GET, f"{self.base_url}/stock/volatile", json=resp, status=200) self.grocy.due_products(True) @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) 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.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) 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.activate def test_get_userfields_valid(self): resp = {"uf1": 0, "uf2": "string"} 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) @responses.activate def test_set_userfields_valid(self): responses.add(responses.PUT, f"{self.base_url}/userfields/chores/1", status=204) self.grocy.set_userfields("chores", 1, "auserfield", "value") @responses.activate def test_set_userfields_error(self): responses.add(responses.PUT, f"{self.base_url}/userfields/chores/1", status=400) self.assertRaises(GrocyError, self.grocy.set_userfields, "chores", 1, "auserfield", "value") @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, ) 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) 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(GrocyError, 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) self.assertIsNone(self.grocy.consume_product(1, 1.3)) @responses.activate def test_consume_product_error(self): responses.add(responses.POST, f"{self.base_url}/stock/products/1/consume", status=400) self.assertRaises(GrocyError, self.grocy.consume_product, 1, 1.3) @pytest.mark.vcr def test_inventory_product_valid(self): current_inventory = int(self.grocy.product(4).available_amount) new_amount = current_inventory + 10 product = self.grocy.inventory_product(4, new_amount, self.date_test, 1, 1, 150, True) assert product.id == 4 assert product.name == "Crisps" assert product.available_amount == new_amount @pytest.mark.vcr def test_inventory_product_error(self): with pytest.raises(GrocyError) as exc_info: current_inventory = int(self.grocy.product(4).available_amount) self.grocy.inventory_product(4, current_inventory, self.date_test, 1, 1, 150, True) error = exc_info.value assert error.status_code == 400 @pytest.mark.vcr def test_add_product_by_barcode_valid(self): product = self.grocy.add_product_by_barcode("42141099", 1, 5, self.date_test, True) assert isinstance(product, Product) assert product.id == 4 assert product.name == "Crisps" @pytest.mark.vcr def test_add_product_by_barcode_error(self): with pytest.raises(GrocyError) as exc_info: self.grocy.add_product_by_barcode("555", 1, 5, self.date_test, True) error = exc_info.value assert error.status_code == 400 @pytest.mark.vcr def test_consume_product_by_barcode_valid(self): product = self.grocy.consume_product_by_barcode( "42141099", 1, False, True) assert isinstance(product, Product) assert product.id == 4 assert product.name == "Crisps" @pytest.mark.vcr def test_consume_product_by_barcode_error(self): with pytest.raises(GrocyError) as exc_info: self.grocy.consume_product_by_barcode("555", 1, False, True) error = exc_info.value assert error.status_code == 400 @pytest.mark.vcr def test_inventory_product_by_barcode_valid(self): currentInv = int( self.grocy.product_by_barcode("42141099").available_amount) newAmount = currentInv + 10 product = self.grocy.inventory_product_by_barcode( "42141099", newAmount, self.date_test, 1, 150, True) assert product.id == 4 assert product.name == "Crisps" assert product.available_amount == newAmount @pytest.mark.vcr def test_inventory_product_by_barcode_error(self): with pytest.raises(GrocyError) as exc_info: currentInv = int( self.grocy.product_by_barcode("42141099").available_amount) self.grocy.inventory_product(4, currentInv, self.date_test, 1, 150, True) error = exc_info.value assert error.status_code == 400 @responses.activate def test_execute_chore_valid(self): responses.add(responses.POST, f"{self.base_url}/chores/1/execute", status=200) self.assertIsNone(self.grocy.execute_chore(1, 1, self.date_test)) @responses.activate def test_execute_chore_error(self): responses.add(responses.POST, f"{self.base_url}/chores/1/execute", status=400) self.assertRaises(GrocyError, self.grocy.execute_chore, 1, 1, self.date_test) @responses.activate def test_get_meal_plan(self): resp_json = json.loads("""[ { "id": "1", "day": "2020-08-10", "type": "recipe", "recipe_id": "1", "recipe_servings": "1", "note": null, "product_id": null, "product_amount": "0.0", "product_qu_id": null, "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, ) meal_plan = self.grocy.meal_plan() self.assertEqual(len(meal_plan), 1) self.assertEqual(meal_plan[0].id, 1) self.assertEqual(meal_plan[0].recipe_id, 1) @responses.activate def test_get_recipe(self): resp_json = json.loads("""{ "id": "1", "name": "Pizza", "description": "<p>Mix everything</p>", "row_created_timestamp": "2020-08-12 11:37:34", "picture_file_name": "51si0q0wsiq5imo4f8wbIMG_5709.jpeg", "base_servings": "4", "desired_servings": "4", "not_check_shoppinglist": "0", "type": "normal", "product_id": "", "userfields": null }""") 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") self.assertEqual(recipe.base_servings, 4) self.assertIsInstance(recipe.get_picture_url_path(400), str)
class TestGrocy(TestCase): def setUp(self): self.grocy = Grocy("https://example.com", "api_key") def test_init(self): assert isinstance(self.grocy, Grocy) @responses.activate def test_get_chores_valid_no_details(self): resp = [{ "chore_id": "1", "last_tracked_time": "2019-11-18 00:00:00", "next_estimated_execution_time": "2019-11-25 00:00:00", "track_date_only": "1" }, { "chore_id": "2", "last_tracked_time": "2019-11-16 00:00:00", "next_estimated_execution_time": "2019-11-23 00:00:00", "track_date_only": "1" }, { "chore_id": "3", "last_tracked_time": "2019-11-10 00:00:00", "next_estimated_execution_time": "2019-12-10 00:00:00", "track_date_only": "1" }, { "chore_id": "4", "last_tracked_time": "2019-11-18 00:00:00", "next_estimated_execution_time": "2019-11-25 00:00:00", "track_date_only": "1", }] responses.add(responses.GET, "https://example.com:9192/api/chores", json=resp, status=200) chores = self.grocy.chores(get_details=False) assert isinstance(chores, list) assert len(chores) == 4 assert chores[0].chore_id == 1 assert chores[1].chore_id == 2 assert chores[2].chore_id == 3 assert chores[3].chore_id == 4 @responses.activate def test_product_get_details_valid(self): current_stock_response = CurrentStockResponse({ "product_id": 0, "amount": "0.33", "best_before_date": "2019-05-02" }) product = Product(current_stock_response) api_client = GrocyApiClient("https://example.com", "api_key") resp = { "product": { "id": 0, "name": "string", "description": "string", "location_id": 0, "qu_id_purchase": 0, "qu_id_stock": 0, "qu_factor_purchase_to_stock": 0, "barcode": "string", "product_group_id": 0, "min_stock_amount": 0, "default_best_before_days": 0, "picture_file_name": "string", "allow_partial_units_in_stock": True, "row_created_timestamp": "2019-05-02T18:30:48.041Z" }, "quantity_unit_purchase": { "id": 0, "name": "string", "name_plural": "string", "description": "string", "row_created_timestamp": "2019-05-02T18:30:48.041Z" }, "quantity_unit_stock": { "id": 0, "name": "string", "name_plural": "string", "description": "string", "row_created_timestamp": "2019-05-02T18:30:48.041Z" }, "last_purchased": "2019-05-02", "last_used": "2019-05-02T18:30:48.041Z", "stock_amount": 0, "stock_amount_opened": 0, "next_best_before_date": "2019-05-02T18:30:48.041Z", "last_price": 0, "location": { "id": 0, "name": "string", "description": "string", "row_created_timestamp": "2019-05-02T18:30:48.041Z" } } responses.add(responses.GET, "https://example.com:9192/api/stock/products/0", json=resp, status=200) product.get_details(api_client) assert product.name == "string" assert product.product_group_id == 0 @responses.activate def test_product_get_details_invalid_no_data(self): current_stock_response = CurrentStockResponse({ "product_id": 0, "amount": "0.33", "best_before_date": "2019-05-02" }) product = Product(current_stock_response) api_client = GrocyApiClient("https://example.com", "api_key") responses.add(responses.GET, "https://example.com:9192/api/stock/products/0", status=200) product.get_details(api_client) assert product.name is None @responses.activate def test_get_stock_valid(self): resp = [{ "product_id": 0, "amount": "0.33", "best_before_date": "2019-05-02" }] responses.add(responses.GET, "https://example.com:9192/api/stock", json=resp, status=200) stock = self.grocy.stock() assert isinstance(stock, list) assert len(stock) == 1 for prod in stock: assert isinstance(prod, Product) @responses.activate def test_get_stock_invalid_no_data(self): responses.add(responses.GET, "https://example.com:9192/api/stock", status=200) assert self.grocy.stock() is None @responses.activate def test_get_stock_invalid_missing_data(self): resp = [{}] responses.add(responses.GET, "https://example.com:9192/api/stock", json=resp, status=200) @responses.activate def test_get_shopping_list_valid(self): resp = [{ "id": 1, "product_id": 6, "note": "string", "amount": 2, "row_created_timestamp": "2019-04-17 10:30:00", "shopping_list_id": 1, "done": 0 }] responses.add(responses.GET, "https://example.com:9192/api/objects/shopping_list", json=resp, status=200) shopping_list = self.grocy.shopping_list() assert isinstance(shopping_list, list) assert len(shopping_list) == 1 for item in shopping_list: assert isinstance(item, ShoppingListProduct) @responses.activate def test_get_shopping_list_invalid_no_data(self): responses.add(responses.GET, "https://example.com:9192/api/objects/shopping_list", status=400) assert self.grocy.shopping_list() is None @responses.activate def test_get_shopping_list_invalid_missing_data(self): resp = [{}] responses.add(responses.GET, "https://example.com:9192/api/objects/shopping_list", json=resp, status=200) @responses.activate def test_add_missing_product_to_shopping_list_valid(self): responses.add( responses.POST, "https://example.com:9192/api/stock/shoppinglist/add-missing-products", status=204) assert self.grocy.add_missing_product_to_shopping_list( ).status_code == 204 @responses.activate def test_add_missing_product_to_shopping_list_error(self): responses.add( responses.POST, "https://example.com:9192/api/stock/shoppinglist/add-missing-products", status=400) assert self.grocy.add_missing_product_to_shopping_list( ).status_code != 204 @responses.activate def test_add_product_to_shopping_list_valid(self): responses.add( responses.POST, "https://example.com:9192/api/stock/shoppinglist/add-product", status=204) assert self.grocy.add_product_to_shopping_list(1).status_code == 204 @responses.activate def test_add_product_to_shopping_list_error(self): responses.add( responses.POST, "https://example.com:9192/api/stock/shoppinglist/add-product", status=400) assert self.grocy.add_product_to_shopping_list(1).status_code != 204 @responses.activate def test_clear_shopping_list_valid(self): responses.add(responses.POST, "https://example.com:9192/api/stock/shoppinglist/clear", status=204) assert self.grocy.clear_shopping_list().status_code == 204 @responses.activate def test_clear_shopping_list_error(self): responses.add(responses.POST, "https://example.com:9192/api/stock/shoppinglist/clear", status=400) assert self.grocy.clear_shopping_list().status_code != 204 @responses.activate def test_remove_product_in_shopping_list_valid(self): responses.add( responses.POST, "https://example.com:9192/api/stock/shoppinglist/remove-product", status=204) assert self.grocy.remove_product_in_shopping_list(1).status_code == 204 @responses.activate def test_remove_product_in_shopping_list_error(self): responses.add( responses.POST, "https://example.com:9192/api/stock/shoppinglist/remove-product", status=400) assert self.grocy.remove_product_in_shopping_list(1).status_code != 204 @responses.activate def test_get_product_groups_valid(self): resp = [{ "id": 1, "name": "string", "description": "string", "row_created_timestamp": "2019-04-17 10:30:00", }] responses.add(responses.GET, "https://example.com:9192/api/objects/product_groups", json=resp, status=200) product_groups_list = self.grocy.product_groups() assert isinstance(product_groups_list, list) assert len(product_groups_list) == 1 for item in product_groups_list: assert isinstance(item, Group) @responses.activate def test_get_product_groups_invalid_no_data(self): responses.add(responses.GET, "https://example.com:9192/api/objects/product_groups", status=400) assert self.grocy.product_groups() is None @responses.activate def test_get_product_groups_invalid_missing_data(self): resp = [{}] responses.add(responses.GET, "https://example.com:9192/api/objects/product_groups", json=resp, status=200) @responses.activate def test_upload_product_picture_valid(self): with patch("os.path.exists") as m_exist: with patch("builtins.open", mock_open()) as m_open: m_exist.return_value = True api_client = GrocyApiClient("https://example.com", "api_key") responses.add( responses.PUT, "https://example.com:9192/api/files/productpictures/MS5qcGc=", status=204) assert api_client.upload_product_picture( 1, "/somepath/pic.jpg").status_code == 204 @responses.activate def test_upload_product_picture_invalid_missing_data(self): with patch("os.path.exists") as m_exist: m_exist.return_value = False api_client = GrocyApiClient("https://example.com", "api_key") responses.add( responses.PUT, "https://example.com:9192/api/files/productpictures/MS5qcGc=", status=204) assert api_client.upload_product_picture( 1, "/somepath/pic.jpg") is None @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: m_exist.return_value = True api_client = GrocyApiClient("https://example.com", "api_key") responses.add( responses.PUT, "https://example.com:9192/api/files/productpictures/MS5qcGc=", status=400) assert api_client.upload_product_picture( 1, "/somepath/pic.jpg").status_code != 204 @responses.activate def test_update_product_pic_valid(self): api_client = GrocyApiClient("https://example.com", "api_key") responses.add(responses.PUT, "https://example.com:9192/api/objects/products/1", status=204) assert api_client.update_product_pic(1).status_code == 204 @responses.activate def test_update_product_pic_error(self): api_client = GrocyApiClient("https://example.com", "api_key") responses.add(responses.PUT, "https://example.com:9192/api/objects/products/1", status=400) assert api_client.update_product_pic(1).status_code != 204 @responses.activate def test_get_expiring_products_valid(self): resp = { "expiring_products": [{ "product_id": 0, "amount": "0.33", "best_before_date": "2019-05-02", "amount_opened": "0" }], "expired_products": [], "missing_products": [] } responses.add(responses.GET, "https://example.com:9192/api/stock/volatile", json=resp, status=200) expiring_product = self.grocy.expiring_products() assert isinstance(expiring_product, list) assert len(expiring_product) == 1 for prod in expiring_product: assert isinstance(prod, Product) @responses.activate def test_get_expiring_invalid_no_data(self): resp = { "expiring_products": [], "expired_products": [], "missing_products": [] } responses.add(responses.GET, "https://example.com:9192/api/stock/volatile", json=resp, status=200) assert not self.grocy.expiring_products() @responses.activate def test_get_expiring_invalid_missing_data(self): resp = {} responses.add(responses.GET, "https://example.com:9192/api/stock/volatile", json=resp, status=200) @responses.activate def test_get_expired_products_valid(self): resp = { "expired_products": [{ "product_id": 0, "amount": "0.33", "best_before_date": "2019-05-02", "amount_opened": "0" }], "expiring_products": [], "missing_products": [] } responses.add(responses.GET, "https://example.com:9192/api/stock/volatile", json=resp, status=200) expired_product = self.grocy.expired_products() assert isinstance(expired_product, list) assert len(expired_product) == 1 for prod in expired_product: assert isinstance(prod, Product) @responses.activate def test_get_expired_invalid_no_data(self): resp = { "expiring_products": [], "expired_products": [], "missing_products": [] } responses.add(responses.GET, "https://example.com:9192/api/stock/volatile", json=resp, status=200) assert not self.grocy.expired_products() @responses.activate def test_get_expired_invalid_missing_data(self): resp = {} responses.add(responses.GET, "https://example.com:9192/api/stock/volatile", json=resp, status=200) @responses.activate def test_get_missing_products_valid(self): resp = { "missing_products": [{ "product_id": 0, "amount": "0.33", "best_before_date": "2019-05-02", "amount_opened": "0" }], "expired_products": [], "expiring_products": [] } responses.add(responses.GET, "https://example.com:9192/api/stock/volatile", json=resp, status=200) missing_product = self.grocy.missing_products() assert isinstance(missing_product, list) assert len(missing_product) == 1 for prod in missing_product: assert isinstance(prod, Product) @responses.activate def test_get_missing_invalid_no_data(self): resp = { "expiring_products": [], "expired_products": [], "missing_products": [] } responses.add(responses.GET, "https://example.com:9192/api/stock/volatile", json=resp, status=200) assert not self.grocy.missing_products() @responses.activate def test_get_stock_invalid_missing_data(self): resp = {} responses.add(responses.GET, "https://example.com:9192/api/stock/volatile", json=resp, status=200) @responses.activate def test_get_userfields_valid(self): resp = {"uf1": 0, "uf2": "string"} responses.add(responses.GET, "https://example.com:9192/api/userfields/chores/1", json=resp, status=200) a_chore_uf = self.grocy.get_userfields("chores", 1) assert a_chore_uf['uf1'] == 0 @responses.activate def test_get_userfields_invalid_no_data(self): resp = [] responses.add(responses.GET, "https://example.com:9192/api/userfields/chores/1", json=resp, status=200) assert not self.grocy.get_userfields("chores", 1) @responses.activate def test_set_userfields_valid(self): responses.add(responses.PUT, "https://example.com:9192/api/userfields/chores/1", status=204) assert self.grocy.set_userfields("chores", 1, "auserfield", "value").status_code == 204 @responses.activate def test_set_userfields_error(self): responses.add(responses.PUT, "https://example.com:9192/api/userfields/chores/1", status=400) assert self.grocy.set_userfields("chores", 1, "auserfield", "value").status_code != 204 @responses.activate def test_get_last_db_changed_valid(self): resp = {"changed_time": "2019-09-18T05:30:58.598Z"} responses.add(responses.GET, "https://example.com:9192/api/system/db-changed-time", json=resp, status=200) timestamp = self.grocy.get_last_db_changed() assert isinstance(timestamp, datetime) @responses.activate def test_get_last_db_changed_invalid_no_data(self): resp = {} responses.add(responses.GET, "https://example.com:9192/api/system/db-changed-time", json=resp, status=200) assert self.grocy.get_last_db_changed() is None