"""Test the Z-Wave over MQTT config flow.""" from unittest.mock import patch import pytest from homeassistant import config_entries, setup from homeassistant.components.hassio.handler import HassioAPIError from homeassistant.components.ozw.config_flow import TITLE from homeassistant.components.ozw.const import DOMAIN from tests.common import MockConfigEntry ADDON_DISCOVERY_INFO = { "addon": "OpenZWave", "host": "host1", "port": 1234, "username": "name1", "password": "pass1", } @pytest.fixture(name="supervisor") def mock_supervisor_fixture(): """Mock Supervisor.""" with patch("homeassistant.components.hassio.is_hassio", return_value=True): yield @pytest.fixture(name="addon_info") def mock_addon_info(): """Mock Supervisor add-on info.""" with patch("homeassistant.components.hassio.async_get_addon_info") as addon_info: addon_info.return_value = {} yield addon_info @pytest.fixture(name="addon_running") def mock_addon_running(addon_info): """Mock add-on already running.""" addon_info.return_value["state"] = "started" return addon_info @pytest.fixture(name="addon_installed") def mock_addon_installed(addon_info): """Mock add-on already installed but not running.""" addon_info.return_value["state"] = "stopped" addon_info.return_value["version"] = "1.0" return addon_info @pytest.fixture(name="addon_options") def mock_addon_options(addon_info): """Mock add-on options.""" addon_info.return_value["options"] = {} return addon_info.return_value["options"] @pytest.fixture(name="set_addon_options") def mock_set_addon_options(): """Mock set add-on options.""" with patch( "homeassistant.components.hassio.async_set_addon_options" ) as set_options: yield set_options @pytest.fixture(name="install_addon") def mock_install_addon(): """Mock install add-on.""" with patch("homeassistant.components.hassio.async_install_addon") as install_addon: yield install_addon @pytest.fixture(name="start_addon") def mock_start_addon(): """Mock start add-on.""" with patch("homeassistant.components.hassio.async_start_addon") as start_addon: yield start_addon async def test_user_not_supervisor_create_entry(hass, mqtt): """Test the user step creates an entry not on Supervisor.""" await setup.async_setup_component(hass, "persistent_notification", {}) with patch( "homeassistant.components.ozw.async_setup_entry", return_value=True, ) as mock_setup_entry: result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) await hass.async_block_till_done() assert result["type"] == "create_entry" assert result["title"] == TITLE assert result["data"] == { "usb_path": None, "network_key": None, "use_addon": False, "integration_created_addon": False, } assert len(mock_setup_entry.mock_calls) == 1 async def test_mqtt_not_setup(hass): """Test that mqtt is required.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == "abort" assert result["reason"] == "mqtt_required" async def test_one_instance_allowed(hass): """Test that only one instance is allowed.""" entry = MockConfigEntry(domain=DOMAIN, data={}, title=TITLE) entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == "abort" assert result["reason"] == "single_instance_allowed" async def test_not_addon(hass, supervisor, mqtt): """Test opting out of add-on on Supervisor.""" await setup.async_setup_component(hass, "persistent_notification", {}) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) with patch( "homeassistant.components.ozw.async_setup_entry", return_value=True, ) as mock_setup_entry: result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": False} ) await hass.async_block_till_done() assert result["type"] == "create_entry" assert result["title"] == TITLE assert result["data"] == { "usb_path": None, "network_key": None, "use_addon": False, "integration_created_addon": False, } assert len(mock_setup_entry.mock_calls) == 1 async def test_addon_running(hass, supervisor, addon_running, addon_options): """Test add-on already running on Supervisor.""" addon_options["device"] = "/test" addon_options["network_key"] = "abc123" await setup.async_setup_component(hass, "persistent_notification", {}) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) with patch( "homeassistant.components.ozw.async_setup_entry", return_value=True, ) as mock_setup_entry: result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": True} ) await hass.async_block_till_done() assert result["type"] == "create_entry" assert result["title"] == TITLE assert result["data"] == { "usb_path": "/test", "network_key": "abc123", "use_addon": True, "integration_created_addon": False, } assert len(mock_setup_entry.mock_calls) == 1 async def test_addon_info_failure(hass, supervisor, addon_info): """Test add-on info failure.""" addon_info.side_effect = HassioAPIError() await setup.async_setup_component(hass, "persistent_notification", {}) 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"], {"use_addon": True} ) assert result["type"] == "abort" assert result["reason"] == "addon_info_failed" async def test_addon_installed( hass, supervisor, addon_installed, addon_options, set_addon_options, start_addon ): """Test add-on already installed but not running on Supervisor.""" await setup.async_setup_component(hass, "persistent_notification", {}) 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"], {"use_addon": True} ) with patch( "homeassistant.components.ozw.async_setup_entry", return_value=True, ) as mock_setup_entry: result = await hass.config_entries.flow.async_configure( result["flow_id"], {"usb_path": "/test", "network_key": "abc123"} ) await hass.async_block_till_done() assert result["type"] == "create_entry" assert result["title"] == TITLE assert result["data"] == { "usb_path": "/test", "network_key": "abc123", "use_addon": True, "integration_created_addon": False, } assert len(mock_setup_entry.mock_calls) == 1 async def test_set_addon_config_failure( hass, supervisor, addon_installed, addon_options, set_addon_options ): """Test add-on set config failure.""" set_addon_options.side_effect = HassioAPIError() await setup.async_setup_component(hass, "persistent_notification", {}) 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"], {"use_addon": True} ) result = await hass.config_entries.flow.async_configure( result["flow_id"], {"usb_path": "/test", "network_key": "abc123"} ) assert result["type"] == "abort" assert result["reason"] == "addon_set_config_failed" async def test_start_addon_failure( hass, supervisor, addon_installed, addon_options, set_addon_options, start_addon ): """Test add-on start failure.""" start_addon.side_effect = HassioAPIError() await setup.async_setup_component(hass, "persistent_notification", {}) 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"], {"use_addon": True} ) result = await hass.config_entries.flow.async_configure( result["flow_id"], {"usb_path": "/test", "network_key": "abc123"} ) assert result["type"] == "form" assert result["errors"] == {"base": "addon_start_failed"} async def test_addon_not_installed( hass, supervisor, addon_installed, install_addon, addon_options, set_addon_options, start_addon, ): """Test add-on not installed.""" addon_installed.return_value["version"] = None await setup.async_setup_component(hass, "persistent_notification", {}) 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"], {"use_addon": True} ) assert result["type"] == "progress" # Make sure the flow continues when the progress task is done. await hass.async_block_till_done() result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result["type"] == "form" assert result["step_id"] == "start_addon" with patch( "homeassistant.components.ozw.async_setup_entry", return_value=True, ) as mock_setup_entry: result = await hass.config_entries.flow.async_configure( result["flow_id"], {"usb_path": "/test", "network_key": "abc123"} ) await hass.async_block_till_done() assert result["type"] == "create_entry" assert result["title"] == TITLE assert result["data"] == { "usb_path": "/test", "network_key": "abc123", "use_addon": True, "integration_created_addon": True, } assert len(mock_setup_entry.mock_calls) == 1 async def test_install_addon_failure(hass, supervisor, addon_installed, install_addon): """Test add-on install failure.""" addon_installed.return_value["version"] = None install_addon.side_effect = HassioAPIError() await setup.async_setup_component(hass, "persistent_notification", {}) 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"], {"use_addon": True} ) assert result["type"] == "progress" # Make sure the flow continues when the progress task is done. await hass.async_block_till_done() result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result["type"] == "abort" assert result["reason"] == "addon_install_failed" async def test_supervisor_discovery(hass, supervisor, addon_running, addon_options): """Test flow started from Supervisor discovery.""" await setup.async_setup_component(hass, "persistent_notification", {}) addon_options["device"] = "/test" addon_options["network_key"] = "abc123" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_HASSIO}, data=ADDON_DISCOVERY_INFO, ) with patch( "homeassistant.components.ozw.async_setup_entry", return_value=True, ) as mock_setup_entry: result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) await hass.async_block_till_done() assert result["type"] == "create_entry" assert result["title"] == TITLE assert result["data"] == { "usb_path": "/test", "network_key": "abc123", "use_addon": True, "integration_created_addon": False, } assert len(mock_setup_entry.mock_calls) == 1 async def test_clean_discovery_on_user_create( hass, supervisor, addon_running, addon_options ): """Test discovery flow is cleaned up when a user flow is finished.""" await setup.async_setup_component(hass, "persistent_notification", {}) addon_options["device"] = "/test" addon_options["network_key"] = "abc123" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_HASSIO}, data=ADDON_DISCOVERY_INFO, ) assert result["type"] == "form" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) with patch( "homeassistant.components.ozw.async_setup_entry", return_value=True, ) as mock_setup_entry: result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": False} ) await hass.async_block_till_done() assert len(hass.config_entries.flow.async_progress()) == 0 assert result["type"] == "create_entry" assert result["title"] == TITLE assert result["data"] == { "usb_path": None, "network_key": None, "use_addon": False, "integration_created_addon": False, } assert len(mock_setup_entry.mock_calls) == 1 async def test_abort_discovery_with_user_flow( hass, supervisor, addon_running, addon_options ): """Test discovery flow is aborted if a user flow is in progress.""" await setup.async_setup_component(hass, "persistent_notification", {}) await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_HASSIO}, data=ADDON_DISCOVERY_INFO, ) assert result["type"] == "abort" assert result["reason"] == "already_in_progress" assert len(hass.config_entries.flow.async_progress()) == 1 async def test_abort_discovery_with_existing_entry( hass, supervisor, addon_running, addon_options ): """Test discovery flow is aborted if an entry already exists.""" await setup.async_setup_component(hass, "persistent_notification", {}) entry = MockConfigEntry(domain=DOMAIN, data={}, title=TITLE, unique_id=DOMAIN) entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_HASSIO}, data=ADDON_DISCOVERY_INFO, ) assert result["type"] == "abort" assert result["reason"] == "already_configured" async def test_discovery_addon_not_running( hass, supervisor, addon_installed, addon_options, set_addon_options, start_addon ): """Test discovery with add-on already installed but not running.""" addon_options["device"] = None await setup.async_setup_component(hass, "persistent_notification", {}) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_HASSIO}, data=ADDON_DISCOVERY_INFO, ) assert result["step_id"] == "hassio_confirm" assert result["type"] == "form" result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["step_id"] == "start_addon" assert result["type"] == "form" async def test_discovery_addon_not_installed( hass, supervisor, addon_installed, install_addon, addon_options ): """Test discovery with add-on not installed.""" addon_installed.return_value["version"] = None await setup.async_setup_component(hass, "persistent_notification", {}) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_HASSIO}, data=ADDON_DISCOVERY_INFO, ) assert result["step_id"] == "hassio_confirm" assert result["type"] == "form" result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["step_id"] == "install_addon" assert result["type"] == "progress" await hass.async_block_till_done() result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result["type"] == "form" assert result["step_id"] == "start_addon" async def test_import_addon_installed( hass, supervisor, addon_installed, addon_options, set_addon_options, start_addon ): """Test add-on already installed but not running on Supervisor.""" hass.config.components.add("mqtt") await setup.async_setup_component(hass, "persistent_notification", {}) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data={"usb_path": "/test/imported", "network_key": "imported123"}, ) assert result["type"] == "form" assert result["step_id"] == "on_supervisor" result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": True} ) assert result["type"] == "form" assert result["step_id"] == "start_addon" # the default input should be the imported data default_input = result["data_schema"]({}) with patch( "homeassistant.components.ozw.async_setup_entry", return_value=True, ) as mock_setup_entry: result = await hass.config_entries.flow.async_configure( result["flow_id"], default_input ) await hass.async_block_till_done() assert result["type"] == "create_entry" assert result["title"] == TITLE assert result["data"] == { "usb_path": "/test/imported", "network_key": "imported123", "use_addon": True, "integration_created_addon": False, } assert len(mock_setup_entry.mock_calls) == 1