core/tests/components/alarmdecoder/test_config_flow.py

455 lines
14 KiB
Python

"""Test the AlarmDecoder config flow."""
from unittest.mock import patch
from alarmdecoder.util import NoDeviceError
import pytest
from homeassistant import config_entries
from homeassistant.components.alarmdecoder import config_flow
from homeassistant.components.alarmdecoder.const import (
CONF_ALT_NIGHT_MODE,
CONF_AUTO_BYPASS,
CONF_CODE_ARM_REQUIRED,
CONF_DEVICE_BAUD,
CONF_DEVICE_PATH,
CONF_RELAY_ADDR,
CONF_RELAY_CHAN,
CONF_ZONE_LOOP,
CONF_ZONE_NAME,
CONF_ZONE_NUMBER,
CONF_ZONE_RFID,
CONF_ZONE_TYPE,
DEFAULT_ARM_OPTIONS,
DEFAULT_ZONE_OPTIONS,
DOMAIN,
OPTIONS_ARM,
OPTIONS_ZONES,
PROTOCOL_SERIAL,
PROTOCOL_SOCKET,
)
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_PROTOCOL
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry
@pytest.mark.parametrize(
("protocol", "connection", "title"),
[
(
PROTOCOL_SOCKET,
{
CONF_HOST: "alarmdecoder123",
CONF_PORT: 10001,
},
"alarmdecoder123:10001",
),
(
PROTOCOL_SERIAL,
{
CONF_DEVICE_PATH: "/dev/ttyUSB123",
CONF_DEVICE_BAUD: 115000,
},
"/dev/ttyUSB123",
),
],
)
async def test_setups(hass: HomeAssistant, protocol, connection, title) -> None:
"""Test flow for setting up the available AlarmDecoder protocols."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_PROTOCOL: protocol},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "protocol"
with (
patch("homeassistant.components.alarmdecoder.config_flow.AdExt.open"),
patch("homeassistant.components.alarmdecoder.config_flow.AdExt.close"),
patch(
"homeassistant.components.alarmdecoder.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"], connection
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == title
assert result["data"] == {
**connection,
CONF_PROTOCOL: protocol,
}
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
async def test_setup_connection_error(hass: HomeAssistant) -> None:
"""Test flow for setup with a connection error."""
port = 1001
host = "alarmdecoder"
protocol = PROTOCOL_SOCKET
connection_settings = {CONF_HOST: host, CONF_PORT: port}
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_PROTOCOL: protocol},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "protocol"
with (
patch(
"homeassistant.components.alarmdecoder.config_flow.AdExt.open",
side_effect=NoDeviceError,
),
patch("homeassistant.components.alarmdecoder.config_flow.AdExt.close"),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"], connection_settings
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "cannot_connect"}
with (
patch(
"homeassistant.components.alarmdecoder.config_flow.AdExt.open",
side_effect=Exception,
),
patch("homeassistant.components.alarmdecoder.config_flow.AdExt.close"),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"], connection_settings
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "unknown"}
async def test_options_arm_flow(hass: HomeAssistant) -> None:
"""Test arm options flow."""
user_input = {
CONF_ALT_NIGHT_MODE: True,
CONF_AUTO_BYPASS: True,
CONF_CODE_ARM_REQUIRED: True,
}
entry = MockConfigEntry(domain=DOMAIN)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"edit_selection": "Arming Settings"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "arm_settings"
with patch(
"homeassistant.components.alarmdecoder.async_setup_entry", return_value=True
):
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input=user_input,
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert entry.options == {
OPTIONS_ARM: user_input,
OPTIONS_ZONES: DEFAULT_ZONE_OPTIONS,
}
async def test_options_zone_flow(hass: HomeAssistant) -> None:
"""Test options flow for adding/deleting zones."""
zone_number = "2"
zone_settings = {
CONF_ZONE_NAME: "Front Entry",
CONF_ZONE_TYPE: BinarySensorDeviceClass.WINDOW,
}
entry = MockConfigEntry(domain=DOMAIN)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"edit_selection": "Zones"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zone_select"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={CONF_ZONE_NUMBER: zone_number},
)
with patch(
"homeassistant.components.alarmdecoder.async_setup_entry", return_value=True
):
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input=zone_settings,
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert entry.options == {
OPTIONS_ARM: DEFAULT_ARM_OPTIONS,
OPTIONS_ZONES: {zone_number: zone_settings},
}
# Make sure zone can be removed...
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"edit_selection": "Zones"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zone_select"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={CONF_ZONE_NUMBER: zone_number},
)
with patch(
"homeassistant.components.alarmdecoder.async_setup_entry", return_value=True
):
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert entry.options == {
OPTIONS_ARM: DEFAULT_ARM_OPTIONS,
OPTIONS_ZONES: {},
}
async def test_options_zone_flow_validation(hass: HomeAssistant) -> None:
"""Test input validation for zone options flow."""
zone_number = "2"
zone_settings = {
CONF_ZONE_NAME: "Front Entry",
CONF_ZONE_TYPE: BinarySensorDeviceClass.WINDOW,
}
entry = MockConfigEntry(domain=DOMAIN)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"edit_selection": "Zones"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zone_select"
# Zone Number must be int
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={CONF_ZONE_NUMBER: "asd"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zone_select"
assert result["errors"] == {CONF_ZONE_NUMBER: "int"}
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={CONF_ZONE_NUMBER: zone_number},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zone_details"
# CONF_RELAY_ADDR & CONF_RELAY_CHAN are inclusive
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={**zone_settings, CONF_RELAY_ADDR: "1"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zone_details"
assert result["errors"] == {"base": "relay_inclusive"}
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={**zone_settings, CONF_RELAY_CHAN: "1"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zone_details"
assert result["errors"] == {"base": "relay_inclusive"}
# CONF_RELAY_ADDR, CONF_RELAY_CHAN must be int
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={**zone_settings, CONF_RELAY_ADDR: "abc", CONF_RELAY_CHAN: "abc"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zone_details"
assert result["errors"] == {
CONF_RELAY_ADDR: "int",
CONF_RELAY_CHAN: "int",
}
# CONF_ZONE_LOOP depends on CONF_ZONE_RFID
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={**zone_settings, CONF_ZONE_LOOP: "1"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zone_details"
assert result["errors"] == {CONF_ZONE_LOOP: "loop_rfid"}
# CONF_ZONE_LOOP must be int
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={**zone_settings, CONF_ZONE_RFID: "rfid123", CONF_ZONE_LOOP: "ab"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zone_details"
assert result["errors"] == {CONF_ZONE_LOOP: "int"}
# CONF_ZONE_LOOP must be between [1,4]
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={**zone_settings, CONF_ZONE_RFID: "rfid123", CONF_ZONE_LOOP: "5"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "zone_details"
assert result["errors"] == {CONF_ZONE_LOOP: "loop_range"}
# All valid settings
with patch(
"homeassistant.components.alarmdecoder.async_setup_entry", return_value=True
):
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
**zone_settings,
CONF_ZONE_RFID: "rfid123",
CONF_ZONE_LOOP: "2",
CONF_RELAY_ADDR: "12",
CONF_RELAY_CHAN: "1",
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert entry.options == {
OPTIONS_ARM: DEFAULT_ARM_OPTIONS,
OPTIONS_ZONES: {
zone_number: {
**zone_settings,
CONF_ZONE_RFID: "rfid123",
CONF_ZONE_LOOP: 2,
CONF_RELAY_ADDR: 12,
CONF_RELAY_CHAN: 1,
}
},
}
@pytest.mark.parametrize(
("protocol", "connection"),
[
(
PROTOCOL_SOCKET,
{
CONF_HOST: "alarmdecoder123",
CONF_PORT: 10001,
},
),
(
PROTOCOL_SERIAL,
{
CONF_DEVICE_PATH: "/dev/ttyUSB123",
CONF_DEVICE_BAUD: 115000,
},
),
],
)
async def test_one_device_allowed(hass: HomeAssistant, protocol, connection) -> None:
"""Test that only one AlarmDecoder device is allowed."""
flow = config_flow.AlarmDecoderFlowHandler()
flow.hass = hass
MockConfigEntry(
domain=DOMAIN,
data=connection,
).add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_PROTOCOL: protocol},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "protocol"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], connection
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"