diff --git a/tests/components/picnic/__init__.py b/tests/components/picnic/__init__.py index fe6e65cbd2b3..2330da78648a 100644 --- a/tests/components/picnic/__init__.py +++ b/tests/components/picnic/__init__.py @@ -1 +1,13 @@ """Tests for the Picnic integration.""" + +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry + + +async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None: + """Fixture for setting up the component.""" + config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() diff --git a/tests/components/picnic/snapshots/test_sensor.ambr b/tests/components/picnic/snapshots/test_sensor.ambr new file mode 100644 index 000000000000..0034b024cc8a --- /dev/null +++ b/tests/components/picnic/snapshots/test_sensor.ambr @@ -0,0 +1,883 @@ +# serializer version: 1 +# name: test_all_entities[sensor.mock_title_cart_items_count-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_cart_items_count', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Cart items count', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Cart items count', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'cart_items_count', + 'unique_id': '295-6y3-1nf4.cart_items_count', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_cart_items_count-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'Mock Title Cart items count', + }), + 'context': , + 'entity_id': 'sensor.mock_title_cart_items_count', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10', + }) +# --- +# name: test_all_entities[sensor.mock_title_cart_total_price-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_cart_total_price', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Cart total price', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Cart total price', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'cart_total_price', + 'unique_id': '295-6y3-1nf4.cart_total_price', + 'unit_of_measurement': '€', + }) +# --- +# name: test_all_entities[sensor.mock_title_cart_total_price-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'Mock Title Cart total price', + : '€', + }), + 'context': , + 'entity_id': 'sensor.mock_title_cart_total_price', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '25.35', + }) +# --- +# name: test_all_entities[sensor.mock_title_end_of_last_order_s_slot-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_end_of_last_order_s_slot', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': "End of last order's slot", + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': "End of last order's slot", + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_order_slot_end', + 'unique_id': '295-6y3-1nf4.last_order_slot_end', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_end_of_last_order_s_slot-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : "Mock Title End of last order's slot", + }), + 'context': , + 'entity_id': 'sensor.mock_title_end_of_last_order_s_slot', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2021-02-26T20:15:00+00:00', + }) +# --- +# name: test_all_entities[sensor.mock_title_end_of_next_delivery_s_slot-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_end_of_next_delivery_s_slot', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': "End of next delivery's slot", + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': "End of next delivery's slot", + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'next_delivery_slot_end', + 'unique_id': '295-6y3-1nf4.next_delivery_slot_end', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_end_of_next_delivery_s_slot-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : "Mock Title End of next delivery's slot", + }), + 'context': , + 'entity_id': 'sensor.mock_title_end_of_next_delivery_s_slot', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_all_entities[sensor.mock_title_end_of_selected_slot-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_end_of_selected_slot', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'End of selected slot', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'End of selected slot', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'selected_slot_end', + 'unique_id': '295-6y3-1nf4.selected_slot_end', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_end_of_selected_slot-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : 'Mock Title End of selected slot', + }), + 'context': , + 'entity_id': 'sensor.mock_title_end_of_selected_slot', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2021-03-03T14:45:00+00:00', + }) +# --- +# name: test_all_entities[sensor.mock_title_estimated_arrival_of_next_delivery-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_estimated_arrival_of_next_delivery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Estimated arrival of next delivery', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Estimated arrival of next delivery', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'next_delivery_estimated_arrival', + 'unique_id': '295-6y3-1nf4.next_delivery_estimated_arrival', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_estimated_arrival_of_next_delivery-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : 'Mock Title Estimated arrival of next delivery', + }), + 'context': , + 'entity_id': 'sensor.mock_title_estimated_arrival_of_next_delivery', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_all_entities[sensor.mock_title_expected_end_of_next_delivery-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_expected_end_of_next_delivery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Expected end of next delivery', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Expected end of next delivery', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'next_delivery_eta_end', + 'unique_id': '295-6y3-1nf4.next_delivery_eta_end', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_expected_end_of_next_delivery-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : 'Mock Title Expected end of next delivery', + }), + 'context': , + 'entity_id': 'sensor.mock_title_expected_end_of_next_delivery', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_all_entities[sensor.mock_title_expected_start_of_next_delivery-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_expected_start_of_next_delivery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Expected start of next delivery', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Expected start of next delivery', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'next_delivery_eta_start', + 'unique_id': '295-6y3-1nf4.next_delivery_eta_start', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_expected_start_of_next_delivery-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : 'Mock Title Expected start of next delivery', + }), + 'context': , + 'entity_id': 'sensor.mock_title_expected_start_of_next_delivery', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_all_entities[sensor.mock_title_last_order_delivery_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_last_order_delivery_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Last order delivery time', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Last order delivery time', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_order_delivery_time', + 'unique_id': '295-6y3-1nf4.last_order_delivery_time', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_last_order_delivery_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : 'Mock Title Last order delivery time', + }), + 'context': , + 'entity_id': 'sensor.mock_title_last_order_delivery_time', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2021-02-26T19:54:05+00:00', + }) +# --- +# name: test_all_entities[sensor.mock_title_max_order_time_of_last_order-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_max_order_time_of_last_order', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Max order time of last order', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Max order time of last order', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_order_max_order_time', + 'unique_id': '295-6y3-1nf4.last_order_max_order_time', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_max_order_time_of_last_order-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : 'Mock Title Max order time of last order', + }), + 'context': , + 'entity_id': 'sensor.mock_title_max_order_time_of_last_order', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2021-02-25T21:00:00+00:00', + }) +# --- +# name: test_all_entities[sensor.mock_title_max_order_time_of_selected_slot-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_max_order_time_of_selected_slot', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Max order time of selected slot', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Max order time of selected slot', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'selected_slot_max_order_time', + 'unique_id': '295-6y3-1nf4.selected_slot_max_order_time', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_max_order_time_of_selected_slot-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : 'Mock Title Max order time of selected slot', + }), + 'context': , + 'entity_id': 'sensor.mock_title_max_order_time_of_selected_slot', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2021-03-02T21:00:00+00:00', + }) +# --- +# name: test_all_entities[sensor.mock_title_minimum_order_value_for_selected_slot-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_minimum_order_value_for_selected_slot', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Minimum order value for selected slot', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Minimum order value for selected slot', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'selected_slot_min_order_value', + 'unique_id': '295-6y3-1nf4.selected_slot_min_order_value', + 'unit_of_measurement': '€', + }) +# --- +# name: test_all_entities[sensor.mock_title_minimum_order_value_for_selected_slot-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'Mock Title Minimum order value for selected slot', + : '€', + }), + 'context': , + 'entity_id': 'sensor.mock_title_minimum_order_value_for_selected_slot', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '35.0', + }) +# --- +# name: test_all_entities[sensor.mock_title_start_of_last_order_s_slot-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_start_of_last_order_s_slot', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': "Start of last order's slot", + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': "Start of last order's slot", + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_order_slot_start', + 'unique_id': '295-6y3-1nf4.last_order_slot_start', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_start_of_last_order_s_slot-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : "Mock Title Start of last order's slot", + }), + 'context': , + 'entity_id': 'sensor.mock_title_start_of_last_order_s_slot', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2021-02-26T19:15:00+00:00', + }) +# --- +# name: test_all_entities[sensor.mock_title_start_of_next_delivery_s_slot-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_start_of_next_delivery_s_slot', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': "Start of next delivery's slot", + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': "Start of next delivery's slot", + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'next_delivery_slot_start', + 'unique_id': '295-6y3-1nf4.next_delivery_slot_start', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_start_of_next_delivery_s_slot-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : "Mock Title Start of next delivery's slot", + }), + 'context': , + 'entity_id': 'sensor.mock_title_start_of_next_delivery_s_slot', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_all_entities[sensor.mock_title_start_of_selected_slot-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_start_of_selected_slot', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Start of selected slot', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Start of selected slot', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'selected_slot_start', + 'unique_id': '295-6y3-1nf4.selected_slot_start', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_start_of_selected_slot-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'timestamp', + : 'Mock Title Start of selected slot', + }), + 'context': , + 'entity_id': 'sensor.mock_title_start_of_selected_slot', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2021-03-03T13:45:00+00:00', + }) +# --- +# name: test_all_entities[sensor.mock_title_status_of_last_order-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_status_of_last_order', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Status of last order', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Status of last order', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_order_status', + 'unique_id': '295-6y3-1nf4.last_order_status', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.mock_title_status_of_last_order-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'Mock Title Status of last order', + }), + 'context': , + 'entity_id': 'sensor.mock_title_status_of_last_order', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'COMPLETED', + }) +# --- +# name: test_all_entities[sensor.mock_title_total_price_of_last_order-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_title_total_price_of_last_order', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Total price of last order', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Total price of last order', + 'platform': 'picnic', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_order_total_price', + 'unique_id': '295-6y3-1nf4.last_order_total_price', + 'unit_of_measurement': '€', + }) +# --- +# name: test_all_entities[sensor.mock_title_total_price_of_last_order-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'Data provided by Picnic', + : 'Mock Title Total price of last order', + : '€', + }), + 'context': , + 'entity_id': 'sensor.mock_title_total_price_of_last_order', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '41.33', + }) +# --- diff --git a/tests/components/picnic/test_sensor.py b/tests/components/picnic/test_sensor.py index 0dbbc2a6a401..88630eb17d11 100644 --- a/tests/components/picnic/test_sensor.py +++ b/tests/components/picnic/test_sensor.py @@ -1,628 +1,373 @@ """The tests for the Picnic sensor platform.""" -import copy -from datetime import timedelta -import unittest -from unittest.mock import patch +from unittest.mock import MagicMock, patch +from freezegun.api import FrozenDateTimeFactory import pytest -from python_picnic_api2.models import Cart, DeliverySummary, User +from python_picnic_api2.models import DeliverySummary, Eta import requests +from syrupy.assertion import SnapshotAssertion -from homeassistant import config_entries -from homeassistant.components.picnic import const -from homeassistant.components.picnic.const import DOMAIN -from homeassistant.components.picnic.sensor import SENSOR_TYPES -from homeassistant.components.sensor import SensorDeviceClass +from homeassistant.components.picnic.const import DEFAULT_UPDATE_INTERVAL, DOMAIN +from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ( CONF_ACCESS_TOKEN, - CONF_COUNTRY_CODE, - CURRENCY_EURO, STATE_UNAVAILABLE, STATE_UNKNOWN, Platform, ) +from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er -from homeassistant.util import dt as dt_util -from tests.common import ( - MockConfigEntry, - async_fire_time_changed, - async_test_home_assistant, -) +from . import setup_integration -DEFAULT_USER_RESPONSE = { - "user_id": "295-6y3-1nf4", - "firstname": "User", - "lastname": "Name", - "address": { - "house_number": 123, - "house_number_ext": "a", - "postcode": "4321 AB", - "street": "Commonstreet", - "city": "Somewhere", - }, - "total_deliveries": 123, - "completed_deliveries": 112, -} -DEFAULT_CART_RESPONSE = { - "items": [], - "delivery_slots": [ - { - "slot_id": "611a3b074872b23576bef456a", - "window_start": "2021-03-03T14:45:00.000+01:00", - "window_end": "2021-03-03T15:45:00.000+01:00", - "cut_off_time": "2021-03-02T22:00:00.000+01:00", - "minimum_order_value": 3500, +from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform + + +@pytest.fixture(autouse=True) +def enable_all_entities(entity_registry_enabled_by_default: None) -> None: + """Make sure all entities are enabled.""" + + +async def test_all_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + entity_registry: er.EntityRegistry, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test all entities.""" + with patch("homeassistant.components.picnic.PLATFORMS", [Platform.SENSOR]): + await setup_integration(hass, mock_config_entry) + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +async def test_setup_platform_not_available( + hass: HomeAssistant, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test the set-up of the sensor platform if API is not available.""" + mock_picnic_api.get_user.side_effect = requests.exceptions.ConnectionError + mock_picnic_api.get_cart.side_effect = requests.exceptions.ConnectionError + mock_picnic_api.get_deliveries.side_effect = requests.exceptions.ConnectionError + mock_picnic_api.get_delivery_position.side_effect = ( + requests.exceptions.ConnectionError + ) + + await setup_integration(hass, mock_config_entry) + + assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + assert hass.states.get("sensor.mock_title_max_order_time_of_selected_slot") is None + assert hass.states.get("sensor.mock_title_status_of_last_order") is None + assert hass.states.get("sensor.mock_title_total_price_of_last_order") is None + + +async def test_sensors_no_selected_time_slot( + hass: HomeAssistant, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test sensor states with no explicit selected time slot.""" + cart = mock_picnic_api.get_cart.return_value + cart.selected_slot.state = "IMPLICIT" + + await setup_integration(hass, mock_config_entry) + + assert ( + hass.states.get("sensor.mock_title_start_of_selected_slot").state + == STATE_UNKNOWN + ) + assert ( + hass.states.get("sensor.mock_title_end_of_selected_slot").state == STATE_UNKNOWN + ) + assert ( + hass.states.get("sensor.mock_title_max_order_time_of_selected_slot").state + == STATE_UNKNOWN + ) + assert ( + hass.states.get("sensor.mock_title_minimum_order_value_for_selected_slot").state + == STATE_UNKNOWN + ) + + +async def test_next_delivery_sensors( + hass: HomeAssistant, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test sensor states when last order is not yet delivered.""" + delivery = mock_picnic_api.get_deliveries.return_value[0] + delivery.raw["delivery_time"] = None + delivery.status = "CURRENT" + + await setup_integration(hass, mock_config_entry) + + assert ( + hass.states.get("sensor.mock_title_last_order_delivery_time").state + == STATE_UNKNOWN + ) + assert ( + hass.states.get("sensor.mock_title_expected_start_of_next_delivery").state + == "2021-02-26T19:54:00+00:00" + ) + assert ( + hass.states.get("sensor.mock_title_expected_end_of_next_delivery").state + == "2021-02-26T20:14:00+00:00" + ) + assert ( + hass.states.get("sensor.mock_title_start_of_next_delivery_s_slot").state + == "2021-02-26T19:15:00+00:00" + ) + assert ( + hass.states.get("sensor.mock_title_end_of_next_delivery_s_slot").state + == "2021-02-26T20:15:00+00:00" + ) + + +async def test_sensors_eta_date_malformed( + hass: HomeAssistant, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test sensor states when last order eta dates are malformed.""" + await setup_integration(hass, mock_config_entry) + + delivery = mock_picnic_api.get_deliveries.return_value[0] + delivery.eta2 = Eta(start="wrong-time", end="other-malformed-datetime") + delivery.status = "CURRENT" + + freezer.tick(DEFAULT_UPDATE_INTERVAL) + async_fire_time_changed(hass) + await hass.async_block_till_done(wait_background_tasks=True) + + assert ( + hass.states.get("sensor.mock_title_expected_start_of_next_delivery").state + == STATE_UNKNOWN + ) + assert ( + hass.states.get("sensor.mock_title_expected_end_of_next_delivery").state + == STATE_UNKNOWN + ) + + +async def test_sensors_use_detailed_eta_if_available( + hass: HomeAssistant, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test sensor states use the more precise delivery position ETA.""" + await setup_integration(hass, mock_config_entry) + + delivery = mock_picnic_api.get_deliveries.return_value[0] + delivery.raw["delivery_time"] = None + delivery.status = "CURRENT" + mock_picnic_api.get_delivery_position.return_value = { + "eta_window": { + "start": "2021-03-05T10:19:20.452+00:00", + "end": "2021-03-05T10:39:20.452+00:00", }, - ], - "selected_slot": {"slot_id": "611a3b074872b23576bef456a", "state": "EXPLICIT"}, - "total_count": 10, - "total_price": 2535, -} -DEFAULT_DELIVERY_RESPONSE = { - "delivery_id": "z28fjso23e", - "creation_time": "2021-02-24T21:48:46.395+01:00", - "slot": { - "slot_id": "602473859a40dc24c6b65879", - "hub_id": "AMS", - "window_start": "2021-02-26T20:15:00.000+01:00", - "window_end": "2021-02-26T21:15:00.000+01:00", - "cut_off_time": "2021-02-25T22:00:00.000+01:00", - "minimum_order_value": 3500, - }, - "eta2": { - "start": "2021-02-26T20:54:00.000+01:00", - "end": "2021-02-26T21:14:00.000+01:00", - }, - "status": "COMPLETED", - "delivery_time": { - "start": "2021-02-26T20:54:05.221+01:00", - "end": "2021-02-26T20:58:31.802+01:00", - }, - "orders": [ - { - "creation_time": "2021-02-24T21:48:46.418+01:00", - "total_price": 3597, - }, - { - "creation_time": "2021-02-25T17:10:26.816+01:00", - "total_price": 536, - }, - ], -} + "eta": 1614941090000, + } -SENSOR_KEYS = [desc.key for desc in SENSOR_TYPES] + freezer.tick(DEFAULT_UPDATE_INTERVAL) + async_fire_time_changed(hass) + await hass.async_block_till_done(wait_background_tasks=True) + + mock_picnic_api.get_delivery_position.assert_called_with(delivery.delivery_id) + assert ( + hass.states.get("sensor.mock_title_expected_start_of_next_delivery").state + == "2021-03-05T10:19:20+00:00" + ) + assert ( + hass.states.get("sensor.mock_title_expected_end_of_next_delivery").state + == "2021-03-05T10:39:20+00:00" + ) + assert ( + hass.states.get("sensor.mock_title_estimated_arrival_of_next_delivery").state + == "2021-03-05T10:44:50+00:00" + ) -@pytest.mark.usefixtures("hass_storage") -class TestPicnicSensor(unittest.IsolatedAsyncioTestCase): - """Test the Picnic sensor.""" +async def test_sensors_no_data( + hass: HomeAssistant, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test sensor states when the api only returns empty objects.""" + await setup_integration(hass, mock_config_entry) - async def asyncSetUp(self): - """Set up things to be run when tests are started.""" - self._manager = async_test_home_assistant() - self.hass = await self._manager.__aenter__() - self.entity_registry = er.async_get(self.hass) + mock_picnic_api.get_user.return_value = {} + mock_picnic_api.get_cart.return_value = None + mock_picnic_api.get_deliveries.return_value = None + mock_picnic_api.get_delivery_position.side_effect = ValueError - # Patch the api client - self.picnic_patcher = patch("homeassistant.components.picnic.PicnicAPI") - self.picnic_mock = self.picnic_patcher.start() - self.picnic_mock().session.auth_token = "3q29fpwhulzes" + freezer.tick(DEFAULT_UPDATE_INTERVAL) + async_fire_time_changed(hass) + await hass.async_block_till_done(wait_background_tasks=True) - # Add a config entry and setup the integration - config_data = { - CONF_ACCESS_TOKEN: "x-original-picnic-auth-token", - CONF_COUNTRY_CODE: "NL", - } - self.config_entry = MockConfigEntry( - domain=const.DOMAIN, - data=config_data, - unique_id="295-6y3-1nf4", - ) - self.config_entry.add_to_hass(self.hass) - - async def asyncTearDown(self): - """Tear down the test setup, stop hass/patchers.""" - await self.hass.async_stop(force=True) - await self._manager.__aexit__(None, None, None) - self.picnic_patcher.stop() - - @property - def _coordinator(self): - return self.config_entry.runtime_data - - def _assert_sensor(self, name, state=None, cls=None, unit=None, disabled=False): - sensor = self.hass.states.get(name) - if disabled: - assert sensor is None - return - - assert sensor.state == state - if cls: - assert sensor.attributes["device_class"] == cls - if unit: - assert sensor.attributes["unit_of_measurement"] == unit - - assert sensor.attributes["attribution"] == "Data provided by Picnic" - - async def _setup_platform( - self, use_default_responses=False, enable_all_sensors=True + for entity_id in ( + "sensor.mock_title_cart_total_price", + "sensor.mock_title_start_of_selected_slot", + "sensor.mock_title_end_of_selected_slot", + "sensor.mock_title_max_order_time_of_selected_slot", + "sensor.mock_title_minimum_order_value_for_selected_slot", + "sensor.mock_title_max_order_time_of_last_order", + "sensor.mock_title_last_order_delivery_time", + "sensor.mock_title_expected_start_of_next_delivery", + "sensor.mock_title_expected_end_of_next_delivery", ): - """Set up the Picnic sensor platform.""" - if use_default_responses: - self.picnic_mock().get_user.return_value = User.from_api( - copy.deepcopy(DEFAULT_USER_RESPONSE) - ) - self.picnic_mock().get_cart.return_value = Cart.from_api( - copy.deepcopy(DEFAULT_CART_RESPONSE) - ) - self.picnic_mock().get_deliveries.return_value = [ - DeliverySummary.from_api(copy.deepcopy(DEFAULT_DELIVERY_RESPONSE)) - ] - self.picnic_mock().get_delivery_position.return_value = {} + assert hass.states.get(entity_id).state == STATE_UNAVAILABLE - await self.hass.config_entries.async_setup(self.config_entry.entry_id) - await self.hass.async_block_till_done() - if enable_all_sensors: - await self._enable_all_sensors() +async def test_sensors_malformed_delivery_data( + hass: HomeAssistant, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test sensor states when the delivery api returns not a list.""" + await setup_integration(hass, mock_config_entry) - async def _enable_all_sensors(self): - """Enable all sensors of the Picnic integration.""" - # Enable the sensors - for sensor_type in SENSOR_KEYS: - entry = self.entity_registry.async_get_or_create( - Platform.SENSOR, DOMAIN, f"{self.config_entry.unique_id}.{sensor_type}" - ) - updated_entry = self.entity_registry.async_update_entity( - entry.entity_id, disabled_by=None - ) - assert updated_entry.disabled is False - await self.hass.async_block_till_done() + mock_picnic_api.get_deliveries.return_value = {"error": "message"} - # Trigger a reload of the data - async_fire_time_changed( - self.hass, - dt_util.utcnow() - + timedelta(seconds=config_entries.RELOAD_AFTER_UPDATE_DELAY + 1), - ) - await self.hass.async_block_till_done() + freezer.tick(DEFAULT_UPDATE_INTERVAL) + async_fire_time_changed(hass) + await hass.async_block_till_done(wait_background_tasks=True) - async def test_sensor_setup_platform_not_available(self): - """Test the set-up of the sensor platform if API is not available.""" - # Configure mock requests to yield exceptions - self.picnic_mock().get_user.side_effect = requests.exceptions.ConnectionError - self.picnic_mock().get_cart.side_effect = requests.exceptions.ConnectionError - self.picnic_mock().get_deliveries.side_effect = ( - requests.exceptions.ConnectionError - ) - self.picnic_mock().get_delivery_position.side_effect = ( - requests.exceptions.ConnectionError - ) - await self._setup_platform(enable_all_sensors=False) + for entity_id in ( + "sensor.mock_title_max_order_time_of_last_order", + "sensor.mock_title_last_order_delivery_time", + "sensor.mock_title_expected_start_of_next_delivery", + "sensor.mock_title_expected_end_of_next_delivery", + ): + assert hass.states.get(entity_id).state == STATE_UNKNOWN - # Assert that sensors are not set up - assert ( - self.hass.states.get("sensor.mock_title_max_order_time_of_selected_slot") - is None - ) - assert self.hass.states.get("sensor.mock_title_status_of_last_order") is None - assert ( - self.hass.states.get("sensor.mock_title_total_price_of_last_order") is None - ) - async def test_sensors_setup(self): - """Test the default sensor setup behaviour.""" - await self._setup_platform(use_default_responses=True) +async def test_sensors_malformed_response( + hass: HomeAssistant, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test sensors become unavailable when the API yields ValueError.""" + await setup_integration(hass, mock_config_entry) - self._assert_sensor("sensor.mock_title_cart_items_count", "10") - self._assert_sensor( - "sensor.mock_title_cart_total_price", - "25.35", - unit=CURRENCY_EURO, - ) - self._assert_sensor( - "sensor.mock_title_start_of_selected_slot", - "2021-03-03T13:45:00+00:00", - cls=SensorDeviceClass.TIMESTAMP, - ) - self._assert_sensor( - "sensor.mock_title_end_of_selected_slot", - "2021-03-03T14:45:00+00:00", - cls=SensorDeviceClass.TIMESTAMP, - ) - self._assert_sensor( - "sensor.mock_title_max_order_time_of_selected_slot", - "2021-03-02T21:00:00+00:00", - cls=SensorDeviceClass.TIMESTAMP, - ) - self._assert_sensor( - "sensor.mock_title_minimum_order_value_for_selected_slot", - "35.0", - ) - self._assert_sensor( - "sensor.mock_title_start_of_last_order_s_slot", - "2021-02-26T19:15:00+00:00", - cls=SensorDeviceClass.TIMESTAMP, - ) - self._assert_sensor( - "sensor.mock_title_end_of_last_order_s_slot", - "2021-02-26T20:15:00+00:00", - cls=SensorDeviceClass.TIMESTAMP, - ) - self._assert_sensor("sensor.mock_title_status_of_last_order", "COMPLETED") - self._assert_sensor( - "sensor.mock_title_max_order_time_of_last_order", - "2021-02-25T21:00:00+00:00", - cls=SensorDeviceClass.TIMESTAMP, - ) - self._assert_sensor( - "sensor.mock_title_last_order_delivery_time", - "2021-02-26T19:54:05+00:00", - cls=SensorDeviceClass.TIMESTAMP, - ) - self._assert_sensor( - "sensor.mock_title_total_price_of_last_order", - "41.33", - unit=CURRENCY_EURO, - ) - self._assert_sensor( - "sensor.mock_title_expected_start_of_next_delivery", - "unknown", - cls=SensorDeviceClass.TIMESTAMP, - ) - self._assert_sensor( - "sensor.mock_title_expected_end_of_next_delivery", - "unknown", - cls=SensorDeviceClass.TIMESTAMP, - ) - self._assert_sensor( - "sensor.mock_title_start_of_next_delivery_s_slot", - "unknown", - cls=SensorDeviceClass.TIMESTAMP, - ) - self._assert_sensor( - "sensor.mock_title_end_of_next_delivery_s_slot", - "unknown", - cls=SensorDeviceClass.TIMESTAMP, - ) + mock_picnic_api.get_user.side_effect = ValueError + mock_picnic_api.get_cart.side_effect = ValueError - async def test_sensors_setup_disabled_by_default(self): - """Test that some sensors are disabled by default.""" - await self._setup_platform(use_default_responses=True, enable_all_sensors=False) + freezer.tick(DEFAULT_UPDATE_INTERVAL) + async_fire_time_changed(hass) + await hass.async_block_till_done(wait_background_tasks=True) - self._assert_sensor("sensor.mock_title_cart_items_count", disabled=True) - self._assert_sensor( - "sensor.mock_title_start_of_last_order_s_slot", disabled=True - ) - self._assert_sensor("sensor.mock_title_end_of_last_order_s_slot", disabled=True) - self._assert_sensor("sensor.mock_title_status_of_last_order", disabled=True) - self._assert_sensor( - "sensor.mock_title_total_price_of_last_order", disabled=True - ) - self._assert_sensor( - "sensor.mock_title_start_of_next_delivery_s_slot", - disabled=True, - ) - self._assert_sensor( - "sensor.mock_title_end_of_next_delivery_s_slot", disabled=True - ) + assert ( + hass.states.get("sensor.mock_title_cart_total_price").state == STATE_UNAVAILABLE + ) - async def test_sensors_no_selected_time_slot(self): - """Test sensor states with no explicit selected time slot.""" - # Adjust cart response - cart_response = copy.deepcopy(DEFAULT_CART_RESPONSE) - cart_response["selected_slot"]["state"] = "IMPLICIT" - # Set mock responses - self.picnic_mock().get_user.return_value = User.from_api( - copy.deepcopy(DEFAULT_USER_RESPONSE) - ) - self.picnic_mock().get_cart.return_value = Cart.from_api(cart_response) - self.picnic_mock().get_deliveries.return_value = [ - DeliverySummary.from_api(copy.deepcopy(DEFAULT_DELIVERY_RESPONSE)) - ] - self.picnic_mock().get_delivery_position.return_value = {} - await self._setup_platform() +async def test_multiple_active_orders( + hass: HomeAssistant, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test sensor values with multiple active orders.""" + completed = mock_picnic_api.get_deliveries.return_value[0] - # Assert sensors are unknown - self._assert_sensor("sensor.mock_title_start_of_selected_slot", STATE_UNKNOWN) - self._assert_sensor("sensor.mock_title_end_of_selected_slot", STATE_UNKNOWN) - self._assert_sensor( - "sensor.mock_title_max_order_time_of_selected_slot", - STATE_UNKNOWN, - ) - self._assert_sensor( - "sensor.mock_title_minimum_order_value_for_selected_slot", - STATE_UNKNOWN, - ) + def _undelivered(window_start: str, window_end: str, eta_start: str, eta_end: str): + order = DeliverySummary.from_api(completed.raw.copy()) + order.raw["delivery_time"] = None + order.status = "CURRENT" + order.slot.window_start = window_start + order.slot.window_end = window_end + order.eta2 = Eta(start=eta_start, end=eta_end) + return order - async def test_next_delivery_sensors(self): - """Test sensor states when last order is not yet delivered.""" - # Adjust default delivery response - delivery_response = copy.deepcopy(DEFAULT_DELIVERY_RESPONSE) - del delivery_response["delivery_time"] - delivery_response["status"] = "CURRENT" + undelivered_order = _undelivered( + "2022-03-01T09:15:00.000+01:00", + "2022-03-01T10:15:00.000+01:00", + "2022-03-01T09:30:00.000+01:00", + "2022-03-01T09:45:00.000+01:00", + ) + undelivered_order_2 = _undelivered( + "2022-03-08T13:15:00.000+01:00", + "2022-03-08T14:15:00.000+01:00", + "2022-03-08T13:30:00.000+01:00", + "2022-03-08T13:45:00.000+01:00", + ) - # Set mock responses - self.picnic_mock().get_user.return_value = User.from_api( - copy.deepcopy(DEFAULT_USER_RESPONSE) - ) - self.picnic_mock().get_cart.return_value = Cart.from_api( - copy.deepcopy(DEFAULT_CART_RESPONSE) - ) - self.picnic_mock().get_deliveries.return_value = [ - DeliverySummary.from_api(delivery_response) - ] - self.picnic_mock().get_delivery_position.return_value = {} - await self._setup_platform() + mock_picnic_api.get_deliveries.return_value = [ + undelivered_order_2, + undelivered_order, + completed, + ] + mock_picnic_api.get_delivery_position.return_value = {} - # Assert delivery time is not available, but eta is - self._assert_sensor("sensor.mock_title_last_order_delivery_time", STATE_UNKNOWN) - self._assert_sensor( - "sensor.mock_title_expected_start_of_next_delivery", - "2021-02-26T19:54:00+00:00", - ) - self._assert_sensor( - "sensor.mock_title_expected_end_of_next_delivery", - "2021-02-26T20:14:00+00:00", - ) - self._assert_sensor( - "sensor.mock_title_start_of_next_delivery_s_slot", - "2021-02-26T19:15:00+00:00", - ) - self._assert_sensor( - "sensor.mock_title_end_of_next_delivery_s_slot", - "2021-02-26T20:15:00+00:00", - ) + await setup_integration(hass, mock_config_entry) - async def test_sensors_eta_date_malformed(self): - """Test sensor states when last order eta dates are malformed.""" - # Set-up platform with default mock responses - await self._setup_platform(use_default_responses=True) + assert ( + hass.states.get("sensor.mock_title_start_of_last_order_s_slot").state + == "2022-03-08T12:15:00+00:00" + ) + assert ( + hass.states.get("sensor.mock_title_end_of_last_order_s_slot").state + == "2022-03-08T13:15:00+00:00" + ) + assert ( + hass.states.get("sensor.mock_title_start_of_next_delivery_s_slot").state + == "2022-03-01T08:15:00+00:00" + ) + assert ( + hass.states.get("sensor.mock_title_end_of_next_delivery_s_slot").state + == "2022-03-01T09:15:00+00:00" + ) + assert ( + hass.states.get("sensor.mock_title_expected_start_of_next_delivery").state + == "2022-03-01T08:30:00+00:00" + ) + assert ( + hass.states.get("sensor.mock_title_expected_end_of_next_delivery").state + == "2022-03-01T08:45:00+00:00" + ) - # Set non-datetime strings as eta - eta_dates: dict[str, str] = { - "start": "wrong-time", - "end": "other-malformed-datetime", - } - delivery_response = copy.deepcopy(DEFAULT_DELIVERY_RESPONSE) - delivery_response["eta2"] = eta_dates - delivery_response["status"] = "CURRENT" - self.picnic_mock().get_deliveries.return_value = [ - DeliverySummary.from_api(delivery_response) - ] - await self._coordinator.async_refresh() - # Assert eta times are not available due to malformed date strings - self._assert_sensor( - "sensor.mock_title_expected_start_of_next_delivery", - STATE_UNKNOWN, - ) - self._assert_sensor( - "sensor.mock_title_expected_end_of_next_delivery", - STATE_UNKNOWN, - ) +async def test_device_registry_entry( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test if device registry entry is populated correctly.""" + await setup_integration(hass, mock_config_entry) - async def test_sensors_use_detailed_eta_if_available(self): - """Test sensor states when last order is not yet delivered.""" - # Set-up platform with default mock responses - await self._setup_platform(use_default_responses=True) + picnic_service = device_registry.async_get_device_by_identifier( + (DOMAIN, "295-6y3-1nf4"), + mock_config_entry.entry_id, + ) + assert picnic_service.model == "295-6y3-1nf4" + assert picnic_service.name == "Mock Title" + assert picnic_service.entry_type is dr.DeviceEntryType.SERVICE - # Provide a delivery position response with different ETA - # and remove delivery time from response - delivery_response = copy.deepcopy(DEFAULT_DELIVERY_RESPONSE) - del delivery_response["delivery_time"] - delivery_response["status"] = "CURRENT" - self.picnic_mock().get_deliveries.return_value = [ - DeliverySummary.from_api(delivery_response) - ] - self.picnic_mock().get_delivery_position.return_value = { - "eta_window": { - "start": "2021-03-05T10:19:20.452+00:00", - "end": "2021-03-05T10:39:20.452+00:00", - }, - "eta": 1614941090000, - } - await self._coordinator.async_refresh() - # Assert detailed ETA is used - self.picnic_mock().get_delivery_position.assert_called_with( - delivery_response["delivery_id"] - ) - self._assert_sensor( - "sensor.mock_title_expected_start_of_next_delivery", - "2021-03-05T10:19:20+00:00", - ) - self._assert_sensor( - "sensor.mock_title_expected_end_of_next_delivery", - "2021-03-05T10:39:20+00:00", - ) - self._assert_sensor( - "sensor.mock_title_estimated_arrival_of_next_delivery", - "2021-03-05T10:44:50+00:00", - ) +async def test_auth_token_is_saved_on_update( + hass: HomeAssistant, + mock_picnic_api: MagicMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test auth-token changes are reflected by the config entry.""" + await setup_integration(hass, mock_config_entry) - # The live estimate is cleared again once position data disappears - self.picnic_mock().get_delivery_position.return_value = {} - async_fire_time_changed(self.hass, dt_util.utcnow() + timedelta(minutes=31)) - await self.hass.async_block_till_done(wait_background_tasks=True) + updated_auth_token = "x-updated-picnic-auth-token" + mock_picnic_api.session.auth_token = updated_auth_token - self._assert_sensor( - "sensor.mock_title_estimated_arrival_of_next_delivery", - STATE_UNKNOWN, - ) + assert mock_config_entry.data.get(CONF_ACCESS_TOKEN) != updated_auth_token - async def test_sensors_no_data(self): - """Test sensor states when the api only returns empty objects.""" - # Setup platform with default responses - await self._setup_platform(use_default_responses=True) + freezer.tick(DEFAULT_UPDATE_INTERVAL) + async_fire_time_changed(hass) + await hass.async_block_till_done(wait_background_tasks=True) - # Change mock responses to empty data and refresh the coordinator - self.picnic_mock().get_user.return_value = {} - self.picnic_mock().get_cart.return_value = None - self.picnic_mock().get_deliveries.return_value = None - self.picnic_mock().get_delivery_position.side_effect = ValueError - await self._coordinator.async_refresh() - - # Assert all default-enabled sensors have STATE_UNAVAILABLE - # because the last update failed - assert self._coordinator.last_update_success is False - self._assert_sensor("sensor.mock_title_cart_total_price", STATE_UNAVAILABLE) - self._assert_sensor( - "sensor.mock_title_start_of_selected_slot", STATE_UNAVAILABLE - ) - self._assert_sensor("sensor.mock_title_end_of_selected_slot", STATE_UNAVAILABLE) - self._assert_sensor( - "sensor.mock_title_max_order_time_of_selected_slot", - STATE_UNAVAILABLE, - ) - self._assert_sensor( - "sensor.mock_title_minimum_order_value_for_selected_slot", - STATE_UNAVAILABLE, - ) - self._assert_sensor( - "sensor.mock_title_max_order_time_of_last_order", - STATE_UNAVAILABLE, - ) - self._assert_sensor( - "sensor.mock_title_last_order_delivery_time", - STATE_UNAVAILABLE, - ) - self._assert_sensor( - "sensor.mock_title_expected_start_of_next_delivery", - STATE_UNAVAILABLE, - ) - self._assert_sensor( - "sensor.mock_title_expected_end_of_next_delivery", - STATE_UNAVAILABLE, - ) - - async def test_sensors_malformed_delivery_data(self): - """Test sensor states when the delivery api returns not a list.""" - # Setup platform with default responses - await self._setup_platform(use_default_responses=True) - - # Change mock responses to empty data and refresh the coordinator - self.picnic_mock().get_deliveries.return_value = {"error": "message"} - await self._coordinator.async_refresh() - - # Assert all last-order sensors have STATE_UNAVAILABLE - # because the delivery info fetch failed - assert self._coordinator.last_update_success is True - self._assert_sensor( - "sensor.mock_title_max_order_time_of_last_order", - STATE_UNKNOWN, - ) - self._assert_sensor("sensor.mock_title_last_order_delivery_time", STATE_UNKNOWN) - self._assert_sensor( - "sensor.mock_title_expected_start_of_next_delivery", - STATE_UNKNOWN, - ) - self._assert_sensor( - "sensor.mock_title_expected_end_of_next_delivery", - STATE_UNKNOWN, - ) - - async def test_sensors_malformed_response(self): - """Test coordinator update fails when API yields ValueError.""" - # Setup platform with default responses - await self._setup_platform(use_default_responses=True) - - # Change mock responses to empty data and refresh the coordinator - self.picnic_mock().get_user.side_effect = ValueError - self.picnic_mock().get_cart.side_effect = ValueError - await self._coordinator.async_refresh() - - # Assert coordinator update failed - assert self._coordinator.last_update_success is False - - async def test_multiple_active_orders(self): - """Test sensor values with multiple active orders.""" - # Create 2 undelivered orders - undelivered_order = copy.deepcopy(DEFAULT_DELIVERY_RESPONSE) - del undelivered_order["delivery_time"] - undelivered_order["status"] = "CURRENT" - undelivered_order["slot"]["window_start"] = "2022-03-01T09:15:00.000+01:00" - undelivered_order["slot"]["window_end"] = "2022-03-01T10:15:00.000+01:00" - undelivered_order["eta2"]["start"] = "2022-03-01T09:30:00.000+01:00" - undelivered_order["eta2"]["end"] = "2022-03-01T09:45:00.000+01:00" - - undelivered_order_2 = copy.deepcopy(undelivered_order) - undelivered_order_2["slot"]["window_start"] = "2022-03-08T13:15:00.000+01:00" - undelivered_order_2["slot"]["window_end"] = "2022-03-08T14:15:00.000+01:00" - undelivered_order_2["eta2"]["start"] = "2022-03-08T13:30:00.000+01:00" - undelivered_order_2["eta2"]["end"] = "2022-03-08T13:45:00.000+01:00" - - deliveries_response = [ - DeliverySummary.from_api(undelivered_order_2), - DeliverySummary.from_api(undelivered_order), - DeliverySummary.from_api(copy.deepcopy(DEFAULT_DELIVERY_RESPONSE)), - ] - - # Set mock responses - self.picnic_mock().get_user.return_value = User.from_api( - copy.deepcopy(DEFAULT_USER_RESPONSE) - ) - self.picnic_mock().get_cart.return_value = Cart.from_api( - copy.deepcopy(DEFAULT_CART_RESPONSE) - ) - self.picnic_mock().get_deliveries.return_value = deliveries_response - self.picnic_mock().get_delivery_position.return_value = {} - await self._setup_platform() - - self._assert_sensor( - "sensor.mock_title_start_of_last_order_s_slot", - "2022-03-08T12:15:00+00:00", - ) - self._assert_sensor( - "sensor.mock_title_end_of_last_order_s_slot", - "2022-03-08T13:15:00+00:00", - ) - self._assert_sensor( - "sensor.mock_title_start_of_next_delivery_s_slot", - "2022-03-01T08:15:00+00:00", - ) - self._assert_sensor( - "sensor.mock_title_end_of_next_delivery_s_slot", - "2022-03-01T09:15:00+00:00", - ) - self._assert_sensor( - "sensor.mock_title_expected_start_of_next_delivery", - "2022-03-01T08:30:00+00:00", - ) - self._assert_sensor( - "sensor.mock_title_expected_end_of_next_delivery", - "2022-03-01T08:45:00+00:00", - ) - - async def test_device_registry_entry(self): - """Test if device registry entry is populated correctly.""" - # Setup platform and default mock responses - await self._setup_platform(use_default_responses=True) - - device_registry = dr.async_get(self.hass) # pylint: disable=home-assistant-tests-registry-fixtures - picnic_service = device_registry.async_get_device_by_identifier( - (const.DOMAIN, DEFAULT_USER_RESPONSE["user_id"]), - self.config_entry.entry_id, - ) - assert picnic_service.model == DEFAULT_USER_RESPONSE["user_id"] - assert picnic_service.name == "Mock Title" - assert picnic_service.entry_type is dr.DeviceEntryType.SERVICE - - async def test_auth_token_is_saved_on_update(self): - """Test auth-token changes are reflected by the config entry.""" - # Setup platform and default mock responses - await self._setup_platform(use_default_responses=True) - - # Set a different auth token in the session mock - updated_auth_token = "x-updated-picnic-auth-token" - self.picnic_mock().session.auth_token = updated_auth_token - - # Verify the updated auth token is not set and fetch data using the coordinator - assert self.config_entry.data.get(CONF_ACCESS_TOKEN) != updated_auth_token - await self._coordinator.async_refresh() - - # Verify that the updated auth token is saved in the config entry - assert self.config_entry.data.get(CONF_ACCESS_TOKEN) == updated_auth_token + assert mock_config_entry.data.get(CONF_ACCESS_TOKEN) == updated_auth_token