core/tests/components/apcupsd/test_config_flow.py

186 lines
6.8 KiB
Python
Raw Normal View History

Refactor apcupsd to use config flow (#64809) * Add Config Flow to APCUPSd integration and remove YAML support. * Hide the binary sensor if user does not select STATFLAG resource. * Add tests for config flows. * Simplify config flow code. * Spell fix. * Fix pylint warnings. * Simplify the code for config flow. * First attempt to implement import flows to suppport legacy YAML configurations. * Remove unnecessary log calls. * Wrap synchronous update call with `hass.async_add_executor_job`. * Import the YAML configurations when sensor platform is set up. * Move the logger call since the variables are not properly set up. * Add codeowner. * Fix name field of manifest.json. * Fix linting issue. * Fix incorrect dependency due to incorrect rebase. * Update codeowner and config flows via hassfest. * Postpone the deprecation warning to 2022.7. * Import future annotations for init file. * Add an newline at the end to make prettier happy. * Update github id. * Add type hints for return types of steps in config flow. * Move the deprecation date for YAML config to 2022.12. * Update according to reviews. * Use async_forward_entry_setups. * Add helper properties to `APCUPSdData` class. * Add device_info for binary sensor. * Simplify config flow. * Remove options flow strings. * update the tests according to the changes. * Add `entity_registry_enabled_default` to entities and use imported CONF_RESOURCES to disable entities instead of skipping them. * Update according to reviews. * Do not use model of the UPS as the title for the integration. Instead, simply use "APCUPSd" as the integration title and let the device info serve as title for each device instead. * Change schema to be a global variable. * Add more comments. * Rewrite the tests for config flows. * Fix enabled_by_default. * Show friendly titles in the integration. * Add import check in `async_setup_platform` to avoid importing in sensor platform setup. * Add import check in `async_setup_platform` to avoid importing in sensor platform setup. * Update comments in test files. * Use parametrize instead of manually iterating different test cases. * Swap the order of the platform constants. * Avoid using broad exceptions. * Set up device info via `_attr_device_info`. * Remove unrelated test in `test_config_flow`. * Use `DeviceInfo` instead of dict to assign to `_attr_device_info`. * Add english translation. * Add `async_create_issue` for deprecated YAML configuration. * Enable UPS status by default since it could show "online, charging, on battery etc" which is meaningful for all users. * Apply suggestions from code review * Apply suggestion * Apply suggestion Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2022-09-28 07:14:04 +00:00
"""Test APCUPSd config flow setup process."""
from copy import copy
from unittest.mock import patch
import pytest
from homeassistant.components.apcupsd import DOMAIN
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_RESOURCES, CONF_SOURCE
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from . import CONF_DATA, MOCK_MINIMAL_STATUS, MOCK_STATUS
from tests.common import MockConfigEntry
def _patch_setup():
return patch(
"homeassistant.components.apcupsd.async_setup_entry",
return_value=True,
)
async def test_config_flow_cannot_connect(hass: HomeAssistant) -> None:
"""Test config flow setup with connection error."""
with patch("apcaccess.status.get") as mock_get:
mock_get.side_effect = OSError()
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
data=CONF_DATA,
)
assert result["type"] == FlowResultType.FORM
assert result["errors"]["base"] == "cannot_connect"
async def test_config_flow_no_status(hass: HomeAssistant) -> None:
"""Test config flow setup with successful connection but no status is reported."""
with patch(
"apcaccess.status.parse",
return_value={}, # Returns no status.
), patch("apcaccess.status.get", return_value=b""):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "no_status"
async def test_config_flow_duplicate(hass: HomeAssistant) -> None:
"""Test duplicate config flow setup."""
# First add an exiting config entry to hass.
mock_entry = MockConfigEntry(
version=1,
domain=DOMAIN,
title="APCUPSd",
data=CONF_DATA,
unique_id=MOCK_STATUS["SERIALNO"],
source=SOURCE_USER,
)
mock_entry.add_to_hass(hass)
with patch("apcaccess.status.parse") as mock_parse, patch(
"apcaccess.status.get", return_value=b""
), _patch_setup():
mock_parse.return_value = MOCK_STATUS
# Now, create the integration again using the same config data, we should reject
# the creation due same host / port.
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
data=CONF_DATA,
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "already_configured"
# Then, we create the integration once again using a different port. However,
# the apcaccess patch is kept to report the same serial number, we should
# reject the creation as well.
another_host = {
CONF_HOST: CONF_DATA[CONF_HOST],
CONF_PORT: CONF_DATA[CONF_PORT] + 1,
}
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
data=another_host,
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "already_configured"
# Now we change the serial number and add it again. This should be successful.
another_device_status = copy(MOCK_STATUS)
another_device_status["SERIALNO"] = MOCK_STATUS["SERIALNO"] + "ZZZ"
mock_parse.return_value = another_device_status
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
data=another_host,
)
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["data"] == another_host
async def test_flow_works(hass: HomeAssistant) -> None:
"""Test successful creation of config entries via user configuration."""
with patch("apcaccess.status.parse", return_value=MOCK_STATUS), patch(
"apcaccess.status.get", return_value=b""
), _patch_setup() as mock_setup:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={CONF_SOURCE: SOURCE_USER},
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == SOURCE_USER
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=CONF_DATA
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == MOCK_STATUS["MODEL"]
assert result["data"] == CONF_DATA
mock_setup.assert_called_once()
@pytest.mark.parametrize(
"extra_status,expected_title",
[
({"UPSNAME": "Friendly Name"}, "Friendly Name"),
({"MODEL": "MODEL X"}, "MODEL X"),
({"SERIALNO": "ZZZZ"}, "ZZZZ"),
({}, "APC UPS"),
],
)
async def test_flow_minimal_status(
hass: HomeAssistant, extra_status: dict[str, str], expected_title: str
) -> None:
"""Test successful creation of config entries via user configuration when minimal status is reported.
We test different combinations of minimal statuses, where the title of the
integration will vary.
"""
with patch("apcaccess.status.parse") as mock_parse, patch(
"apcaccess.status.get", return_value=b""
), _patch_setup() as mock_setup:
status = MOCK_MINIMAL_STATUS | extra_status
mock_parse.return_value = status
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_USER}, data=CONF_DATA
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["data"] == CONF_DATA
assert result["title"] == expected_title
mock_setup.assert_called_once()
async def test_flow_import(hass: HomeAssistant) -> None:
"""Test successful creation of config entries via YAML import."""
with patch("apcaccess.status.parse", return_value=MOCK_STATUS), patch(
"apcaccess.status.get", return_value=b""
), _patch_setup() as mock_setup:
# Importing from YAML will create an extra field CONF_RESOURCES in the config
# entry, here we test if it is properly stored.
resources = ["MODEL"]
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=CONF_DATA | {CONF_RESOURCES: resources},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == MOCK_STATUS["MODEL"]
assert result["data"] == CONF_DATA | {CONF_RESOURCES: resources}
mock_setup.assert_called_once()