diff --git a/tests/components/enphase_envoy/conftest.py b/tests/components/enphase_envoy/conftest.py new file mode 100644 index 00000000000..cb01e8a81e4 --- /dev/null +++ b/tests/components/enphase_envoy/conftest.py @@ -0,0 +1,110 @@ +"""Define test fixtures for Enphase Envoy.""" +import json +from unittest.mock import AsyncMock, Mock, patch + +import pytest + +from homeassistant.components.enphase_envoy import DOMAIN +from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry, load_fixture + + +@pytest.fixture(name="config_entry") +def config_entry_fixture(hass: HomeAssistant, config, serial_number): + """Define a config entry fixture.""" + entry = MockConfigEntry( + domain=DOMAIN, + title=f"Envoy {serial_number}" if serial_number else "Envoy", + unique_id=serial_number, + data=config, + ) + entry.add_to_hass(hass) + return entry + + +@pytest.fixture(name="config") +def config_fixture(): + """Define a config entry data fixture.""" + return { + CONF_HOST: "1.1.1.1", + CONF_NAME: "Envoy 1234", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + } + + +@pytest.fixture(name="gateway_data", scope="session") +def gateway_data_fixture(): + """Define a fixture to return gateway data.""" + return json.loads(load_fixture("data.json", "enphase_envoy")) + + +@pytest.fixture(name="inverters_production_data", scope="session") +def inverters_production_data_fixture(): + """Define a fixture to return inverter production data.""" + return json.loads(load_fixture("inverters_production.json", "enphase_envoy")) + + +@pytest.fixture(name="mock_envoy_reader") +def mock_envoy_reader_fixture( + gateway_data, + mock_get_data, + mock_get_full_serial_number, + mock_inverters_production, + serial_number, +): + """Define a mocked EnvoyReader fixture.""" + mock_envoy_reader = Mock( + getData=mock_get_data, + get_full_serial_number=mock_get_full_serial_number, + inverters_production=mock_inverters_production, + ) + + for key, value in gateway_data.items(): + setattr(mock_envoy_reader, key, AsyncMock(return_value=value)) + + return mock_envoy_reader + + +@pytest.fixture(name="mock_get_full_serial_number") +def mock_get_full_serial_number_fixture(serial_number): + """Define a mocked EnvoyReader.get_full_serial_number fixture.""" + return AsyncMock(return_value=serial_number) + + +@pytest.fixture(name="mock_get_data") +def mock_get_data_fixture(): + """Define a mocked EnvoyReader.getData fixture.""" + return AsyncMock() + + +@pytest.fixture(name="mock_inverters_production") +def mock_inverters_production_fixture(inverters_production_data): + """Define a mocked EnvoyReader.inverters_production fixture.""" + return AsyncMock(return_value=inverters_production_data) + + +@pytest.fixture(name="setup_enphase_envoy") +async def setup_enphase_envoy_fixture(hass, config, mock_envoy_reader): + """Define a fixture to set up Enphase Envoy.""" + with patch( + "homeassistant.components.enphase_envoy.config_flow.EnvoyReader", + return_value=mock_envoy_reader, + ), patch( + "homeassistant.components.enphase_envoy.EnvoyReader", + return_value=mock_envoy_reader, + ), patch( + "homeassistant.components.enphase_envoy.PLATFORMS", [] + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + yield + + +@pytest.fixture(name="serial_number") +def serial_number_fixture(): + """Define a serial number fixture.""" + return "1234" diff --git a/tests/components/enphase_envoy/fixtures/__init__.py b/tests/components/enphase_envoy/fixtures/__init__.py new file mode 100644 index 00000000000..b3ef7db17a3 --- /dev/null +++ b/tests/components/enphase_envoy/fixtures/__init__.py @@ -0,0 +1 @@ +"""Define data fixtures for Enphase Envoy.""" diff --git a/tests/components/enphase_envoy/fixtures/data.json b/tests/components/enphase_envoy/fixtures/data.json new file mode 100644 index 00000000000..d6868a6dbf7 --- /dev/null +++ b/tests/components/enphase_envoy/fixtures/data.json @@ -0,0 +1,10 @@ +{ + "production": 1840, + "daily_production": 28223, + "seven_days_production": 174482, + "lifetime_production": 5924391, + "consumption": 1840, + "daily_consumption": 5923857, + "seven_days_consumption": 5923857, + "lifetime_consumption": 5923857 +} diff --git a/tests/components/enphase_envoy/fixtures/inverters_production.json b/tests/components/enphase_envoy/fixtures/inverters_production.json new file mode 100644 index 00000000000..14891f2d278 --- /dev/null +++ b/tests/components/enphase_envoy/fixtures/inverters_production.json @@ -0,0 +1,18 @@ +{ + "202140024014": [136, "2022-10-08 16:43:36"], + "202140023294": [163, "2022-10-08 16:43:41"], + "202140013819": [130, "2022-10-08 16:43:31"], + "202140023794": [139, "2022-10-08 16:43:38"], + "202140023381": [130, "2022-10-08 16:43:47"], + "202140024176": [54, "2022-10-08 16:43:59"], + "202140003284": [132, "2022-10-08 16:43:55"], + "202140019854": [129, "2022-10-08 16:43:58"], + "202140020743": [131, "2022-10-08 16:43:49"], + "202140023531": [28, "2022-10-08 16:43:53"], + "202140024241": [164, "2022-10-08 16:43:33"], + "202140022963": [164, "2022-10-08 16:43:41"], + "202140023149": [118, "2022-10-08 16:43:47"], + "202140024828": [129, "2022-10-08 16:43:36"], + "202140023269": [133, "2022-10-08 16:43:43"], + "202140024157": [112, "2022-10-08 16:43:52"] +} diff --git a/tests/components/enphase_envoy/test_config_flow.py b/tests/components/enphase_envoy/test_config_flow.py index caba2296927..fac5b01c60e 100644 --- a/tests/components/enphase_envoy/test_config_flow.py +++ b/tests/components/enphase_envoy/test_config_flow.py @@ -1,46 +1,31 @@ """Test the Enphase Envoy config flow.""" -from unittest.mock import MagicMock, patch +from unittest.mock import AsyncMock, MagicMock import httpx +import pytest from homeassistant import config_entries from homeassistant.components import zeroconf from homeassistant.components.enphase_envoy.const import DOMAIN -from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant -from tests.common import MockConfigEntry - -async def test_form(hass: HomeAssistant) -> None: +async def test_form(hass: HomeAssistant, config, setup_enphase_envoy) -> None: """Test we get the form.""" - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == "form" assert result["errors"] == {} - with patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.getData", - return_value=True, - ), patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.get_full_serial_number", - return_value="1234", - ), patch( - "homeassistant.components.enphase_envoy.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, - ) - await hass.async_block_till_done() - + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) assert result2["type"] == "create_entry" assert result2["title"] == "Envoy 1234" assert result2["data"] == { @@ -49,38 +34,27 @@ async def test_form(hass: HomeAssistant) -> None: "username": "test-username", "password": "test-password", } - assert len(mock_setup_entry.mock_calls) == 1 -async def test_user_no_serial_number(hass: HomeAssistant) -> None: +@pytest.mark.parametrize("serial_number", [None]) +async def test_user_no_serial_number( + hass: HomeAssistant, config, setup_enphase_envoy +) -> None: """Test user setup without a serial number.""" - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == "form" assert result["errors"] == {} - with patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.getData", - return_value=True, - ), patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.get_full_serial_number", - return_value=None, - ), patch( - "homeassistant.components.enphase_envoy.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, - ) - await hass.async_block_till_done() - + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) assert result2["type"] == "create_entry" assert result2["title"] == "Envoy" assert result2["data"] == { @@ -89,40 +63,36 @@ async def test_user_no_serial_number(hass: HomeAssistant) -> None: "username": "test-username", "password": "test-password", } - assert len(mock_setup_entry.mock_calls) == 1 -async def test_user_fetching_serial_fails(hass: HomeAssistant) -> None: +@pytest.mark.parametrize( + "mock_get_full_serial_number", + [ + AsyncMock( + side_effect=httpx.HTTPStatusError( + "any", request=MagicMock(), response=MagicMock() + ) + ) + ], +) +async def test_user_fetching_serial_fails( + hass: HomeAssistant, setup_enphase_envoy +) -> None: """Test user setup without a serial number.""" - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == "form" assert result["errors"] == {} - with patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.getData", - return_value=True, - ), patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.get_full_serial_number", - side_effect=httpx.HTTPStatusError( - "any", request=MagicMock(), response=MagicMock() - ), - ), patch( - "homeassistant.components.enphase_envoy.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, - ) - await hass.async_block_till_done() - + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) assert result2["type"] == "create_entry" assert result2["title"] == "Envoy" assert result2["data"] == { @@ -131,83 +101,75 @@ async def test_user_fetching_serial_fails(hass: HomeAssistant) -> None: "username": "test-username", "password": "test-password", } - assert len(mock_setup_entry.mock_calls) == 1 -async def test_form_invalid_auth(hass: HomeAssistant) -> None: +@pytest.mark.parametrize( + "mock_get_data", + [ + AsyncMock( + side_effect=httpx.HTTPStatusError( + "any", request=MagicMock(), response=MagicMock() + ) + ) + ], +) +async def test_form_invalid_auth(hass: HomeAssistant, setup_enphase_envoy) -> None: """Test we handle invalid auth.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - - with patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.getData", - side_effect=httpx.HTTPStatusError( - "any", request=MagicMock(), response=MagicMock() - ), - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, - ) - + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) assert result2["type"] == "form" assert result2["errors"] == {"base": "invalid_auth"} -async def test_form_cannot_connect(hass: HomeAssistant) -> None: +@pytest.mark.parametrize( + "mock_get_data", [AsyncMock(side_effect=httpx.HTTPError("any"))] +) +async def test_form_cannot_connect(hass: HomeAssistant, setup_enphase_envoy) -> None: """Test we handle cannot connect error.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - - with patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.getData", - side_effect=httpx.HTTPError("any"), - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, - ) - + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) assert result2["type"] == "form" assert result2["errors"] == {"base": "cannot_connect"} -async def test_form_unknown_error(hass: HomeAssistant) -> None: +@pytest.mark.parametrize("mock_get_data", [AsyncMock(side_effect=ValueError)]) +async def test_form_unknown_error(hass: HomeAssistant, setup_enphase_envoy) -> None: """Test we handle unknown error.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - - with patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.getData", - side_effect=ValueError, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, - ) - + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) assert result2["type"] == "form" assert result2["errors"] == {"base": "unknown"} -async def test_zeroconf(hass: HomeAssistant) -> None: +async def test_zeroconf(hass: HomeAssistant, setup_enphase_envoy) -> None: """Test we can setup from zeroconf.""" - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, @@ -221,28 +183,17 @@ async def test_zeroconf(hass: HomeAssistant) -> None: type="mock_type", ), ) - await hass.async_block_till_done() - assert result["type"] == "form" assert result["step_id"] == "user" - with patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.getData", - return_value=True, - ), patch( - "homeassistant.components.enphase_envoy.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, - ) - await hass.async_block_till_done() - + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) assert result2["type"] == "create_entry" assert result2["title"] == "Envoy 1234" assert result2["result"].unique_id == "1234" @@ -252,63 +203,34 @@ async def test_zeroconf(hass: HomeAssistant) -> None: "username": "test-username", "password": "test-password", } - assert len(mock_setup_entry.mock_calls) == 1 -async def test_form_host_already_exists(hass: HomeAssistant) -> None: +async def test_form_host_already_exists( + hass: HomeAssistant, config_entry, setup_enphase_envoy +) -> None: """Test host already exists.""" - - config_entry = MockConfigEntry( - domain=DOMAIN, - data={ - "host": "1.1.1.1", - "name": "Envoy", - "username": "test-username", - "password": "test-password", - }, - title="Envoy", - ) - config_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == "form" assert result["errors"] == {} - with patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.getData", - return_value=True, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, - ) - await hass.async_block_till_done() - + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) assert result2["type"] == "abort" assert result2["reason"] == "already_configured" -async def test_zeroconf_serial_already_exists(hass: HomeAssistant) -> None: +async def test_zeroconf_serial_already_exists( + hass: HomeAssistant, config_entry, setup_enphase_envoy +) -> None: """Test serial number already exists from zeroconf.""" - - config_entry = MockConfigEntry( - domain=DOMAIN, - data={ - "host": "1.1.1.1", - "name": "Envoy", - "username": "test-username", - "password": "test-password", - }, - unique_id="1234", - title="Envoy", - ) - config_entry.add_to_hass(hass) - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, @@ -322,28 +244,16 @@ async def test_zeroconf_serial_already_exists(hass: HomeAssistant) -> None: type="mock_type", ), ) - assert result["type"] == "abort" assert result["reason"] == "already_configured" - assert config_entry.data[CONF_HOST] == "4.4.4.4" + + assert config_entry.data["host"] == "4.4.4.4" -async def test_zeroconf_serial_already_exists_ignores_ipv6(hass: HomeAssistant) -> None: +async def test_zeroconf_serial_already_exists_ignores_ipv6( + hass: HomeAssistant, config_entry, setup_enphase_envoy +) -> None: """Test serial number already exists from zeroconf but the discovery is ipv6.""" - - config_entry = MockConfigEntry( - domain=DOMAIN, - data={ - "host": "1.1.1.1", - "name": "Envoy", - "username": "test-username", - "password": "test-password", - }, - unique_id="1234", - title="Envoy", - ) - config_entry.add_to_hass(hass) - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, @@ -357,71 +267,39 @@ async def test_zeroconf_serial_already_exists_ignores_ipv6(hass: HomeAssistant) type="mock_type", ), ) - assert result["type"] == "abort" assert result["reason"] == "not_ipv4_address" - assert config_entry.data[CONF_HOST] == "1.1.1.1" + + assert config_entry.data["host"] == "1.1.1.1" -async def test_zeroconf_host_already_exists(hass: HomeAssistant) -> None: +@pytest.mark.parametrize("serial_number", [None]) +async def test_zeroconf_host_already_exists( + hass: HomeAssistant, config_entry, setup_enphase_envoy +) -> None: """Test hosts already exists from zeroconf.""" - - config_entry = MockConfigEntry( - domain=DOMAIN, - data={ - "host": "1.1.1.1", - "name": "Envoy", - "username": "test-username", - "password": "test-password", - }, - title="Envoy", + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="1.1.1.1", + addresses=["1.1.1.1"], + hostname="mock_hostname", + name="mock_name", + port=None, + properties={"serialnum": "1234"}, + type="mock_type", + ), ) - config_entry.add_to_hass(hass) - - with patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.getData", - return_value=True, - ), patch( - "homeassistant.components.enphase_envoy.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_ZEROCONF}, - data=zeroconf.ZeroconfServiceInfo( - host="1.1.1.1", - addresses=["1.1.1.1"], - hostname="mock_hostname", - name="mock_name", - port=None, - properties={"serialnum": "1234"}, - type="mock_type", - ), - ) - await hass.async_block_till_done() - assert result["type"] == "abort" assert result["reason"] == "already_configured" assert config_entry.unique_id == "1234" assert config_entry.title == "Envoy 1234" - assert len(mock_setup_entry.mock_calls) == 1 -async def test_reauth(hass: HomeAssistant) -> None: +async def test_reauth(hass: HomeAssistant, config_entry, setup_enphase_envoy) -> None: """Test we reauth auth.""" - - config_entry = MockConfigEntry( - domain=DOMAIN, - data={ - "host": "1.1.1.1", - "name": "Envoy", - "username": "test-username", - "password": "test-password", - }, - title="Envoy", - ) - config_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={ @@ -430,19 +308,13 @@ async def test_reauth(hass: HomeAssistant) -> None: "entry_id": config_entry.entry_id, }, ) - - with patch( - "homeassistant.components.enphase_envoy.config_flow.EnvoyReader.getData", - return_value=True, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, - ) - + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) assert result2["type"] == "abort" assert result2["reason"] == "reauth_successful"