diff --git a/tests/components/v2c/__init__.py b/tests/components/v2c/__init__.py index fdb29e58644..6cb6662b850 100644 --- a/tests/components/v2c/__init__.py +++ b/tests/components/v2c/__init__.py @@ -1 +1,11 @@ """Tests for the V2C integration.""" + +from tests.common import MockConfigEntry + + +async def init_integration(hass, config_entry: MockConfigEntry) -> MockConfigEntry: + """Set up the V2C integration in Home Assistant.""" + 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/v2c/conftest.py b/tests/components/v2c/conftest.py index 2bdfc405e2d..3508c0596b2 100644 --- a/tests/components/v2c/conftest.py +++ b/tests/components/v2c/conftest.py @@ -4,6 +4,12 @@ from collections.abc import Generator from unittest.mock import AsyncMock, patch import pytest +from pytrydan.models.trydan import TrydanData + +from homeassistant.components.v2c import DOMAIN +from homeassistant.const import CONF_HOST + +from tests.common import MockConfigEntry, load_json_object_fixture @pytest.fixture @@ -13,3 +19,33 @@ def mock_setup_entry() -> Generator[AsyncMock, None, None]: "homeassistant.components.v2c.async_setup_entry", return_value=True ) as mock_setup_entry: yield mock_setup_entry + + +@pytest.fixture +def mock_config_entry() -> MockConfigEntry: + """Define a config entry fixture.""" + return MockConfigEntry( + domain=DOMAIN, + entry_id="da58ee91f38c2406c2a36d0a1a7f8569", + title="EVSE 1.1.1.1", + data={CONF_HOST: "1.1.1.1"}, + ) + + +@pytest.fixture +def mock_v2c_client() -> Generator[AsyncMock, None, None]: + """Mock a V2C client.""" + with ( + patch( + "homeassistant.components.v2c.Trydan", + autospec=True, + ) as mock_client, + patch( + "homeassistant.components.v2c.config_flow.Trydan", + new=mock_client, + ), + ): + client = mock_client.return_value + get_data_json = load_json_object_fixture("get_data.json", DOMAIN) + client.get_data.return_value = TrydanData.from_api(get_data_json) + yield client diff --git a/tests/components/v2c/fixtures/get_data.json b/tests/components/v2c/fixtures/get_data.json new file mode 100644 index 00000000000..7c250dee021 --- /dev/null +++ b/tests/components/v2c/fixtures/get_data.json @@ -0,0 +1,23 @@ +{ + "ID": "ABC123", + "ChargeState": 2, + "ReadyState": 0, + "ChargePower": 1500.27, + "ChargeEnergy": 1.8, + "SlaveError": 4, + "ChargeTime": 4355, + "HousePower": 0.0, + "FVPower": 0.0, + "BatteryPower": 0.0, + "Paused": 0, + "Locked": 0, + "Timer": 0, + "Intensity": 6, + "Dynamic": 0, + "MinIntensity": 6, + "MaxIntensity": 16, + "PauseDynamic": 0, + "FirmwareVersion": "2.1.7", + "DynamicPowerMode": 2, + "ContractedPower": 4600 +} diff --git a/tests/components/v2c/snapshots/test_sensor.ambr b/tests/components/v2c/snapshots/test_sensor.ambr new file mode 100644 index 00000000000..2504aa2e7c8 --- /dev/null +++ b/tests/components/v2c/snapshots/test_sensor.ambr @@ -0,0 +1,257 @@ +# serializer version: 1 +# name: test_sensor[sensor.evse_1_1_1_1_charge_energy-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.evse_1_1_1_1_charge_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Charge energy', + 'platform': 'v2c', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'charge_energy', + 'unique_id': 'da58ee91f38c2406c2a36d0a1a7f8569_charge_energy', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.evse_1_1_1_1_charge_energy-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'energy', + 'friendly_name': 'EVSE 1.1.1.1 Charge energy', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.evse_1_1_1_1_charge_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1.8', + }) +# --- +# name: test_sensor[sensor.evse_1_1_1_1_charge_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.evse_1_1_1_1_charge_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:ev-station', + 'original_name': 'Charge power', + 'platform': 'v2c', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'charge_power', + 'unique_id': 'da58ee91f38c2406c2a36d0a1a7f8569_charge_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.evse_1_1_1_1_charge_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'EVSE 1.1.1.1 Charge power', + 'icon': 'mdi:ev-station', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.evse_1_1_1_1_charge_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1500.27', + }) +# --- +# name: test_sensor[sensor.evse_1_1_1_1_charge_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.evse_1_1_1_1_charge_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Charge time', + 'platform': 'v2c', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'charge_time', + 'unique_id': 'da58ee91f38c2406c2a36d0a1a7f8569_charge_time', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.evse_1_1_1_1_charge_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'EVSE 1.1.1.1 Charge time', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.evse_1_1_1_1_charge_time', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '4355', + }) +# --- +# name: test_sensor[sensor.evse_1_1_1_1_house_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.evse_1_1_1_1_house_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'House power', + 'platform': 'v2c', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'house_power', + 'unique_id': 'da58ee91f38c2406c2a36d0a1a7f8569_house_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.evse_1_1_1_1_house_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'EVSE 1.1.1.1 House power', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.evse_1_1_1_1_house_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensor[sensor.evse_1_1_1_1_photovoltaic_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.evse_1_1_1_1_photovoltaic_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Photovoltaic power', + 'platform': 'v2c', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'fv_power', + 'unique_id': 'da58ee91f38c2406c2a36d0a1a7f8569_fv_power', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.evse_1_1_1_1_photovoltaic_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'EVSE 1.1.1.1 Photovoltaic power', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.evse_1_1_1_1_photovoltaic_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- diff --git a/tests/components/v2c/test_config_flow.py b/tests/components/v2c/test_config_flow.py index 04cf66d1d58..993fcaccc58 100644 --- a/tests/components/v2c/test_config_flow.py +++ b/tests/components/v2c/test_config_flow.py @@ -1,41 +1,36 @@ """Test the V2C config flow.""" -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock import pytest from pytrydan.exceptions import TrydanError -from homeassistant import config_entries from homeassistant.components.v2c.const import DOMAIN +from homeassistant.config_entries import SOURCE_USER +from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType -async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None: - """Test we get the form.""" +async def test_full_flow( + hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_v2c_client: AsyncMock +) -> None: + """Test we can finish a config flow.""" result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} + DOMAIN, context={"source": SOURCE_USER} ) assert result["type"] is FlowResultType.FORM assert result["errors"] == {} - with patch( - "pytrydan.Trydan.get_data", - return_value={}, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - }, - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_HOST: "1.1.1.1"}, + ) + await hass.async_block_till_done() - assert result2["type"] is FlowResultType.CREATE_ENTRY - assert result2["title"] == "EVSE 1.1.1.1" - assert result2["data"] == { - "host": "1.1.1.1", - } + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "EVSE 1.1.1.1" + assert result["data"] == {CONF_HOST: "1.1.1.1"} assert len(mock_setup_entry.mock_calls) == 1 @@ -47,41 +42,32 @@ async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None: ], ) async def test_form_cannot_connect( - hass: HomeAssistant, mock_setup_entry: AsyncMock, side_effect: Exception, error: str + hass: HomeAssistant, + mock_setup_entry: AsyncMock, + side_effect: Exception, + error: str, + mock_v2c_client: AsyncMock, ) -> None: """Test we handle cannot connect error.""" result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} + DOMAIN, context={"source": SOURCE_USER} + ) + mock_v2c_client.get_data.side_effect = side_effect + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_HOST: "1.1.1.1"}, ) - with patch( - "pytrydan.Trydan.get_data", - side_effect=side_effect, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - }, - ) + assert result["type"] is FlowResultType.FORM + assert result["errors"] == {"base": error} + mock_v2c_client.get_data.side_effect = None - assert result2["type"] is FlowResultType.FORM - assert result2["errors"] == {"base": error} + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_HOST: "1.1.1.1"}, + ) + await hass.async_block_till_done() - with patch( - "pytrydan.Trydan.get_data", - return_value={}, - ): - result3 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - }, - ) - await hass.async_block_till_done() - - assert result3["type"] is FlowResultType.CREATE_ENTRY - assert result3["title"] == "EVSE 1.1.1.1" - assert result3["data"] == { - "host": "1.1.1.1", - } + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "EVSE 1.1.1.1" + assert result["data"] == {CONF_HOST: "1.1.1.1"} diff --git a/tests/components/v2c/test_sensor.py b/tests/components/v2c/test_sensor.py new file mode 100644 index 00000000000..b30dfd436ff --- /dev/null +++ b/tests/components/v2c/test_sensor.py @@ -0,0 +1,27 @@ +"""Test the V2C sensor platform.""" + +from unittest.mock import AsyncMock, patch + +from syrupy import SnapshotAssertion + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import init_integration + +from tests.common import MockConfigEntry, snapshot_platform + + +async def test_sensor( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + snapshot: SnapshotAssertion, + mock_v2c_client: AsyncMock, + mock_config_entry: MockConfigEntry, + entity_registry_enabled_by_default: None, +) -> None: + """Test states of the sensor.""" + with patch("homeassistant.components.v2c.PLATFORMS", [Platform.SENSOR]): + await init_integration(hass, mock_config_entry) + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)