255 lines
8.7 KiB
Python
255 lines
8.7 KiB
Python
"""Tests for the Config Entry Flow helper."""
|
|
from unittest.mock import patch, Mock
|
|
|
|
import pytest
|
|
|
|
from homeassistant import config_entries, data_entry_flow, setup
|
|
from homeassistant.helpers import config_entry_flow
|
|
from tests.common import (
|
|
MockConfigEntry,
|
|
MockModule,
|
|
mock_coro,
|
|
mock_integration,
|
|
mock_entity_platform,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def discovery_flow_conf(hass):
|
|
"""Register a handler."""
|
|
handler_conf = {"discovered": False}
|
|
|
|
async def has_discovered_devices(hass):
|
|
"""Mock if we have discovered devices."""
|
|
return handler_conf["discovered"]
|
|
|
|
with patch.dict(config_entries.HANDLERS):
|
|
config_entry_flow.register_discovery_flow(
|
|
"test", "Test", has_discovered_devices, config_entries.CONN_CLASS_LOCAL_POLL
|
|
)
|
|
yield handler_conf
|
|
|
|
|
|
@pytest.fixture
|
|
def webhook_flow_conf(hass):
|
|
"""Register a handler."""
|
|
with patch.dict(config_entries.HANDLERS):
|
|
config_entry_flow.register_webhook_flow("test_single", "Test Single", {}, False)
|
|
config_entry_flow.register_webhook_flow(
|
|
"test_multiple", "Test Multiple", {}, True
|
|
)
|
|
yield {}
|
|
|
|
|
|
async def test_single_entry_allowed(hass, discovery_flow_conf):
|
|
"""Test only a single entry is allowed."""
|
|
flow = config_entries.HANDLERS["test"]()
|
|
flow.hass = hass
|
|
|
|
MockConfigEntry(domain="test").add_to_hass(hass)
|
|
result = await flow.async_step_user()
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
|
assert result["reason"] == "single_instance_allowed"
|
|
|
|
|
|
async def test_user_no_devices_found(hass, discovery_flow_conf):
|
|
"""Test if no devices found."""
|
|
flow = config_entries.HANDLERS["test"]()
|
|
flow.hass = hass
|
|
flow.context = {"source": config_entries.SOURCE_USER}
|
|
result = await flow.async_step_confirm(user_input={})
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
|
assert result["reason"] == "no_devices_found"
|
|
|
|
|
|
async def test_user_has_confirmation(hass, discovery_flow_conf):
|
|
"""Test user requires no confirmation to setup."""
|
|
flow = config_entries.HANDLERS["test"]()
|
|
flow.hass = hass
|
|
discovery_flow_conf["discovered"] = True
|
|
|
|
result = await flow.async_step_user()
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
|
|
@pytest.mark.parametrize("source", ["discovery", "ssdp", "zeroconf"])
|
|
async def test_discovery_single_instance(hass, discovery_flow_conf, source):
|
|
"""Test we not allow duplicates."""
|
|
flow = config_entries.HANDLERS["test"]()
|
|
flow.hass = hass
|
|
flow.context = {}
|
|
|
|
MockConfigEntry(domain="test").add_to_hass(hass)
|
|
result = await getattr(flow, "async_step_{}".format(source))({})
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
|
assert result["reason"] == "single_instance_allowed"
|
|
|
|
|
|
@pytest.mark.parametrize("source", ["discovery", "ssdp", "zeroconf"])
|
|
async def test_discovery_confirmation(hass, discovery_flow_conf, source):
|
|
"""Test we ask for confirmation via discovery."""
|
|
flow = config_entries.HANDLERS["test"]()
|
|
flow.hass = hass
|
|
flow.context = {}
|
|
|
|
result = await getattr(flow, "async_step_{}".format(source))({})
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
assert result["step_id"] == "confirm"
|
|
|
|
result = await flow.async_step_confirm({})
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
|
|
|
|
async def test_multiple_discoveries(hass, discovery_flow_conf):
|
|
"""Test we only create one instance for multiple discoveries."""
|
|
mock_entity_platform(hass, "config_flow.test", None)
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
"test", context={"source": config_entries.SOURCE_DISCOVERY}, data={}
|
|
)
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
# Second discovery
|
|
result = await hass.config_entries.flow.async_init(
|
|
"test", context={"source": config_entries.SOURCE_DISCOVERY}, data={}
|
|
)
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
|
|
|
|
|
async def test_only_one_in_progress(hass, discovery_flow_conf):
|
|
"""Test a user initialized one will finish and cancel discovered one."""
|
|
mock_entity_platform(hass, "config_flow.test", None)
|
|
|
|
# Discovery starts flow
|
|
result = await hass.config_entries.flow.async_init(
|
|
"test", context={"source": config_entries.SOURCE_DISCOVERY}, data={}
|
|
)
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
# User starts flow
|
|
result = await hass.config_entries.flow.async_init(
|
|
"test", context={"source": config_entries.SOURCE_USER}, data={}
|
|
)
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
# Discovery flow has not been aborted
|
|
assert len(hass.config_entries.flow.async_progress()) == 2
|
|
|
|
# Discovery should be aborted once user confirms
|
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
assert len(hass.config_entries.flow.async_progress()) == 0
|
|
|
|
|
|
async def test_import_no_confirmation(hass, discovery_flow_conf):
|
|
"""Test import requires no confirmation to set up."""
|
|
flow = config_entries.HANDLERS["test"]()
|
|
flow.hass = hass
|
|
discovery_flow_conf["discovered"] = True
|
|
|
|
result = await flow.async_step_import(None)
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
|
|
|
|
async def test_import_single_instance(hass, discovery_flow_conf):
|
|
"""Test import doesn't create second instance."""
|
|
flow = config_entries.HANDLERS["test"]()
|
|
flow.hass = hass
|
|
discovery_flow_conf["discovered"] = True
|
|
MockConfigEntry(domain="test").add_to_hass(hass)
|
|
|
|
result = await flow.async_step_import(None)
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
|
|
|
|
|
async def test_webhook_single_entry_allowed(hass, webhook_flow_conf):
|
|
"""Test only a single entry is allowed."""
|
|
flow = config_entries.HANDLERS["test_single"]()
|
|
flow.hass = hass
|
|
|
|
MockConfigEntry(domain="test_single").add_to_hass(hass)
|
|
result = await flow.async_step_user()
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
|
assert result["reason"] == "one_instance_allowed"
|
|
|
|
|
|
async def test_webhook_multiple_entries_allowed(hass, webhook_flow_conf):
|
|
"""Test multiple entries are allowed when specified."""
|
|
flow = config_entries.HANDLERS["test_multiple"]()
|
|
flow.hass = hass
|
|
|
|
MockConfigEntry(domain="test_multiple").add_to_hass(hass)
|
|
hass.config.api = Mock(base_url="http://example.com")
|
|
|
|
result = await flow.async_step_user()
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
|
|
async def test_webhook_config_flow_registers_webhook(hass, webhook_flow_conf):
|
|
"""Test setting up an entry creates a webhook."""
|
|
flow = config_entries.HANDLERS["test_single"]()
|
|
flow.hass = hass
|
|
|
|
hass.config.api = Mock(base_url="http://example.com")
|
|
result = await flow.async_step_user(user_input={})
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
assert result["data"]["webhook_id"] is not None
|
|
|
|
|
|
async def test_webhook_create_cloudhook(hass, webhook_flow_conf):
|
|
"""Test only a single entry is allowed."""
|
|
assert await setup.async_setup_component(hass, "cloud", {})
|
|
|
|
async_setup_entry = Mock(return_value=mock_coro(True))
|
|
async_unload_entry = Mock(return_value=mock_coro(True))
|
|
|
|
mock_integration(
|
|
hass,
|
|
MockModule(
|
|
"test_single",
|
|
async_setup_entry=async_setup_entry,
|
|
async_unload_entry=async_unload_entry,
|
|
async_remove_entry=config_entry_flow.webhook_async_remove_entry,
|
|
),
|
|
)
|
|
mock_entity_platform(hass, "config_flow.test_single", None)
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
"test_single", context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
|
|
coro = mock_coro({"cloudhook_url": "https://example.com"})
|
|
|
|
with patch(
|
|
"hass_nabucasa.cloudhooks.Cloudhooks.async_create", return_value=coro
|
|
) as mock_create, patch(
|
|
"homeassistant.components.cloud.async_active_subscription", return_value=True
|
|
), patch(
|
|
"homeassistant.components.cloud.async_is_logged_in", return_value=True
|
|
):
|
|
|
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
|
|
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
assert result["description_placeholders"]["webhook_url"] == "https://example.com"
|
|
assert len(mock_create.mock_calls) == 1
|
|
assert len(async_setup_entry.mock_calls) == 1
|
|
|
|
with patch(
|
|
"hass_nabucasa.cloudhooks.Cloudhooks.async_delete", return_value=coro
|
|
) as mock_delete:
|
|
|
|
result = await hass.config_entries.async_remove(result["result"].entry_id)
|
|
|
|
assert len(mock_delete.mock_calls) == 1
|
|
assert result["require_restart"] is False
|