Update opentherm_gw tests to avoid patching internals (#125152)

* Update tests to avoid patching internals

* * Use fixtures for tests
* Update variable names in tests for clarity

* Use hass.config_entries.async_setup instead of setup.async_setup_component
pull/125162/head^2
mvn23 2024-09-03 19:19:43 +02:00 committed by GitHub
parent 8f26cff65a
commit 8e03f3a045
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 157 additions and 195 deletions

View File

@ -0,0 +1,41 @@
"""Test configuration for opentherm_gw."""
from collections.abc import Generator
from unittest.mock import AsyncMock, MagicMock, patch
from pyotgw.vars import OTGW, OTGW_ABOUT
import pytest
VERSION_TEST = "4.2.5"
MINIMAL_STATUS = {OTGW: {OTGW_ABOUT: f"OpenTherm Gateway {VERSION_TEST}"}}
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock]:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.opentherm_gw.async_setup_entry",
return_value=True,
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture
def mock_pyotgw() -> Generator[MagicMock]:
"""Mock a pyotgw.OpenThermGateway object."""
with (
patch(
"homeassistant.components.opentherm_gw.OpenThermGateway",
return_value=MagicMock(
connect=AsyncMock(return_value=MINIMAL_STATUS),
set_control_setpoint=AsyncMock(),
set_max_relative_mod=AsyncMock(),
disconnect=AsyncMock(),
),
) as mock_gateway,
patch(
"homeassistant.components.opentherm_gw.config_flow.pyotgw.OpenThermGateway",
new=mock_gateway,
),
):
yield mock_gateway

View File

@ -1,8 +1,7 @@
"""Test the Opentherm Gateway config flow."""
from unittest.mock import patch
from unittest.mock import AsyncMock, MagicMock
from pyotgw.vars import OTGW, OTGW_ABOUT
from serial import SerialException
from homeassistant import config_entries
@ -25,10 +24,12 @@ from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry
MINIMAL_STATUS = {OTGW: {OTGW_ABOUT: "OpenTherm Gateway 4.2.5"}}
async def test_form_user(hass: HomeAssistant) -> None:
async def test_form_user(
hass: HomeAssistant,
mock_pyotgw: MagicMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test we get the form."""
result = await hass.config_entries.flow.async_init(
@ -37,27 +38,10 @@ async def test_form_user(hass: HomeAssistant) -> None:
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
with (
patch(
"homeassistant.components.opentherm_gw.async_setup",
return_value=True,
) as mock_setup,
patch(
"homeassistant.components.opentherm_gw.async_setup_entry",
return_value=True,
) as mock_setup_entry,
patch(
"pyotgw.OpenThermGateway.connect", return_value=MINIMAL_STATUS
) as mock_pyotgw_connect,
patch(
"pyotgw.OpenThermGateway.disconnect", return_value=None
) as mock_pyotgw_disconnect,
patch("pyotgw.status.StatusManager._process_updates", return_value=None),
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_NAME: "Test Entry 1", CONF_DEVICE: "/dev/ttyUSB0"}
)
await hass.async_block_till_done()
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_NAME: "Test Entry 1", CONF_DEVICE: "/dev/ttyUSB0"}
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == "Test Entry 1"
@ -66,37 +50,21 @@ async def test_form_user(hass: HomeAssistant) -> None:
CONF_DEVICE: "/dev/ttyUSB0",
CONF_ID: "test_entry_1",
}
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
assert len(mock_pyotgw_connect.mock_calls) == 1
assert len(mock_pyotgw_disconnect.mock_calls) == 1
assert mock_pyotgw.return_value.connect.await_count == 1
assert mock_pyotgw.return_value.disconnect.await_count == 1
async def test_form_import(hass: HomeAssistant) -> None:
async def test_form_import(
hass: HomeAssistant,
mock_pyotgw: MagicMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test import from existing config."""
with (
patch(
"homeassistant.components.opentherm_gw.async_setup",
return_value=True,
) as mock_setup,
patch(
"homeassistant.components.opentherm_gw.async_setup_entry",
return_value=True,
) as mock_setup_entry,
patch(
"pyotgw.OpenThermGateway.connect", return_value=MINIMAL_STATUS
) as mock_pyotgw_connect,
patch(
"pyotgw.OpenThermGateway.disconnect", return_value=None
) as mock_pyotgw_disconnect,
patch("pyotgw.status.StatusManager._process_updates", return_value=None),
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={CONF_ID: "legacy_gateway", CONF_DEVICE: "/dev/ttyUSB1"},
)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={CONF_ID: "legacy_gateway", CONF_DEVICE: "/dev/ttyUSB1"},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "legacy_gateway"
@ -105,13 +73,15 @@ async def test_form_import(hass: HomeAssistant) -> None:
CONF_DEVICE: "/dev/ttyUSB1",
CONF_ID: "legacy_gateway",
}
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
assert len(mock_pyotgw_connect.mock_calls) == 1
assert len(mock_pyotgw_disconnect.mock_calls) == 1
assert mock_pyotgw.return_value.connect.await_count == 1
assert mock_pyotgw.return_value.disconnect.await_count == 1
async def test_form_duplicate_entries(hass: HomeAssistant) -> None:
async def test_form_duplicate_entries(
hass: HomeAssistant,
mock_pyotgw: MagicMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test duplicate device or id errors."""
flow1 = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -123,87 +93,76 @@ async def test_form_duplicate_entries(hass: HomeAssistant) -> None:
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with (
patch(
"homeassistant.components.opentherm_gw.async_setup",
return_value=True,
) as mock_setup,
patch(
"homeassistant.components.opentherm_gw.async_setup_entry",
return_value=True,
) as mock_setup_entry,
patch(
"pyotgw.OpenThermGateway.connect", return_value=MINIMAL_STATUS
) as mock_pyotgw_connect,
patch(
"pyotgw.OpenThermGateway.disconnect", return_value=None
) as mock_pyotgw_disconnect,
patch("pyotgw.status.StatusManager._process_updates", return_value=None),
):
result1 = await hass.config_entries.flow.async_configure(
flow1["flow_id"], {CONF_NAME: "Test Entry 1", CONF_DEVICE: "/dev/ttyUSB0"}
)
result2 = await hass.config_entries.flow.async_configure(
flow2["flow_id"], {CONF_NAME: "Test Entry 1", CONF_DEVICE: "/dev/ttyUSB1"}
)
result3 = await hass.config_entries.flow.async_configure(
flow3["flow_id"], {CONF_NAME: "Test Entry 2", CONF_DEVICE: "/dev/ttyUSB0"}
)
result1 = await hass.config_entries.flow.async_configure(
flow1["flow_id"], {CONF_NAME: "Test Entry 1", CONF_DEVICE: "/dev/ttyUSB0"}
)
assert result1["type"] is FlowResultType.CREATE_ENTRY
result2 = await hass.config_entries.flow.async_configure(
flow2["flow_id"], {CONF_NAME: "Test Entry 1", CONF_DEVICE: "/dev/ttyUSB1"}
)
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "id_exists"}
result3 = await hass.config_entries.flow.async_configure(
flow3["flow_id"], {CONF_NAME: "Test Entry 2", CONF_DEVICE: "/dev/ttyUSB0"}
)
assert result3["type"] is FlowResultType.FORM
assert result3["errors"] == {"base": "already_configured"}
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
assert len(mock_pyotgw_connect.mock_calls) == 1
assert len(mock_pyotgw_disconnect.mock_calls) == 1
assert mock_pyotgw.return_value.connect.await_count == 1
assert mock_pyotgw.return_value.disconnect.await_count == 1
async def test_form_connection_timeout(hass: HomeAssistant) -> None:
async def test_form_connection_timeout(
hass: HomeAssistant,
mock_pyotgw: MagicMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test we handle connection timeout."""
result = await hass.config_entries.flow.async_init(
flow = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with (
patch(
"pyotgw.OpenThermGateway.connect", side_effect=(TimeoutError)
) as mock_connect,
patch("pyotgw.status.StatusManager._process_updates", return_value=None),
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_NAME: "Test Entry 1", CONF_DEVICE: "socket://192.0.2.254:1234"},
)
mock_pyotgw.return_value.connect.side_effect = TimeoutError
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "timeout_connect"}
assert len(mock_connect.mock_calls) == 1
result = await hass.config_entries.flow.async_configure(
flow["flow_id"],
{CONF_NAME: "Test Entry 1", CONF_DEVICE: "socket://192.0.2.254:1234"},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "timeout_connect"}
assert mock_pyotgw.return_value.connect.await_count == 1
async def test_form_connection_error(hass: HomeAssistant) -> None:
async def test_form_connection_error(
hass: HomeAssistant,
mock_pyotgw: MagicMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test we handle serial connection error."""
result = await hass.config_entries.flow.async_init(
flow = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with (
patch(
"pyotgw.OpenThermGateway.connect", side_effect=(SerialException)
) as mock_connect,
patch("pyotgw.status.StatusManager._process_updates", return_value=None),
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_NAME: "Test Entry 1", CONF_DEVICE: "/dev/ttyUSB0"}
)
mock_pyotgw.return_value.connect.side_effect = SerialException
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "cannot_connect"}
assert len(mock_connect.mock_calls) == 1
result = await hass.config_entries.flow.async_configure(
flow["flow_id"], {CONF_NAME: "Test Entry 1", CONF_DEVICE: "/dev/ttyUSB0"}
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "cannot_connect"}
assert mock_pyotgw.return_value.connect.await_count == 1
async def test_options_form(hass: HomeAssistant) -> None:
async def test_options_form(
hass: HomeAssistant,
mock_pyotgw: MagicMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test the options form."""
entry = MockConfigEntry(
domain=DOMAIN,
@ -217,23 +176,17 @@ async def test_options_form(hass: HomeAssistant) -> None:
)
entry.add_to_hass(hass)
with (
patch("homeassistant.components.opentherm_gw.async_setup", return_value=True),
patch(
"homeassistant.components.opentherm_gw.async_setup_entry", return_value=True
),
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.options.async_init(
flow = await hass.config_entries.options.async_init(
entry.entry_id, context={"source": "test"}, data=None
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
assert flow["type"] is FlowResultType.FORM
assert flow["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
flow["flow_id"],
user_input={
CONF_FLOOR_TEMP: True,
CONF_READ_PRECISION: PRECISION_HALVES,
@ -248,12 +201,12 @@ async def test_options_form(hass: HomeAssistant) -> None:
assert result["data"][CONF_TEMPORARY_OVRD_MODE] is True
assert result["data"][CONF_FLOOR_TEMP] is True
result = await hass.config_entries.options.async_init(
flow = await hass.config_entries.options.async_init(
entry.entry_id, context={"source": "test"}, data=None
)
result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={CONF_READ_PRECISION: 0}
flow["flow_id"], user_input={CONF_READ_PRECISION: 0}
)
assert result["type"] is FlowResultType.CREATE_ENTRY
@ -262,12 +215,12 @@ async def test_options_form(hass: HomeAssistant) -> None:
assert result["data"][CONF_TEMPORARY_OVRD_MODE] is True
assert result["data"][CONF_FLOOR_TEMP] is True
result = await hass.config_entries.options.async_init(
flow = await hass.config_entries.options.async_init(
entry.entry_id, context={"source": "test"}, data=None
)
result = await hass.config_entries.options.async_configure(
result["flow_id"],
flow["flow_id"],
user_input={
CONF_FLOOR_TEMP: False,
CONF_READ_PRECISION: PRECISION_TENTHS,

View File

@ -1,11 +1,9 @@
"""Test Opentherm Gateway init."""
from unittest.mock import AsyncMock, MagicMock, patch
from unittest.mock import MagicMock
from pyotgw.vars import OTGW, OTGW_ABOUT
import pytest
from homeassistant import setup
from homeassistant.components.opentherm_gw.const import (
DOMAIN,
OpenThermDeviceIdentifier,
@ -14,11 +12,11 @@ from homeassistant.const import CONF_DEVICE, CONF_ID, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from .conftest import VERSION_TEST
from tests.common import MockConfigEntry
VERSION_OLD = "4.2.5"
VERSION_NEW = "4.2.8.1"
MINIMAL_STATUS = {OTGW: {OTGW_ABOUT: f"OpenTherm Gateway {VERSION_OLD}"}}
MINIMAL_STATUS_UPD = {OTGW: {OTGW_ABOUT: f"OpenTherm Gateway {VERSION_NEW}"}}
MOCK_GATEWAY_ID = "mock_gateway"
MOCK_CONFIG_ENTRY = MockConfigEntry(
@ -33,35 +31,28 @@ MOCK_CONFIG_ENTRY = MockConfigEntry(
)
# This tests needs to be adjusted to remove lingering tasks
@pytest.mark.parametrize("expected_lingering_tasks", [True])
async def test_device_registry_insert(
hass: HomeAssistant, device_registry: dr.DeviceRegistry
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
mock_pyotgw: MagicMock,
) -> None:
"""Test that the device registry is initialized correctly."""
MOCK_CONFIG_ENTRY.add_to_hass(hass)
with (
patch(
"homeassistant.components.opentherm_gw.OpenThermGatewayHub.cleanup",
return_value=None,
),
patch("pyotgw.OpenThermGateway.connect", return_value=MINIMAL_STATUS),
):
await setup.async_setup_component(hass, DOMAIN, {})
await hass.config_entries.async_setup(MOCK_CONFIG_ENTRY.entry_id)
await hass.async_block_till_done()
gw_dev = device_registry.async_get_device(
identifiers={(DOMAIN, f"{MOCK_GATEWAY_ID}-{OpenThermDeviceIdentifier.GATEWAY}")}
)
assert gw_dev.sw_version == VERSION_OLD
assert gw_dev is not None
assert gw_dev.sw_version == VERSION_TEST
# This tests needs to be adjusted to remove lingering tasks
@pytest.mark.parametrize("expected_lingering_tasks", [True])
async def test_device_registry_update(
hass: HomeAssistant, device_registry: dr.DeviceRegistry
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
mock_pyotgw: MagicMock,
) -> None:
"""Test that the device registry is updated correctly."""
MOCK_CONFIG_ENTRY.add_to_hass(hass)
@ -74,19 +65,14 @@ async def test_device_registry_update(
name="Mock Gateway",
manufacturer="Schelte Bron",
model="OpenTherm Gateway",
sw_version=VERSION_OLD,
sw_version=VERSION_TEST,
)
with (
patch(
"homeassistant.components.opentherm_gw.OpenThermGatewayHub.cleanup",
return_value=None,
),
patch("pyotgw.OpenThermGateway.connect", return_value=MINIMAL_STATUS_UPD),
):
await setup.async_setup_component(hass, DOMAIN, {})
mock_pyotgw.return_value.connect.return_value = MINIMAL_STATUS_UPD
await hass.config_entries.async_setup(MOCK_CONFIG_ENTRY.entry_id)
await hass.async_block_till_done()
gw_dev = device_registry.async_get_device(
identifiers={(DOMAIN, f"{MOCK_GATEWAY_ID}-{OpenThermDeviceIdentifier.GATEWAY}")}
)
@ -96,7 +82,9 @@ async def test_device_registry_update(
# Device migration test can be removed in 2025.4.0
async def test_device_migration(
hass: HomeAssistant, device_registry: dr.DeviceRegistry
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
mock_pyotgw: MagicMock,
) -> None:
"""Test that the device registry is updated correctly."""
MOCK_CONFIG_ENTRY.add_to_hass(hass)
@ -109,22 +97,10 @@ async def test_device_migration(
name="Mock Gateway",
manufacturer="Schelte Bron",
model="OpenTherm Gateway",
sw_version=VERSION_OLD,
sw_version=VERSION_TEST,
)
with (
patch(
"homeassistant.components.opentherm_gw.OpenThermGateway",
return_value=MagicMock(
connect=AsyncMock(return_value=MINIMAL_STATUS_UPD),
set_control_setpoint=AsyncMock(),
set_max_relative_mod=AsyncMock(),
disconnect=AsyncMock(),
),
),
):
await setup.async_setup_component(hass, DOMAIN, {})
await hass.config_entries.async_setup(MOCK_CONFIG_ENTRY.entry_id)
await hass.async_block_till_done()
assert (
@ -158,7 +134,9 @@ async def test_device_migration(
# Entity migration test can be removed in 2025.4.0
async def test_climate_entity_migration(
hass: HomeAssistant, entity_registry: er.EntityRegistry
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
mock_pyotgw: MagicMock,
) -> None:
"""Test that the climate entity unique_id gets migrated correctly."""
MOCK_CONFIG_ENTRY.add_to_hass(hass)
@ -168,22 +146,12 @@ async def test_climate_entity_migration(
unique_id=MOCK_CONFIG_ENTRY.data[CONF_ID],
)
with (
patch(
"homeassistant.components.opentherm_gw.OpenThermGateway",
return_value=MagicMock(
connect=AsyncMock(return_value=MINIMAL_STATUS_UPD),
set_control_setpoint=AsyncMock(),
set_max_relative_mod=AsyncMock(),
disconnect=AsyncMock(),
),
),
):
await setup.async_setup_component(hass, DOMAIN, {})
await hass.config_entries.async_setup(MOCK_CONFIG_ENTRY.entry_id)
await hass.async_block_till_done()
updated_entry = entity_registry.async_get(entry.entity_id)
assert updated_entry is not None
assert (
entity_registry.async_get(entry.entity_id).unique_id
updated_entry.unique_id
== f"{MOCK_CONFIG_ENTRY.data[CONF_ID]}-{OpenThermDeviceIdentifier.THERMOSTAT}-thermostat_entity"
)