mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Improve coordinator test coverage for enphase_envoy (#122375)
* Improve coordinator test coverage for enphase_envoy * rename to test_coordinator to test_init for enphase_envoy * Mock pyenphase _obtain_token instead of httpx auth requests in enphase_envoy tests. * Move EnvoyTokenAuth patch to mock_envoy of enphase_envoy
This commit is contained in:
parent
09989e6184
commit
ea7b2ecec0
@ -69,6 +69,11 @@ async def mock_envoy(
|
|||||||
request: pytest.FixtureRequest,
|
request: pytest.FixtureRequest,
|
||||||
) -> AsyncGenerator[AsyncMock]:
|
) -> AsyncGenerator[AsyncMock]:
|
||||||
"""Define a mocked Envoy fixture."""
|
"""Define a mocked Envoy fixture."""
|
||||||
|
new_token = jwt.encode(
|
||||||
|
payload={"name": "envoy", "exp": 2007837780},
|
||||||
|
key="secret",
|
||||||
|
algorithm="HS256",
|
||||||
|
)
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.enphase_envoy.config_flow.Envoy",
|
"homeassistant.components.enphase_envoy.config_flow.Envoy",
|
||||||
@ -78,6 +83,10 @@ async def mock_envoy(
|
|||||||
"homeassistant.components.enphase_envoy.Envoy",
|
"homeassistant.components.enphase_envoy.Envoy",
|
||||||
new=mock_client,
|
new=mock_client,
|
||||||
),
|
),
|
||||||
|
patch(
|
||||||
|
"pyenphase.auth.EnvoyTokenAuth._obtain_token",
|
||||||
|
return_value=new_token,
|
||||||
|
),
|
||||||
):
|
):
|
||||||
mock_envoy = mock_client.return_value
|
mock_envoy = mock_client.return_value
|
||||||
# Add the fixtures specified
|
# Add the fixtures specified
|
||||||
|
221
tests/components/enphase_envoy/test_init.py
Normal file
221
tests/components/enphase_envoy/test_init.py
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
"""Test Enphase Envoy runtime."""
|
||||||
|
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
|
from jwt import encode
|
||||||
|
from pyenphase import EnvoyAuthenticationError, EnvoyError, EnvoyTokenAuth
|
||||||
|
from pyenphase.auth import EnvoyLegacyAuth
|
||||||
|
import pytest
|
||||||
|
import respx
|
||||||
|
|
||||||
|
from homeassistant.components.enphase_envoy import DOMAIN
|
||||||
|
from homeassistant.components.enphase_envoy.const import Platform
|
||||||
|
from homeassistant.components.enphase_envoy.coordinator import SCAN_INTERVAL
|
||||||
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_HOST,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_TOKEN,
|
||||||
|
CONF_USERNAME,
|
||||||
|
STATE_UNAVAILABLE,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from . import setup_integration
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
|
||||||
|
|
||||||
|
async def test_with_pre_v7_firmware(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_envoy: AsyncMock,
|
||||||
|
config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test enphase_envoy coordinator with pre V7 firmware."""
|
||||||
|
mock_envoy.firmware = "5.1.1"
|
||||||
|
mock_envoy.auth = EnvoyLegacyAuth(
|
||||||
|
"127.0.0.1", username="test-username", password="test-password"
|
||||||
|
)
|
||||||
|
await setup_integration(hass, config_entry)
|
||||||
|
|
||||||
|
assert config_entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||||
|
assert entity_state.state == "1"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.freeze_time("2024-07-23 00:00:00+00:00")
|
||||||
|
async def test_token_in_config_file(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_envoy: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test coordinator with token provided from config."""
|
||||||
|
token = encode(
|
||||||
|
payload={"name": "envoy", "exp": 1907837780},
|
||||||
|
key="secret",
|
||||||
|
algorithm="HS256",
|
||||||
|
)
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
entry_id="45a36e55aaddb2007c5f6602e0c38e72",
|
||||||
|
title="Envoy 1234",
|
||||||
|
unique_id="1234",
|
||||||
|
data={
|
||||||
|
CONF_HOST: "1.1.1.1",
|
||||||
|
CONF_NAME: "Envoy 1234",
|
||||||
|
CONF_USERNAME: "test-username",
|
||||||
|
CONF_PASSWORD: "test-password",
|
||||||
|
CONF_TOKEN: token,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
mock_envoy.auth = EnvoyTokenAuth("127.0.0.1", token=token, envoy_serial="1234")
|
||||||
|
await setup_integration(hass, entry)
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||||
|
assert entity_state.state == "1"
|
||||||
|
|
||||||
|
|
||||||
|
@respx.mock
|
||||||
|
@pytest.mark.freeze_time("2024-07-23 00:00:00+00:00")
|
||||||
|
async def test_expired_token_in_config(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_envoy: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test coordinator with expired token provided from config."""
|
||||||
|
current_token = encode(
|
||||||
|
# some time in 2021
|
||||||
|
payload={"name": "envoy", "exp": 1627314600},
|
||||||
|
key="secret",
|
||||||
|
algorithm="HS256",
|
||||||
|
)
|
||||||
|
|
||||||
|
# mock envoy with expired token in config
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
entry_id="45a36e55aaddb2007c5f6602e0c38e72",
|
||||||
|
title="Envoy 1234",
|
||||||
|
unique_id="1234",
|
||||||
|
data={
|
||||||
|
CONF_HOST: "1.1.1.1",
|
||||||
|
CONF_NAME: "Envoy 1234",
|
||||||
|
CONF_USERNAME: "test-username",
|
||||||
|
CONF_PASSWORD: "test-password",
|
||||||
|
CONF_TOKEN: current_token,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
# Make sure to mock pyenphase.auth.EnvoyTokenAuth._obtain_token
|
||||||
|
# when specifying username and password in EnvoyTokenauth
|
||||||
|
mock_envoy.auth = EnvoyTokenAuth(
|
||||||
|
"127.0.0.1",
|
||||||
|
token=current_token,
|
||||||
|
envoy_serial="1234",
|
||||||
|
cloud_username="test_username",
|
||||||
|
cloud_password="test_password",
|
||||||
|
)
|
||||||
|
await setup_integration(hass, entry)
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||||
|
assert entity_state.state == "1"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_coordinator_update_error(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_envoy: AsyncMock,
|
||||||
|
config_entry: MockConfigEntry,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test coordinator update error handling."""
|
||||||
|
await setup_integration(hass, config_entry)
|
||||||
|
|
||||||
|
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||||
|
original_state = entity_state
|
||||||
|
|
||||||
|
# force HA to detect changed data by changing raw
|
||||||
|
mock_envoy.data.raw = {"I": "am changed 1"}
|
||||||
|
mock_envoy.update.side_effect = EnvoyError
|
||||||
|
|
||||||
|
# Move time to next update
|
||||||
|
freezer.tick(SCAN_INTERVAL)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
|
||||||
|
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||||
|
assert entity_state.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
mock_envoy.reset_mock(return_value=True, side_effect=True)
|
||||||
|
|
||||||
|
mock_envoy.data.raw = {"I": "am changed 2"}
|
||||||
|
|
||||||
|
# Move time to next update
|
||||||
|
freezer.tick(SCAN_INTERVAL)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
|
||||||
|
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||||
|
assert entity_state.state == original_state.state
|
||||||
|
|
||||||
|
|
||||||
|
async def test_coordinator_update_authentication_error(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_envoy: AsyncMock,
|
||||||
|
config_entry: MockConfigEntry,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test enphase_envoy coordinator update authentication error handling."""
|
||||||
|
with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SENSOR]):
|
||||||
|
await setup_integration(hass, config_entry)
|
||||||
|
|
||||||
|
# force HA to detect changed data by changing raw
|
||||||
|
mock_envoy.data.raw = {"I": "am changed 1"}
|
||||||
|
mock_envoy.update.side_effect = EnvoyAuthenticationError("This must fail")
|
||||||
|
|
||||||
|
# Move time to next update
|
||||||
|
freezer.tick(SCAN_INTERVAL)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
|
||||||
|
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||||
|
assert entity_state.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.freeze_time("2024-07-23 00:00:00+00:00")
|
||||||
|
async def test_coordinator_token_refresh_error(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_envoy: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test coordinator with token provided from config."""
|
||||||
|
# 63, 69-79 _async_try_refresh_token
|
||||||
|
token = encode(
|
||||||
|
# some time in 2021
|
||||||
|
payload={"name": "envoy", "exp": 1627314600},
|
||||||
|
key="secret",
|
||||||
|
algorithm="HS256",
|
||||||
|
)
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
entry_id="45a36e55aaddb2007c5f6602e0c38e72",
|
||||||
|
title="Envoy 1234",
|
||||||
|
unique_id="1234",
|
||||||
|
data={
|
||||||
|
CONF_HOST: "1.1.1.1",
|
||||||
|
CONF_NAME: "Envoy 1234",
|
||||||
|
CONF_USERNAME: "test-username",
|
||||||
|
CONF_PASSWORD: "test-password",
|
||||||
|
CONF_TOKEN: token,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
# token refresh without username and password specified in
|
||||||
|
# EnvoyTokenAuthwill force token refresh error
|
||||||
|
mock_envoy.auth = EnvoyTokenAuth("127.0.0.1", token=token, envoy_serial="1234")
|
||||||
|
await setup_integration(hass, entry)
|
||||||
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
assert (entity_state := hass.states.get("sensor.inverter_1"))
|
||||||
|
assert entity_state.state == "1"
|
Loading…
x
Reference in New Issue
Block a user