Files
core/tests/components/verisure/test_config_flow.py
T

562 lines
18 KiB
Python

"""Test the Verisure config flow."""
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from verisure import Error as VerisureError, LoginError as VerisureLoginError
from homeassistant import config_entries
from homeassistant.components.verisure.const import (
CONF_GIID,
CONF_LOCK_CODE_DIGITS,
DEFAULT_LOCK_CODE_DIGITS,
DOMAIN,
)
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from tests.common import MockConfigEntry
async def test_full_user_flow_single_installation(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_verisure_config_flow: MagicMock,
) -> None:
"""Test a full user initiated configuration flow with a single installation."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result.get("step_id") == "user"
assert result.get("type") is FlowResultType.FORM
assert result.get("errors") == {}
mock_verisure_config_flow.get_installations.return_value = {
k1: {k2: {k3: [v3[0]] for k3, v3 in v2.items()} for k2, v2 in v1.items()}
for k1, v1 in mock_verisure_config_flow.get_installations.return_value.items()
}
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "SuperS3cr3t!",
},
)
await hass.async_block_till_done()
assert result2.get("type") is FlowResultType.CREATE_ENTRY
assert result2.get("title") == "ascending (12345th street)"
assert result2.get("data") == {
CONF_GIID: "12345",
CONF_EMAIL: "verisure_my_pages@example.com",
CONF_PASSWORD: "SuperS3cr3t!",
}
assert len(mock_verisure_config_flow.login.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
async def test_full_user_flow_multiple_installations(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_verisure_config_flow: MagicMock,
) -> None:
"""Test a full user initiated configuration flow with multiple installations."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result.get("step_id") == "user"
assert result.get("type") is FlowResultType.FORM
assert result.get("errors") == {}
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "SuperS3cr3t!",
},
)
await hass.async_block_till_done()
assert result2.get("step_id") == "installation"
assert result2.get("type") is FlowResultType.FORM
assert result2.get("errors") is None
result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"], {"giid": "54321"}
)
await hass.async_block_till_done()
assert result3.get("type") is FlowResultType.CREATE_ENTRY
assert result3.get("title") == "descending (54321th street)"
assert result3.get("data") == {
CONF_GIID: "54321",
CONF_EMAIL: "verisure_my_pages@example.com",
CONF_PASSWORD: "SuperS3cr3t!",
}
assert len(mock_verisure_config_flow.login.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
async def test_full_user_flow_single_installation_with_mfa(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_verisure_config_flow: MagicMock,
) -> None:
"""Test a full user initiated flow with a single installation and mfa."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result.get("step_id") == "user"
assert result.get("type") is FlowResultType.FORM
assert result.get("errors") == {}
mock_verisure_config_flow.login.side_effect = VerisureLoginError(
"Multifactor authentication enabled, disable or create MFA cookie"
)
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "SuperS3cr3t!",
},
)
await hass.async_block_till_done()
assert result2.get("type") is FlowResultType.FORM
assert result2.get("step_id") == "mfa"
mock_verisure_config_flow.login.side_effect = None
mock_verisure_config_flow.get_installations.return_value = {
k1: {k2: {k3: [v3[0]] for k3, v3 in v2.items()} for k2, v2 in v1.items()}
for k1, v1 in mock_verisure_config_flow.get_installations.return_value.items()
}
result3 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"code": "123456",
},
)
await hass.async_block_till_done()
assert result3.get("type") is FlowResultType.CREATE_ENTRY
assert result3.get("title") == "ascending (12345th street)"
assert result3.get("data") == {
CONF_GIID: "12345",
CONF_EMAIL: "verisure_my_pages@example.com",
CONF_PASSWORD: "SuperS3cr3t!",
}
assert len(mock_verisure_config_flow.login.mock_calls) == 1
assert len(mock_verisure_config_flow.request_mfa.mock_calls) == 1
assert len(mock_verisure_config_flow.validate_mfa.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
async def test_full_user_flow_multiple_installations_with_mfa(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_verisure_config_flow: MagicMock,
) -> None:
"""Test a full user initiated configuration flow with a single installation."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result.get("step_id") == "user"
assert result.get("type") is FlowResultType.FORM
assert result.get("errors") == {}
mock_verisure_config_flow.login.side_effect = VerisureLoginError(
"Multifactor authentication enabled, disable or create MFA cookie"
)
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "SuperS3cr3t!",
},
)
await hass.async_block_till_done()
assert result2.get("type") is FlowResultType.FORM
assert result2.get("step_id") == "mfa"
mock_verisure_config_flow.login.side_effect = None
result3 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"code": "123456",
},
)
await hass.async_block_till_done()
assert result3.get("step_id") == "installation"
assert result3.get("type") is FlowResultType.FORM
assert result3.get("errors") is None
result4 = await hass.config_entries.flow.async_configure(
result3["flow_id"], {"giid": "54321"}
)
await hass.async_block_till_done()
assert result4.get("type") is FlowResultType.CREATE_ENTRY
assert result4.get("title") == "descending (54321th street)"
assert result4.get("data") == {
CONF_GIID: "54321",
CONF_EMAIL: "verisure_my_pages@example.com",
CONF_PASSWORD: "SuperS3cr3t!",
}
assert len(mock_verisure_config_flow.login.mock_calls) == 1
assert len(mock_verisure_config_flow.request_mfa.mock_calls) == 1
assert len(mock_verisure_config_flow.validate_mfa.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize(
("side_effect", "error"),
[
(VerisureLoginError, "invalid_auth"),
(VerisureError, "unknown"),
],
)
async def test_verisure_errors(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_verisure_config_flow: MagicMock,
side_effect: Exception,
error: str,
) -> None:
"""Test a flow with an invalid Verisure My Pages login."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
mock_verisure_config_flow.login.side_effect = side_effect
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "SuperS3cr3t!",
},
)
await hass.async_block_till_done()
assert result2.get("type") is FlowResultType.FORM
assert result2.get("step_id") == "user"
assert result2.get("errors") == {"base": error}
mock_verisure_config_flow.login.side_effect = VerisureLoginError(
"Multifactor authentication enabled, disable or create MFA cookie"
)
mock_verisure_config_flow.request_mfa.side_effect = side_effect
result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "SuperS3cr3t!",
},
)
await hass.async_block_till_done()
mock_verisure_config_flow.request_mfa.side_effect = None
assert result3.get("type") is FlowResultType.FORM
assert result3.get("step_id") == "user"
assert result3.get("errors") == {"base": "unknown_mfa"}
result4 = await hass.config_entries.flow.async_configure(
result3["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "SuperS3cr3t!",
},
)
await hass.async_block_till_done()
assert result4.get("type") is FlowResultType.FORM
assert result4.get("step_id") == "mfa"
mock_verisure_config_flow.validate_mfa.side_effect = side_effect
result5 = await hass.config_entries.flow.async_configure(
result4["flow_id"],
{
"code": "123456",
},
)
assert result5.get("type") is FlowResultType.FORM
assert result5.get("step_id") == "mfa"
assert result5.get("errors") == {"base": error}
mock_verisure_config_flow.get_installations.return_value = {
k1: {k2: {k3: [v3[0]] for k3, v3 in v2.items()} for k2, v2 in v1.items()}
for k1, v1 in mock_verisure_config_flow.get_installations.return_value.items()
}
mock_verisure_config_flow.validate_mfa.side_effect = None
mock_verisure_config_flow.login.side_effect = None
result6 = await hass.config_entries.flow.async_configure(
result5["flow_id"],
{
"code": "654321",
},
)
await hass.async_block_till_done()
assert result6.get("type") is FlowResultType.CREATE_ENTRY
assert result6.get("title") == "ascending (12345th street)"
assert result6.get("data") == {
CONF_GIID: "12345",
CONF_EMAIL: "verisure_my_pages@example.com",
CONF_PASSWORD: "SuperS3cr3t!",
}
assert len(mock_verisure_config_flow.login.mock_calls) == 3
assert len(mock_verisure_config_flow.request_mfa.mock_calls) == 2
assert len(mock_verisure_config_flow.validate_mfa.mock_calls) == 2
assert len(mock_setup_entry.mock_calls) == 1
async def test_dhcp(hass: HomeAssistant) -> None:
"""Test that DHCP discovery works."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
data=DhcpServiceInfo(
ip="1.2.3.4", macaddress="0123456789ab", hostname="mock_hostname"
),
context={"source": config_entries.SOURCE_DHCP},
)
assert result.get("type") is FlowResultType.FORM
assert result.get("step_id") == "user"
async def test_reauth_flow(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_verisure_config_flow: MagicMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test a reauthentication flow."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reauth_flow(hass)
assert result.get("step_id") == "reauth_confirm"
assert result.get("type") is FlowResultType.FORM
assert result.get("errors") == {}
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "correct horse battery staple",
},
)
await hass.async_block_till_done()
assert result2.get("type") is FlowResultType.ABORT
assert result2.get("reason") == "reauth_successful"
assert mock_config_entry.data == {
CONF_GIID: "12345",
CONF_EMAIL: "verisure_my_pages@example.com",
CONF_PASSWORD: "correct horse battery staple",
}
assert len(mock_verisure_config_flow.login.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
async def test_reauth_flow_with_mfa(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_verisure_config_flow: MagicMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test a reauthentication flow."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reauth_flow(hass)
assert result.get("step_id") == "reauth_confirm"
assert result.get("type") is FlowResultType.FORM
assert result.get("errors") == {}
mock_verisure_config_flow.login.side_effect = VerisureLoginError(
"Multifactor authentication enabled, disable or create MFA cookie"
)
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "correct horse battery staple!",
},
)
await hass.async_block_till_done()
assert result2.get("type") is FlowResultType.FORM
assert result2.get("step_id") == "reauth_mfa"
mock_verisure_config_flow.login.side_effect = None
result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"],
{
"code": "123456",
},
)
await hass.async_block_till_done()
assert result3.get("type") is FlowResultType.ABORT
assert result3.get("reason") == "reauth_successful"
assert mock_config_entry.data == {
CONF_GIID: "12345",
CONF_EMAIL: "verisure_my_pages@example.com",
CONF_PASSWORD: "correct horse battery staple!",
}
assert len(mock_verisure_config_flow.login.mock_calls) == 2
assert len(mock_verisure_config_flow.request_mfa.mock_calls) == 1
assert len(mock_verisure_config_flow.validate_mfa.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize(
("side_effect", "error"),
[
(VerisureLoginError, "invalid_auth"),
(VerisureError, "unknown"),
],
)
async def test_reauth_flow_errors(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_verisure_config_flow: MagicMock,
mock_config_entry: MockConfigEntry,
side_effect: Exception,
error: str,
) -> None:
"""Test a reauthentication flow."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reauth_flow(hass)
mock_verisure_config_flow.login.side_effect = side_effect
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "WrOngP4ssw0rd!",
},
)
await hass.async_block_till_done()
assert result2.get("step_id") == "reauth_confirm"
assert result2.get("type") is FlowResultType.FORM
assert result2.get("errors") == {"base": error}
mock_verisure_config_flow.login.side_effect = VerisureLoginError(
"Multifactor authentication enabled, disable or create MFA cookie"
)
mock_verisure_config_flow.request_mfa.side_effect = side_effect
result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "SuperS3cr3t!",
},
)
await hass.async_block_till_done()
assert result3.get("type") is FlowResultType.FORM
assert result3.get("step_id") == "reauth_confirm"
assert result3.get("errors") == {"base": "unknown_mfa"}
mock_verisure_config_flow.request_mfa.side_effect = None
result4 = await hass.config_entries.flow.async_configure(
result3["flow_id"],
{
"email": "verisure_my_pages@example.com",
"password": "SuperS3cr3t!",
},
)
await hass.async_block_till_done()
assert result4.get("type") is FlowResultType.FORM
assert result4.get("step_id") == "reauth_mfa"
mock_verisure_config_flow.validate_mfa.side_effect = side_effect
result5 = await hass.config_entries.flow.async_configure(
result4["flow_id"],
{
"code": "123456",
},
)
assert result5.get("type") is FlowResultType.FORM
assert result5.get("step_id") == "reauth_mfa"
assert result5.get("errors") == {"base": error}
mock_verisure_config_flow.validate_mfa.side_effect = None
mock_verisure_config_flow.login.side_effect = None
mock_verisure_config_flow.get_installations.return_value = {
k1: {k2: {k3: [v3[0]] for k3, v3 in v2.items()} for k2, v2 in v1.items()}
for k1, v1 in mock_verisure_config_flow.get_installations.return_value.items()
}
await hass.config_entries.flow.async_configure(
result5["flow_id"],
{
"code": "654321",
},
)
await hass.async_block_till_done()
assert mock_config_entry.data == {
CONF_GIID: "12345",
CONF_EMAIL: "verisure_my_pages@example.com",
CONF_PASSWORD: "SuperS3cr3t!",
}
assert len(mock_verisure_config_flow.login.mock_calls) == 4
assert len(mock_verisure_config_flow.request_mfa.mock_calls) == 2
assert len(mock_verisure_config_flow.validate_mfa.mock_calls) == 2
assert len(mock_setup_entry.mock_calls) == 1
async def test_options_flow(hass: HomeAssistant) -> None:
"""Test options config flow."""
entry = MockConfigEntry(domain=DOMAIN, unique_id="12345", data={}, version=2)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.verisure.async_setup_entry",
return_value=True,
):
assert 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.get("type") is FlowResultType.FORM
assert result.get("step_id") == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={CONF_LOCK_CODE_DIGITS: 4},
)
assert result.get("type") is FlowResultType.CREATE_ENTRY
assert result.get("data") == {CONF_LOCK_CODE_DIGITS: DEFAULT_LOCK_CODE_DIGITS}