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"}
async def _test_credentials(self, url, api_key, port, verify_ssl): """Return true if credentials is valid.""" try: client = Grocy(url, api_key, port, verify_ssl) client.stock() return True except Exception as e: # pylint: disable=broad-except _LOGGER.exception(e) pass return False
async def async_setup(hass, config): """Set up this component.""" # Import client from a external python package hosted on PyPi from pygrocy import Grocy,TransactionType # Print startup message startup = STARTUP.format(name=DOMAIN, version=VERSION, issueurl=ISSUE_URL) _LOGGER.info(startup) # Check that all required files are present file_check = await check_files(hass) if not file_check: return False # Create DATA dict hass.data[DOMAIN_DATA] = {} # Get "global" configuration. url = config[DOMAIN].get(CONF_URL) api_key = config[DOMAIN].get(CONF_API_KEY) # Configure the client. grocy = Grocy(url, api_key) hass.data[DOMAIN_DATA]["client"] = GrocyData(hass, grocy) # Load platforms for platform in PLATFORMS: # Get platform specific configuration platform_config = config[DOMAIN].get(platform, {}) # If platform is not enabled, skip. if not platform_config: continue for entry in platform_config: entry_config = entry _LOGGER.critical(entry_config) # If entry is not enabled, skip. if not entry_config[CONF_ENABLED]: continue hass.async_create_task( discovery.async_load_platform( hass, platform, DOMAIN, entry_config, config ) ) def handle_add_product(call): product_id = call.data['product_id'] amount = call.data.get('amount', 0) price = call.data.get('price', None) grocy.add_product(product_id, amount, price) hass.services.async_register(DOMAIN, "add_product", handle_add_product) def handle_consume_product(call): product_id = call.data['product_id'] amount = call.data.get('amount', 0) spoiled = call.data.get('spoiled', False) transaction_type_raw = call.data.get('transaction_type', None) transaction_type = TransactionType.CONSUME if transaction_type_raw is not None: transaction_type = TransactionType[transaction_type_raw] grocy.consume_product(product_id, amount, spoiled=spoiled, transaction_type=transaction_type) hass.services.async_register(DOMAIN, "consume_product", handle_consume_product) return True
def setUp(self): self.grocy = Grocy("https://example.com/api/", "api_key")
class TestGrocy(TestCase): def setUp(self): self.grocy = Grocy("https://example.com/api/", "api_key") def test_init(self): assert isinstance(self.grocy, Grocy) @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/", "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", "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/api/stock/products/0", json=resp, status=200) product.get_details(api_client) assert product.name == "string" @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/", "api_key") responses.add(responses.GET, "https://example.com/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/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/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/api/stock", json=resp, status=200)
async def async_setup_entry(hass, config_entry): """Set up this integration using UI.""" from pygrocy import Grocy, TransactionType from datetime import datetime import iso8601 conf = hass.data.get(DOMAIN_DATA) if config_entry.source == config_entries.SOURCE_IMPORT: if conf is None: hass.async_create_task( hass.config_entries.async_remove(config_entry.entry_id)) return False # Print startup message _LOGGER.info( CC_STARTUP_VERSION.format(name=DOMAIN, version=VERSION, issue_link=ISSUE_URL)) # Check that all required files are present if not await hass.async_add_executor_job(check_files, hass): return False # Create DATA dict hass.data[DOMAIN_DATA] = {} # Get "global" configuration. url = config_entry.data.get(CONF_URL) api_key = config_entry.data.get(CONF_API_KEY) verify_ssl = config_entry.data.get(CONF_VERIFY_SSL) port_number = config_entry.data.get(CONF_PORT) hash_key = hashlib.md5(api_key.encode("utf-8") + url.encode("utf-8")).hexdigest() # Configure the client. grocy = Grocy(url, api_key, port_number, verify_ssl) hass.data[DOMAIN_DATA]["client"] = GrocyData(hass, grocy) hass.data[DOMAIN_DATA]["hash_key"] = hash_key # Add sensor hass.async_add_job( hass.config_entries.async_forward_entry_setup(config_entry, "sensor")) # Add sensor hass.async_add_job( hass.config_entries.async_forward_entry_setup(config_entry, "binary_sensor")) @callback def handle_add_product(call): product_id = call.data["product_id"] amount = call.data.get("amount", 0) price = call.data.get("price", None) grocy.add_product(product_id, amount, price) hass.services.async_register(DOMAIN, "add_product", handle_add_product) @callback def handle_consume_product(call): product_id = call.data["product_id"] amount = call.data.get("amount", 0) spoiled = call.data.get("spoiled", False) transaction_type_raw = call.data.get("transaction_type", None) transaction_type = TransactionType.CONSUME if transaction_type_raw is not None: transaction_type = TransactionType[transaction_type_raw] grocy.consume_product(product_id, amount, spoiled=spoiled, transaction_type=transaction_type) hass.services.async_register(DOMAIN, "consume_product", handle_consume_product) @callback def handle_execute_chore(call): chore_id = call.data["chore_id"] done_by = call.data.get("done_by", None) tracked_time_str = call.data.get("tracked_time", None) tracked_time = datetime.now() if tracked_time_str is not None: tracked_time = iso8601.parse_date(tracked_time_str) grocy.execute_chore(chore_id, done_by, tracked_time) asyncio.run_coroutine_threadsafe( entity_component.async_update_entity(hass, "sensor.grocy_chores"), hass.loop) hass.services.async_register(DOMAIN, "execute_chore", handle_execute_chore) return True
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)
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") 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_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 = 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
def grocy(): yield Grocy(CONST_BASE_URL, "demo_mode", verify_ssl=CONST_SSL, port=CONST_PORT)
def __init__(self, hass, url, api_key, port_number, verify_ssl): """Initialize.""" super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL) self.api = Grocy(url, api_key, port_number, verify_ssl) self.entities = [] self.data = {}