Refactor Onkyo tests to patch underlying pyeiscp library (#132653)
* Refactor Onkyo tests to patch underlying pyeiscp library instead of home assistant methods * limit test patches to specific component, move atches into conftest * use patch.multiple and restrict patches to specific component * use side effect instead of mocking methodpull/133304/head
parent
f069f340a3
commit
2a49378f4c
|
@ -19,6 +19,16 @@ def create_receiver_info(id: int) -> ReceiverInfo:
|
|||
)
|
||||
|
||||
|
||||
def create_connection(id: int) -> Mock:
|
||||
"""Create an mock connection object for testing."""
|
||||
connection = Mock()
|
||||
connection.host = f"host {id}"
|
||||
connection.port = 0
|
||||
connection.name = f"type {id}"
|
||||
connection.identifier = f"id{id}"
|
||||
return connection
|
||||
|
||||
|
||||
def create_config_entry_from_info(info: ReceiverInfo) -> MockConfigEntry:
|
||||
"""Create a config entry from receiver info."""
|
||||
data = {CONF_HOST: info.host}
|
||||
|
|
|
@ -1,25 +1,16 @@
|
|||
"""Configure tests for the Onkyo integration."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, patch
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.onkyo.const import DOMAIN
|
||||
|
||||
from . import create_connection
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
|
||||
|
||||
@pytest.fixture(name="config_entry")
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Create Onkyo entry in Home Assistant."""
|
||||
|
@ -28,3 +19,56 @@ def mock_config_entry() -> MockConfigEntry:
|
|||
title="Onkyo",
|
||||
data={},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def patch_timeouts():
|
||||
"""Patch timeouts to avoid tests waiting."""
|
||||
with patch.multiple(
|
||||
"homeassistant.components.onkyo.receiver",
|
||||
DEVICE_INTERVIEW_TIMEOUT=0,
|
||||
DEVICE_DISCOVERY_TIMEOUT=0,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def default_mock_discovery():
|
||||
"""Mock discovery with a single device."""
|
||||
|
||||
async def mock_discover(host=None, discovery_callback=None, timeout=0):
|
||||
await discovery_callback(create_connection(1))
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.receiver.pyeiscp.Connection.discover",
|
||||
new=mock_discover,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def stub_mock_discovery():
|
||||
"""Mock discovery with no devices."""
|
||||
|
||||
async def mock_discover(host=None, discovery_callback=None, timeout=0):
|
||||
pass
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.receiver.pyeiscp.Connection.discover",
|
||||
new=mock_discover,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def empty_mock_discovery():
|
||||
"""Mock discovery with an empty connection."""
|
||||
|
||||
async def mock_discover(host=None, discovery_callback=None, timeout=0):
|
||||
await discovery_callback(None)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.receiver.pyeiscp.Connection.discover",
|
||||
new=mock_discover,
|
||||
):
|
||||
yield
|
||||
|
|
|
@ -20,12 +20,13 @@ from homeassistant.data_entry_flow import FlowResultType, InvalidData
|
|||
|
||||
from . import (
|
||||
create_config_entry_from_info,
|
||||
create_connection,
|
||||
create_empty_config_entry,
|
||||
create_receiver_info,
|
||||
setup_integration,
|
||||
)
|
||||
|
||||
from tests.common import Mock, MockConfigEntry
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_user_initial_menu(hass: HomeAssistant) -> None:
|
||||
|
@ -40,9 +41,8 @@ async def test_user_initial_menu(hass: HomeAssistant) -> None:
|
|||
assert not set(init_result["menu_options"]) ^ {"manual", "eiscp_discovery"}
|
||||
|
||||
|
||||
async def test_manual_valid_host(hass: HomeAssistant) -> None:
|
||||
async def test_manual_valid_host(hass: HomeAssistant, default_mock_discovery) -> None:
|
||||
"""Test valid host entered."""
|
||||
|
||||
init_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
|
@ -53,30 +53,17 @@ async def test_manual_valid_host(hass: HomeAssistant) -> None:
|
|||
{"next_step_id": "manual"},
|
||||
)
|
||||
|
||||
mock_info = Mock()
|
||||
mock_info.identifier = "mock_id"
|
||||
mock_info.host = "mock_host"
|
||||
mock_info.model_name = "mock_model"
|
||||
select_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "host 1"},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_interview",
|
||||
return_value=mock_info,
|
||||
):
|
||||
select_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
)
|
||||
|
||||
assert select_result["step_id"] == "configure_receiver"
|
||||
assert (
|
||||
select_result["description_placeholders"]["name"]
|
||||
== "mock_model (mock_host)"
|
||||
)
|
||||
assert select_result["step_id"] == "configure_receiver"
|
||||
assert select_result["description_placeholders"]["name"] == "type 1 (host 1)"
|
||||
|
||||
|
||||
async def test_manual_invalid_host(hass: HomeAssistant) -> None:
|
||||
async def test_manual_invalid_host(hass: HomeAssistant, stub_mock_discovery) -> None:
|
||||
"""Test invalid host entered."""
|
||||
|
||||
init_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
|
@ -87,19 +74,18 @@ async def test_manual_invalid_host(hass: HomeAssistant) -> None:
|
|||
{"next_step_id": "manual"},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_interview", return_value=None
|
||||
):
|
||||
host_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
)
|
||||
host_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
)
|
||||
|
||||
assert host_result["step_id"] == "manual"
|
||||
assert host_result["errors"]["base"] == "cannot_connect"
|
||||
|
||||
|
||||
async def test_manual_valid_host_unexpected_error(hass: HomeAssistant) -> None:
|
||||
async def test_manual_valid_host_unexpected_error(
|
||||
hass: HomeAssistant, empty_mock_discovery
|
||||
) -> None:
|
||||
"""Test valid host entered."""
|
||||
|
||||
init_result = await hass.config_entries.flow.async_init(
|
||||
|
@ -112,55 +98,49 @@ async def test_manual_valid_host_unexpected_error(hass: HomeAssistant) -> None:
|
|||
{"next_step_id": "manual"},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_interview",
|
||||
side_effect=Exception(),
|
||||
):
|
||||
host_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
)
|
||||
host_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
)
|
||||
|
||||
assert host_result["step_id"] == "manual"
|
||||
assert host_result["errors"]["base"] == "unknown"
|
||||
|
||||
|
||||
async def test_discovery_and_no_devices_discovered(hass: HomeAssistant) -> None:
|
||||
async def test_discovery_and_no_devices_discovered(
|
||||
hass: HomeAssistant, stub_mock_discovery
|
||||
) -> None:
|
||||
"""Test initial menu."""
|
||||
init_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_discover", return_value=[]
|
||||
):
|
||||
form_result = await hass.config_entries.flow.async_configure(
|
||||
init_result["flow_id"],
|
||||
{"next_step_id": "eiscp_discovery"},
|
||||
)
|
||||
form_result = await hass.config_entries.flow.async_configure(
|
||||
init_result["flow_id"],
|
||||
{"next_step_id": "eiscp_discovery"},
|
||||
)
|
||||
|
||||
assert form_result["type"] is FlowResultType.ABORT
|
||||
assert form_result["reason"] == "no_devices_found"
|
||||
assert form_result["type"] is FlowResultType.ABORT
|
||||
assert form_result["reason"] == "no_devices_found"
|
||||
|
||||
|
||||
async def test_discovery_with_exception(hass: HomeAssistant) -> None:
|
||||
async def test_discovery_with_exception(
|
||||
hass: HomeAssistant, empty_mock_discovery
|
||||
) -> None:
|
||||
"""Test discovery which throws an unexpected exception."""
|
||||
init_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
)
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_discover",
|
||||
side_effect=Exception(),
|
||||
):
|
||||
form_result = await hass.config_entries.flow.async_configure(
|
||||
init_result["flow_id"],
|
||||
{"next_step_id": "eiscp_discovery"},
|
||||
)
|
||||
|
||||
assert form_result["type"] is FlowResultType.ABORT
|
||||
assert form_result["reason"] == "unknown"
|
||||
form_result = await hass.config_entries.flow.async_configure(
|
||||
init_result["flow_id"],
|
||||
{"next_step_id": "eiscp_discovery"},
|
||||
)
|
||||
|
||||
assert form_result["type"] is FlowResultType.ABORT
|
||||
assert form_result["reason"] == "unknown"
|
||||
|
||||
|
||||
async def test_discovery_with_new_and_existing_found(hass: HomeAssistant) -> None:
|
||||
|
@ -170,13 +150,12 @@ async def test_discovery_with_new_and_existing_found(hass: HomeAssistant) -> Non
|
|||
context={"source": SOURCE_USER},
|
||||
)
|
||||
|
||||
infos = [create_receiver_info(1), create_receiver_info(2)]
|
||||
async def mock_discover(discovery_callback, timeout):
|
||||
await discovery_callback(create_connection(1))
|
||||
await discovery_callback(create_connection(2))
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_discover",
|
||||
return_value=infos,
|
||||
),
|
||||
patch("pyeiscp.Connection.discover", new=mock_discover),
|
||||
# Fake it like the first entry was already added
|
||||
patch.object(OnkyoConfigFlow, "_async_current_ids", return_value=["id1"]),
|
||||
):
|
||||
|
@ -185,12 +164,12 @@ async def test_discovery_with_new_and_existing_found(hass: HomeAssistant) -> Non
|
|||
{"next_step_id": "eiscp_discovery"},
|
||||
)
|
||||
|
||||
assert form_result["type"] is FlowResultType.FORM
|
||||
assert form_result["type"] is FlowResultType.FORM
|
||||
|
||||
assert form_result["data_schema"] is not None
|
||||
schema = form_result["data_schema"].schema
|
||||
container = schema["device"].container
|
||||
assert container == {"id2": "type 2 (host 2)"}
|
||||
assert form_result["data_schema"] is not None
|
||||
schema = form_result["data_schema"].schema
|
||||
container = schema["device"].container
|
||||
assert container == {"id2": "type 2 (host 2)"}
|
||||
|
||||
|
||||
async def test_discovery_with_one_selected(hass: HomeAssistant) -> None:
|
||||
|
@ -200,14 +179,11 @@ async def test_discovery_with_one_selected(hass: HomeAssistant) -> None:
|
|||
context={"source": SOURCE_USER},
|
||||
)
|
||||
|
||||
infos = [create_receiver_info(42), create_receiver_info(0)]
|
||||
async def mock_discover(discovery_callback, timeout):
|
||||
await discovery_callback(create_connection(42))
|
||||
await discovery_callback(create_connection(0))
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_discover",
|
||||
return_value=infos,
|
||||
),
|
||||
):
|
||||
with patch("pyeiscp.Connection.discover", new=mock_discover):
|
||||
form_result = await hass.config_entries.flow.async_configure(
|
||||
init_result["flow_id"],
|
||||
{"next_step_id": "eiscp_discovery"},
|
||||
|
@ -218,11 +194,13 @@ async def test_discovery_with_one_selected(hass: HomeAssistant) -> None:
|
|||
user_input={"device": "id42"},
|
||||
)
|
||||
|
||||
assert select_result["step_id"] == "configure_receiver"
|
||||
assert select_result["description_placeholders"]["name"] == "type 42 (host 42)"
|
||||
assert select_result["step_id"] == "configure_receiver"
|
||||
assert select_result["description_placeholders"]["name"] == "type 42 (host 42)"
|
||||
|
||||
|
||||
async def test_configure_empty_source_list(hass: HomeAssistant) -> None:
|
||||
async def test_configure_empty_source_list(
|
||||
hass: HomeAssistant, default_mock_discovery
|
||||
) -> None:
|
||||
"""Test receiver configuration with no sources set."""
|
||||
|
||||
init_result = await hass.config_entries.flow.async_init(
|
||||
|
@ -235,29 +213,22 @@ async def test_configure_empty_source_list(hass: HomeAssistant) -> None:
|
|||
{"next_step_id": "manual"},
|
||||
)
|
||||
|
||||
mock_info = Mock()
|
||||
mock_info.identifier = "mock_id"
|
||||
select_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_interview",
|
||||
return_value=mock_info,
|
||||
):
|
||||
select_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
)
|
||||
configure_result = await hass.config_entries.flow.async_configure(
|
||||
select_result["flow_id"],
|
||||
user_input={"volume_resolution": 200, "input_sources": []},
|
||||
)
|
||||
|
||||
configure_result = await hass.config_entries.flow.async_configure(
|
||||
select_result["flow_id"],
|
||||
user_input={"volume_resolution": 200, "input_sources": []},
|
||||
)
|
||||
|
||||
assert configure_result["errors"] == {
|
||||
"input_sources": "empty_input_source_list"
|
||||
}
|
||||
assert configure_result["errors"] == {"input_sources": "empty_input_source_list"}
|
||||
|
||||
|
||||
async def test_configure_no_resolution(hass: HomeAssistant) -> None:
|
||||
async def test_configure_no_resolution(
|
||||
hass: HomeAssistant, default_mock_discovery
|
||||
) -> None:
|
||||
"""Test receiver configure with no resolution set."""
|
||||
|
||||
init_result = await hass.config_entries.flow.async_init(
|
||||
|
@ -270,26 +241,21 @@ async def test_configure_no_resolution(hass: HomeAssistant) -> None:
|
|||
{"next_step_id": "manual"},
|
||||
)
|
||||
|
||||
mock_info = Mock()
|
||||
mock_info.identifier = "mock_id"
|
||||
select_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_interview",
|
||||
return_value=mock_info,
|
||||
):
|
||||
select_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
with pytest.raises(InvalidData):
|
||||
await hass.config_entries.flow.async_configure(
|
||||
select_result["flow_id"],
|
||||
user_input={"input_sources": ["TV"]},
|
||||
)
|
||||
|
||||
with pytest.raises(InvalidData):
|
||||
await hass.config_entries.flow.async_configure(
|
||||
select_result["flow_id"],
|
||||
user_input={"input_sources": ["TV"]},
|
||||
)
|
||||
|
||||
|
||||
async def test_configure_resolution_set(hass: HomeAssistant) -> None:
|
||||
async def test_configure_resolution_set(
|
||||
hass: HomeAssistant, default_mock_discovery
|
||||
) -> None:
|
||||
"""Test receiver configure with specified resolution."""
|
||||
|
||||
init_result = await hass.config_entries.flow.async_init(
|
||||
|
@ -302,16 +268,10 @@ async def test_configure_resolution_set(hass: HomeAssistant) -> None:
|
|||
{"next_step_id": "manual"},
|
||||
)
|
||||
|
||||
receiver_info = create_receiver_info(1)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_interview",
|
||||
return_value=receiver_info,
|
||||
):
|
||||
select_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
)
|
||||
select_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
)
|
||||
|
||||
configure_result = await hass.config_entries.flow.async_configure(
|
||||
select_result["flow_id"],
|
||||
|
@ -322,7 +282,9 @@ async def test_configure_resolution_set(hass: HomeAssistant) -> None:
|
|||
assert configure_result["options"]["volume_resolution"] == 200
|
||||
|
||||
|
||||
async def test_configure_invalid_resolution_set(hass: HomeAssistant) -> None:
|
||||
async def test_configure_invalid_resolution_set(
|
||||
hass: HomeAssistant, default_mock_discovery
|
||||
) -> None:
|
||||
"""Test receiver configure with invalid resolution."""
|
||||
|
||||
init_result = await hass.config_entries.flow.async_init(
|
||||
|
@ -335,26 +297,19 @@ async def test_configure_invalid_resolution_set(hass: HomeAssistant) -> None:
|
|||
{"next_step_id": "manual"},
|
||||
)
|
||||
|
||||
mock_info = Mock()
|
||||
mock_info.identifier = "mock_id"
|
||||
select_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_interview",
|
||||
return_value=mock_info,
|
||||
):
|
||||
select_result = await hass.config_entries.flow.async_configure(
|
||||
form_result["flow_id"],
|
||||
user_input={CONF_HOST: "sample-host-name"},
|
||||
with pytest.raises(InvalidData):
|
||||
await hass.config_entries.flow.async_configure(
|
||||
select_result["flow_id"],
|
||||
user_input={"volume_resolution": 42, "input_sources": ["TV"]},
|
||||
)
|
||||
|
||||
with pytest.raises(InvalidData):
|
||||
await hass.config_entries.flow.async_configure(
|
||||
select_result["flow_id"],
|
||||
user_input={"volume_resolution": 42, "input_sources": ["TV"]},
|
||||
)
|
||||
|
||||
|
||||
async def test_reconfigure(hass: HomeAssistant) -> None:
|
||||
async def test_reconfigure(hass: HomeAssistant, default_mock_discovery) -> None:
|
||||
"""Test the reconfigure config flow."""
|
||||
receiver_info = create_receiver_info(1)
|
||||
config_entry = create_config_entry_from_info(receiver_info)
|
||||
|
@ -368,14 +323,10 @@ async def test_reconfigure(hass: HomeAssistant) -> None:
|
|||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "manual"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_interview",
|
||||
return_value=receiver_info,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"host": receiver_info.host}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"host": receiver_info.host}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["step_id"] == "configure_receiver"
|
||||
|
@ -403,14 +354,18 @@ async def test_reconfigure_new_device(hass: HomeAssistant) -> None:
|
|||
|
||||
result = await config_entry.start_reconfigure_flow(hass)
|
||||
|
||||
receiver_info_2 = create_receiver_info(2)
|
||||
mock_connection = create_connection(2)
|
||||
|
||||
# Create mock discover that calls callback immediately
|
||||
async def mock_discover(host, discovery_callback, timeout):
|
||||
await discovery_callback(mock_connection)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_interview",
|
||||
return_value=receiver_info_2,
|
||||
"homeassistant.components.onkyo.receiver.pyeiscp.Connection.discover",
|
||||
new=mock_discover,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"host": receiver_info_2.host}
|
||||
result["flow_id"], user_input={"host": mock_connection.host}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -455,12 +410,10 @@ async def test_import_fail(
|
|||
error: str,
|
||||
) -> None:
|
||||
"""Test import flow failed."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.onkyo.config_flow.async_interview",
|
||||
return_value=None,
|
||||
side_effect=exception,
|
||||
),
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onkyo.receiver.pyeiscp.Connection.discover",
|
||||
side_effect=exception,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=user_input
|
||||
|
|
Loading…
Reference in New Issue