|
|
|
@@ -5,7 +5,6 @@ from pyicloud.exceptions import PyiCloudFailedLoginException
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from homeassistant import data_entry_flow
|
|
|
|
|
from homeassistant.components.icloud import config_flow
|
|
|
|
|
from homeassistant.components.icloud.config_flow import (
|
|
|
|
|
CONF_TRUSTED_DEVICE,
|
|
|
|
|
CONF_VERIFICATION_CODE,
|
|
|
|
@@ -41,6 +40,9 @@ def mock_controller_service():
|
|
|
|
|
"homeassistant.components.icloud.config_flow.PyiCloudService"
|
|
|
|
|
) as service_mock:
|
|
|
|
|
service_mock.return_value.requires_2fa = True
|
|
|
|
|
service_mock.return_value.trusted_devices = TRUSTED_DEVICES
|
|
|
|
|
service_mock.return_value.send_verification_code = Mock(return_value=True)
|
|
|
|
|
service_mock.return_value.validate_verification_code = Mock(return_value=True)
|
|
|
|
|
yield service_mock
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -63,7 +65,7 @@ def mock_controller_service_send_verification_code_failed():
|
|
|
|
|
with patch(
|
|
|
|
|
"homeassistant.components.icloud.config_flow.PyiCloudService"
|
|
|
|
|
) as service_mock:
|
|
|
|
|
service_mock.return_value.requires_2fa = False
|
|
|
|
|
service_mock.return_value.requires_2fa = True
|
|
|
|
|
service_mock.return_value.trusted_devices = TRUSTED_DEVICES
|
|
|
|
|
service_mock.return_value.send_verification_code = Mock(return_value=False)
|
|
|
|
|
yield service_mock
|
|
|
|
@@ -75,20 +77,13 @@ def mock_controller_service_validate_verification_code_failed():
|
|
|
|
|
with patch(
|
|
|
|
|
"homeassistant.components.icloud.config_flow.PyiCloudService"
|
|
|
|
|
) as service_mock:
|
|
|
|
|
service_mock.return_value.requires_2fa = False
|
|
|
|
|
service_mock.return_value.requires_2fa = True
|
|
|
|
|
service_mock.return_value.trusted_devices = TRUSTED_DEVICES
|
|
|
|
|
service_mock.return_value.send_verification_code = Mock(return_value=True)
|
|
|
|
|
service_mock.return_value.validate_verification_code = Mock(return_value=False)
|
|
|
|
|
yield service_mock
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def init_config_flow(hass: HomeAssistantType):
|
|
|
|
|
"""Init a configuration flow."""
|
|
|
|
|
flow = config_flow.IcloudFlowHandler()
|
|
|
|
|
flow.hass = hass
|
|
|
|
|
return flow
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_user(hass: HomeAssistantType, service: MagicMock):
|
|
|
|
|
"""Test user config."""
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
@@ -118,6 +113,7 @@ async def test_user_with_cookie(
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
)
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
|
|
|
assert result["result"].unique_id == USERNAME
|
|
|
|
|
assert result["title"] == USERNAME
|
|
|
|
|
assert result["data"][CONF_USERNAME] == USERNAME
|
|
|
|
|
assert result["data"][CONF_PASSWORD] == PASSWORD
|
|
|
|
@@ -162,6 +158,7 @@ async def test_import_with_cookie(
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
)
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
|
|
|
assert result["result"].unique_id == USERNAME
|
|
|
|
|
assert result["title"] == USERNAME
|
|
|
|
|
assert result["data"][CONF_USERNAME] == USERNAME
|
|
|
|
|
assert result["data"][CONF_PASSWORD] == PASSWORD
|
|
|
|
@@ -180,6 +177,7 @@ async def test_import_with_cookie(
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
|
|
|
assert result["result"].unique_id == USERNAME_2
|
|
|
|
|
assert result["title"] == USERNAME_2
|
|
|
|
|
assert result["data"][CONF_USERNAME] == USERNAME_2
|
|
|
|
|
assert result["data"][CONF_PASSWORD] == PASSWORD
|
|
|
|
@@ -192,7 +190,9 @@ async def test_two_accounts_setup(
|
|
|
|
|
):
|
|
|
|
|
"""Test to setup two accounts."""
|
|
|
|
|
MockConfigEntry(
|
|
|
|
|
domain=DOMAIN, data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}
|
|
|
|
|
domain=DOMAIN,
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
unique_id=USERNAME,
|
|
|
|
|
).add_to_hass(hass)
|
|
|
|
|
|
|
|
|
|
# import with username and password
|
|
|
|
@@ -202,6 +202,7 @@ async def test_two_accounts_setup(
|
|
|
|
|
data={CONF_USERNAME: USERNAME_2, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
)
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
|
|
|
assert result["result"].unique_id == USERNAME_2
|
|
|
|
|
assert result["title"] == USERNAME_2
|
|
|
|
|
assert result["data"][CONF_USERNAME] == USERNAME_2
|
|
|
|
|
assert result["data"][CONF_PASSWORD] == PASSWORD
|
|
|
|
@@ -212,7 +213,9 @@ async def test_two_accounts_setup(
|
|
|
|
|
async def test_abort_if_already_setup(hass: HomeAssistantType):
|
|
|
|
|
"""Test we abort if the account is already setup."""
|
|
|
|
|
MockConfigEntry(
|
|
|
|
|
domain=DOMAIN, data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}
|
|
|
|
|
domain=DOMAIN,
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
unique_id=USERNAME,
|
|
|
|
|
).add_to_hass(hass)
|
|
|
|
|
|
|
|
|
|
# Should fail, same USERNAME (import)
|
|
|
|
@@ -222,7 +225,7 @@ async def test_abort_if_already_setup(hass: HomeAssistantType):
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
)
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
|
|
|
|
assert result["reason"] == "username_exists"
|
|
|
|
|
assert result["reason"] == "already_configured"
|
|
|
|
|
|
|
|
|
|
# Should fail, same USERNAME (flow)
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
@@ -230,8 +233,8 @@ async def test_abort_if_already_setup(hass: HomeAssistantType):
|
|
|
|
|
context={"source": SOURCE_USER},
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
)
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
|
assert result["errors"] == {CONF_USERNAME: "username_exists"}
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
|
|
|
|
assert result["reason"] == "already_configured"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_login_failed(hass: HomeAssistantType):
|
|
|
|
@@ -251,20 +254,28 @@ async def test_login_failed(hass: HomeAssistantType):
|
|
|
|
|
|
|
|
|
|
async def test_trusted_device(hass: HomeAssistantType, service: MagicMock):
|
|
|
|
|
"""Test trusted_device step."""
|
|
|
|
|
flow = init_config_flow(hass)
|
|
|
|
|
await flow.async_step_user({CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD})
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
|
DOMAIN,
|
|
|
|
|
context={"source": SOURCE_USER},
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = await flow.async_step_trusted_device()
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
|
assert result["step_id"] == CONF_TRUSTED_DEVICE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_trusted_device_success(hass: HomeAssistantType, service: MagicMock):
|
|
|
|
|
"""Test trusted_device step success."""
|
|
|
|
|
flow = init_config_flow(hass)
|
|
|
|
|
await flow.async_step_user({CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD})
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
|
DOMAIN,
|
|
|
|
|
context={"source": SOURCE_USER},
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = await flow.async_step_trusted_device({CONF_TRUSTED_DEVICE: 0})
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
|
|
|
result["flow_id"], {CONF_TRUSTED_DEVICE: 0}
|
|
|
|
|
)
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
|
assert result["step_id"] == CONF_VERIFICATION_CODE
|
|
|
|
|
|
|
|
|
@@ -273,34 +284,53 @@ async def test_send_verification_code_failed(
|
|
|
|
|
hass: HomeAssistantType, service_send_verification_code_failed: MagicMock
|
|
|
|
|
):
|
|
|
|
|
"""Test when we have errors during send_verification_code."""
|
|
|
|
|
flow = init_config_flow(hass)
|
|
|
|
|
await flow.async_step_user({CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD})
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
|
DOMAIN,
|
|
|
|
|
context={"source": SOURCE_USER},
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = await flow.async_step_trusted_device({CONF_TRUSTED_DEVICE: 0})
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
|
|
|
result["flow_id"], {CONF_TRUSTED_DEVICE: 0}
|
|
|
|
|
)
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
|
assert result["step_id"] == CONF_TRUSTED_DEVICE
|
|
|
|
|
assert result["errors"] == {CONF_TRUSTED_DEVICE: "send_verification_code"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_verification_code(hass: HomeAssistantType):
|
|
|
|
|
async def test_verification_code(hass: HomeAssistantType, service: MagicMock):
|
|
|
|
|
"""Test verification_code step."""
|
|
|
|
|
flow = init_config_flow(hass)
|
|
|
|
|
await flow.async_step_user({CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD})
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
|
DOMAIN,
|
|
|
|
|
context={"source": SOURCE_USER},
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
)
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
|
|
|
result["flow_id"], {CONF_TRUSTED_DEVICE: 0}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = await flow.async_step_verification_code()
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
|
assert result["step_id"] == CONF_VERIFICATION_CODE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_verification_code_success(
|
|
|
|
|
hass: HomeAssistantType, service_with_cookie: MagicMock
|
|
|
|
|
):
|
|
|
|
|
async def test_verification_code_success(hass: HomeAssistantType, service: MagicMock):
|
|
|
|
|
"""Test verification_code step success."""
|
|
|
|
|
flow = init_config_flow(hass)
|
|
|
|
|
await flow.async_step_user({CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD})
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
|
DOMAIN,
|
|
|
|
|
context={"source": SOURCE_USER},
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
)
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
|
|
|
result["flow_id"], {CONF_TRUSTED_DEVICE: 0}
|
|
|
|
|
)
|
|
|
|
|
service.return_value.requires_2fa = False
|
|
|
|
|
|
|
|
|
|
result = await flow.async_step_verification_code({CONF_VERIFICATION_CODE: 0})
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
|
|
|
result["flow_id"], {CONF_VERIFICATION_CODE: "0"}
|
|
|
|
|
)
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
|
|
|
assert result["result"].unique_id == USERNAME
|
|
|
|
|
assert result["title"] == USERNAME
|
|
|
|
|
assert result["data"][CONF_USERNAME] == USERNAME
|
|
|
|
|
assert result["data"][CONF_PASSWORD] == PASSWORD
|
|
|
|
@@ -312,10 +342,18 @@ async def test_validate_verification_code_failed(
|
|
|
|
|
hass: HomeAssistantType, service_validate_verification_code_failed: MagicMock
|
|
|
|
|
):
|
|
|
|
|
"""Test when we have errors during validate_verification_code."""
|
|
|
|
|
flow = init_config_flow(hass)
|
|
|
|
|
await flow.async_step_user({CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD})
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
|
DOMAIN,
|
|
|
|
|
context={"source": SOURCE_USER},
|
|
|
|
|
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
|
|
|
|
)
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
|
|
|
result["flow_id"], {CONF_TRUSTED_DEVICE: 0}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = await flow.async_step_verification_code({CONF_VERIFICATION_CODE: 0})
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
|
|
|
result["flow_id"], {CONF_VERIFICATION_CODE: "0"}
|
|
|
|
|
)
|
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
|
assert result["step_id"] == CONF_TRUSTED_DEVICE
|
|
|
|
|
assert result["errors"] == {"base": "validate_verification_code"}
|
|
|
|
|