321 lines
10 KiB
Python
321 lines
10 KiB
Python
"""Test the Life360 config flow."""
|
|
from unittest.mock import patch
|
|
|
|
from life360 import Life360Error, LoginError
|
|
import pytest
|
|
import voluptuous as vol
|
|
|
|
from homeassistant import config_entries, data_entry_flow
|
|
from homeassistant.components.life360.const import (
|
|
CONF_AUTHORIZATION,
|
|
CONF_DRIVING_SPEED,
|
|
CONF_MAX_GPS_ACCURACY,
|
|
DEFAULT_OPTIONS,
|
|
DOMAIN,
|
|
SHOW_DRIVING,
|
|
)
|
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
from tests.common import MockConfigEntry
|
|
|
|
TEST_USER = "Test@Test.com"
|
|
TEST_PW = "password"
|
|
TEST_PW_3 = "password_3"
|
|
TEST_AUTHORIZATION = "authorization_string"
|
|
TEST_AUTHORIZATION_2 = "authorization_string_2"
|
|
TEST_AUTHORIZATION_3 = "authorization_string_3"
|
|
TEST_MAX_GPS_ACCURACY = "300"
|
|
TEST_DRIVING_SPEED = "18"
|
|
TEST_SHOW_DRIVING = True
|
|
|
|
USER_INPUT = {CONF_USERNAME: TEST_USER, CONF_PASSWORD: TEST_PW}
|
|
|
|
TEST_CONFIG_DATA = {
|
|
CONF_USERNAME: TEST_USER,
|
|
CONF_PASSWORD: TEST_PW,
|
|
CONF_AUTHORIZATION: TEST_AUTHORIZATION,
|
|
}
|
|
TEST_CONFIG_DATA_2 = {
|
|
CONF_USERNAME: TEST_USER,
|
|
CONF_PASSWORD: TEST_PW,
|
|
CONF_AUTHORIZATION: TEST_AUTHORIZATION_2,
|
|
}
|
|
TEST_CONFIG_DATA_3 = {
|
|
CONF_USERNAME: TEST_USER,
|
|
CONF_PASSWORD: TEST_PW_3,
|
|
CONF_AUTHORIZATION: TEST_AUTHORIZATION_3,
|
|
}
|
|
|
|
USER_OPTIONS = {
|
|
"limit_gps_acc": True,
|
|
CONF_MAX_GPS_ACCURACY: TEST_MAX_GPS_ACCURACY,
|
|
"set_drive_speed": True,
|
|
CONF_DRIVING_SPEED: TEST_DRIVING_SPEED,
|
|
SHOW_DRIVING: TEST_SHOW_DRIVING,
|
|
}
|
|
TEST_OPTIONS = {
|
|
CONF_MAX_GPS_ACCURACY: float(TEST_MAX_GPS_ACCURACY),
|
|
CONF_DRIVING_SPEED: float(TEST_DRIVING_SPEED),
|
|
SHOW_DRIVING: TEST_SHOW_DRIVING,
|
|
}
|
|
|
|
|
|
# ========== Common Fixtures & Functions ===============================================
|
|
|
|
|
|
@pytest.fixture(name="life360", autouse=True)
|
|
def life360_fixture():
|
|
"""Mock life360 config entry setup & unload."""
|
|
with patch(
|
|
"homeassistant.components.life360.async_setup_entry", return_value=True
|
|
), patch("homeassistant.components.life360.async_unload_entry", return_value=True):
|
|
yield
|
|
|
|
|
|
@pytest.fixture
|
|
def life360_api():
|
|
"""Mock Life360 api."""
|
|
with patch(
|
|
"homeassistant.components.life360.config_flow.Life360", autospec=True
|
|
) as mock:
|
|
yield mock.return_value
|
|
|
|
|
|
def create_config_entry(hass, state=None):
|
|
"""Create mock config entry."""
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data=TEST_CONFIG_DATA,
|
|
version=1,
|
|
state=state,
|
|
options=DEFAULT_OPTIONS,
|
|
unique_id=TEST_USER.lower(),
|
|
)
|
|
config_entry.add_to_hass(hass)
|
|
return config_entry
|
|
|
|
|
|
# ========== User Flow Tests ===========================================================
|
|
|
|
|
|
async def test_user_show_form(hass: HomeAssistant, life360_api) -> None:
|
|
"""Test that the form is served with no input."""
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
life360_api.get_authorization.assert_not_called()
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
assert result["step_id"] == "user"
|
|
assert not result["errors"]
|
|
|
|
schema = result["data_schema"].schema
|
|
assert set(schema) == set(USER_INPUT)
|
|
# username and password fields should be empty.
|
|
keys = list(schema)
|
|
for key in USER_INPUT:
|
|
assert keys[keys.index(key)].default == vol.UNDEFINED
|
|
|
|
|
|
async def test_user_config_flow_success(hass: HomeAssistant, life360_api) -> None:
|
|
"""Test a successful user config flow."""
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
life360_api.get_authorization.return_value = TEST_AUTHORIZATION
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], USER_INPUT
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
life360_api.get_authorization.assert_called_once()
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
|
assert result["title"] == TEST_USER.lower()
|
|
assert result["data"] == TEST_CONFIG_DATA
|
|
assert result["options"] == DEFAULT_OPTIONS
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("exception", "error"),
|
|
[(LoginError, "invalid_auth"), (Life360Error, "cannot_connect")],
|
|
)
|
|
async def test_user_config_flow_error(
|
|
hass: HomeAssistant, life360_api, caplog: pytest.LogCaptureFixture, exception, error
|
|
) -> None:
|
|
"""Test a user config flow with an error."""
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
life360_api.get_authorization.side_effect = exception("test reason")
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], USER_INPUT
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
life360_api.get_authorization.assert_called_once()
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
assert result["step_id"] == "user"
|
|
assert result["errors"]
|
|
assert result["errors"]["base"] == error
|
|
|
|
assert "test reason" in caplog.text
|
|
|
|
schema = result["data_schema"].schema
|
|
assert set(schema) == set(USER_INPUT)
|
|
# username and password fields should be prefilled with current values.
|
|
keys = list(schema)
|
|
for key, val in USER_INPUT.items():
|
|
default = keys[keys.index(key)].default
|
|
assert default != vol.UNDEFINED
|
|
assert default() == val
|
|
|
|
|
|
async def test_user_config_flow_already_configured(
|
|
hass: HomeAssistant, life360_api
|
|
) -> None:
|
|
"""Test a user config flow with an account already configured."""
|
|
create_config_entry(hass)
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], USER_INPUT
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
life360_api.get_authorization.assert_not_called()
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.ABORT
|
|
assert result["reason"] == "already_configured"
|
|
|
|
|
|
# ========== Reauth Flow Tests =========================================================
|
|
|
|
|
|
@pytest.mark.parametrize("state", [None, config_entries.ConfigEntryState.LOADED])
|
|
async def test_reauth_config_flow_success(
|
|
hass: HomeAssistant, life360_api, caplog: pytest.LogCaptureFixture, state
|
|
) -> None:
|
|
"""Test a successful reauthorization config flow."""
|
|
config_entry = create_config_entry(hass, state=state)
|
|
|
|
# Simulate current username & password are still valid, but authorization string has
|
|
# expired, such that getting a new authorization string from server is successful.
|
|
life360_api.get_authorization.return_value = TEST_AUTHORIZATION_2
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={
|
|
"source": config_entries.SOURCE_REAUTH,
|
|
"entry_id": config_entry.entry_id,
|
|
"title_placeholders": {"name": config_entry.title},
|
|
"unique_id": config_entry.unique_id,
|
|
},
|
|
data=config_entry.data,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
life360_api.get_authorization.assert_called_once()
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.ABORT
|
|
assert result["reason"] == "reauth_successful"
|
|
|
|
assert "Reauthorization successful" in caplog.text
|
|
|
|
assert config_entry.data == TEST_CONFIG_DATA_2
|
|
|
|
|
|
async def test_reauth_config_flow_login_error(
|
|
hass: HomeAssistant, life360_api, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Test a reauthorization config flow with a login error."""
|
|
config_entry = create_config_entry(hass)
|
|
|
|
# Simulate current username & password are invalid, which results in a form
|
|
# requesting new password, with old password as default value.
|
|
life360_api.get_authorization.side_effect = LoginError("test reason")
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={
|
|
"source": config_entries.SOURCE_REAUTH,
|
|
"entry_id": config_entry.entry_id,
|
|
"title_placeholders": {"name": config_entry.title},
|
|
"unique_id": config_entry.unique_id,
|
|
},
|
|
data=config_entry.data,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
life360_api.get_authorization.assert_called_once()
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
assert result["step_id"] == "reauth_confirm"
|
|
assert result["errors"]
|
|
assert result["errors"]["base"] == "invalid_auth"
|
|
|
|
assert "test reason" in caplog.text
|
|
|
|
schema = result["data_schema"].schema
|
|
assert len(schema) == 1
|
|
assert "password" in schema
|
|
key = list(schema)[0]
|
|
assert key.default() == TEST_PW
|
|
|
|
# Simulate getting a new, valid password.
|
|
life360_api.get_authorization.reset_mock(side_effect=True)
|
|
life360_api.get_authorization.return_value = TEST_AUTHORIZATION_3
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {CONF_PASSWORD: TEST_PW_3}
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
life360_api.get_authorization.assert_called_once()
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.ABORT
|
|
assert result["reason"] == "reauth_successful"
|
|
|
|
assert "Reauthorization successful" in caplog.text
|
|
|
|
assert config_entry.data == TEST_CONFIG_DATA_3
|
|
|
|
|
|
# ========== Option flow Tests =========================================================
|
|
|
|
|
|
async def test_options_flow(hass: HomeAssistant) -> None:
|
|
"""Test an options flow."""
|
|
config_entry = create_config_entry(hass)
|
|
|
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
assert result["step_id"] == "init"
|
|
assert not result["errors"]
|
|
|
|
schema = result["data_schema"].schema
|
|
assert set(schema) == set(USER_OPTIONS)
|
|
|
|
flow_id = result["flow_id"]
|
|
|
|
result = await hass.config_entries.options.async_configure(flow_id, USER_OPTIONS)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
|
assert result["data"] == TEST_OPTIONS
|
|
|
|
assert config_entry.options == TEST_OPTIONS
|