def test_validate_platform_config(self): """Test validating platform configuration.""" platform_schema = PLATFORM_SCHEMA.extend({ 'hello': str, }) platform_schema_base = PLATFORM_SCHEMA_BASE.extend({}) loader.set_component( self.hass, 'platform_conf', MockModule('platform_conf', platform_schema_base=platform_schema_base)) loader.set_component( self.hass, 'platform_conf.whatever', MockPlatform('whatever', platform_schema=platform_schema)) with assert_setup_component(0): assert setup.setup_component( self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'whatever', 'hello': 'world', 'invalid': 'extra', } }) self.hass.data.pop(setup.DATA_SETUP) self.hass.config.components.remove('platform_conf') with assert_setup_component(1): assert setup.setup_component( self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'whatever', 'hello': 'world', }, 'platform_conf 2': { 'platform': 'whatever', 'invalid': True } }) self.hass.data.pop(setup.DATA_SETUP) self.hass.config.components.remove('platform_conf') with assert_setup_component(0): assert setup.setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'not_existing', 'hello': 'world', } }) self.hass.data.pop(setup.DATA_SETUP) self.hass.config.components.remove('platform_conf') with assert_setup_component(1): assert setup.setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'whatever', 'hello': 'world', } }) self.hass.data.pop(setup.DATA_SETUP) self.hass.config.components.remove('platform_conf') with assert_setup_component(1): assert setup.setup_component(self.hass, 'platform_conf', { 'platform_conf': [{ 'platform': 'whatever', 'hello': 'world', }] }) self.hass.data.pop(setup.DATA_SETUP) self.hass.config.components.remove('platform_conf') # Any falsey platform config will be ignored (None, {}, etc) with assert_setup_component(0) as config: assert setup.setup_component(self.hass, 'platform_conf', {'platform_conf': None}) assert 'platform_conf' in self.hass.config.components assert not config['platform_conf'] # empty assert setup.setup_component(self.hass, 'platform_conf', {'platform_conf': {}}) assert 'platform_conf' in self.hass.config.components assert not config['platform_conf'] # empty
async def test_two_step_flow(opp, client, enable_custom_integrations): """Test we can finish a two step flow.""" mock_integration( opp, MockModule("test", async_setup_entry=AsyncMock(return_value=True)) ) mock_entity_platform(opp, "config_flow.test", None) class TestFlow(core_ce.ConfigFlow): VERSION = 1 async def async_step_user(self, user_input=None): return self.async_show_form( step_id="account", data_schema=vol.Schema({"user_title": str}) ) async def async_step_account(self, user_input=None): return self.async_create_entry( title=user_input["user_title"], data={"secret": "account_token"} ) with patch.dict(HANDLERS, {"test": TestFlow}): resp = await client.post( "/api/config/config_entries/flow", json={"handler": "test"} ) assert resp.status == 200 data = await resp.json() flow_id = data.pop("flow_id") assert data == { "type": "form", "handler": "test", "step_id": "account", "data_schema": [{"name": "user_title", "type": "string"}], "description_placeholders": None, "errors": None, "last_step": None, } with patch.dict(HANDLERS, {"test": TestFlow}): resp = await client.post( f"/api/config/config_entries/flow/{flow_id}", json={"user_title": "user-title"}, ) assert resp.status == 200 entries = opp.config_entries.async_entries("test") assert len(entries) == 1 data = await resp.json() data.pop("flow_id") assert data == { "handler": "test", "type": "create_entry", "title": "user-title", "version": 1, "result": { "disabled_by": None, "domain": "test", "entry_id": entries[0].entry_id, "source": core_ce.SOURCE_USER, "state": core_ce.ConfigEntryState.LOADED.value, "supports_options": False, "supports_unload": False, "pref_disable_new_entities": False, "pref_disable_polling": False, "title": "user-title", "reason": None, }, "description": None, "description_placeholders": None, "options": {}, }
async def test_setup_frontend_before_recorder(hass): """Test frontend is setup before recorder.""" order = [] def gen_domain_setup(domain): async def async_setup(hass, config): order.append(domain) return True return async_setup mock_integration( hass, MockModule( domain="normal_integration", async_setup=gen_domain_setup("normal_integration"), partial_manifest={"after_dependencies": ["an_after_dep"]}, ), ) mock_integration( hass, MockModule( domain="an_after_dep", async_setup=gen_domain_setup("an_after_dep"), ), ) mock_integration( hass, MockModule( domain="frontend", async_setup=gen_domain_setup("frontend"), partial_manifest={ "dependencies": ["http"], "after_dependencies": ["an_after_dep"], }, ), ) mock_integration( hass, MockModule( domain="http", async_setup=gen_domain_setup("http"), ), ) mock_integration( hass, MockModule( domain="recorder", async_setup=gen_domain_setup("recorder"), ), ) await bootstrap._async_set_up_integrations( hass, { "frontend": {}, "http": {}, "recorder": {}, "normal_integration": {}, "an_after_dep": {}, }, ) assert "frontend" in hass.config.components assert "normal_integration" in hass.config.components assert "recorder" in hass.config.components assert order == [ "http", "frontend", "recorder", "an_after_dep", "normal_integration", ]
def test_validate_platform_config(self, caplog): """Test validating platform configuration.""" platform_schema = PLATFORM_SCHEMA.extend({"hello": str}) platform_schema_base = PLATFORM_SCHEMA_BASE.extend({}) mock_integration( self.opp, MockModule("platform_conf", platform_schema_base=platform_schema_base), ) mock_entity_platform( self.opp, "platform_conf.whatever", MockPlatform(platform_schema=platform_schema), ) with assert_setup_component(0): assert setup.setup_component( self.opp, "platform_conf", { "platform_conf": { "platform": "not_existing", "hello": "world" } }, ) self.opp.data.pop(setup.DATA_SETUP) self.opp.config.components.remove("platform_conf") with assert_setup_component(1): assert setup.setup_component( self.opp, "platform_conf", {"platform_conf": { "platform": "whatever", "hello": "world" }}, ) self.opp.data.pop(setup.DATA_SETUP) self.opp.config.components.remove("platform_conf") with assert_setup_component(1): assert setup.setup_component( self.opp, "platform_conf", { "platform_conf": [{ "platform": "whatever", "hello": "world" }] }, ) self.opp.data.pop(setup.DATA_SETUP) self.opp.config.components.remove("platform_conf") # Any falsey platform config will be ignored (None, {}, etc) with assert_setup_component(0) as config: assert setup.setup_component(self.opp, "platform_conf", {"platform_conf": None}) assert "platform_conf" in self.opp.config.components assert not config["platform_conf"] # empty assert setup.setup_component(self.opp, "platform_conf", {"platform_conf": {}}) assert "platform_conf" in self.opp.config.components assert not config["platform_conf"] # empty
async def test_get_integration_with_requirements_pip_install_fails_two_passes( hass): """Check getting an integration with loaded requirements and the pip install fails two passes.""" hass.config.skip_pip = False mock_integration( hass, MockModule("test_component_dep", requirements=["test-comp-dep==1.0.0"])) mock_integration( hass, MockModule("test_component_after_dep", requirements=["test-comp-after-dep==1.0.0"]), ) mock_integration( hass, MockModule( "test_component", requirements=["test-comp==1.0.0"], dependencies=["test_component_dep"], partial_manifest={ "after_dependencies": ["test_component_after_dep"] }, ), ) def _mock_install_package(package, **kwargs): if package == "test-comp==1.0.0": return True return False # 1st pass with pytest.raises(RequirementsNotFound), patch( "homeassistant.util.package.is_installed", return_value=False) as mock_is_installed, patch( "homeassistant.util.package.install_package", side_effect=_mock_install_package) as mock_inst: integration = await async_get_integration_with_requirements( hass, "test_component") assert integration assert integration.domain == "test_component" assert len(mock_is_installed.mock_calls) == 3 assert sorted(mock_call[1][0] for mock_call in mock_is_installed.mock_calls) == [ "test-comp-after-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp==1.0.0", ] assert len(mock_inst.mock_calls) == 7 assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [ "test-comp-after-dep==1.0.0", "test-comp-after-dep==1.0.0", "test-comp-after-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp==1.0.0", ] # 2nd pass with pytest.raises(RequirementsNotFound), patch( "homeassistant.util.package.is_installed", return_value=False) as mock_is_installed, patch( "homeassistant.util.package.install_package", side_effect=_mock_install_package) as mock_inst: integration = await async_get_integration_with_requirements( hass, "test_component") assert integration assert integration.domain == "test_component" assert len(mock_is_installed.mock_calls) == 1 assert sorted(mock_call[1][0] for mock_call in mock_is_installed.mock_calls) == [ "test-comp==1.0.0", ] # On another attempt we remember failures and don't try again assert len(mock_inst.mock_calls) == 1 assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [ "test-comp==1.0.0" ] # Now clear the history and so we try again async_clear_install_history(hass) with pytest.raises(RequirementsNotFound), patch( "homeassistant.util.package.is_installed", return_value=False) as mock_is_installed, patch( "homeassistant.util.package.install_package", side_effect=_mock_install_package) as mock_inst: integration = await async_get_integration_with_requirements( hass, "test_component") assert integration assert integration.domain == "test_component" assert len(mock_is_installed.mock_calls) == 3 assert sorted(mock_call[1][0] for mock_call in mock_is_installed.mock_calls) == [ "test-comp-after-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp==1.0.0", ] assert len(mock_inst.mock_calls) == 7 assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [ "test-comp-after-dep==1.0.0", "test-comp-after-dep==1.0.0", "test-comp-after-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp==1.0.0", ] # Now clear the history and mock success async_clear_install_history(hass) with patch("homeassistant.util.package.is_installed", return_value=False) as mock_is_installed, patch( "homeassistant.util.package.install_package", return_value=True) as mock_inst: integration = await async_get_integration_with_requirements( hass, "test_component") assert integration assert integration.domain == "test_component" assert len(mock_is_installed.mock_calls) == 3 assert sorted(mock_call[1][0] for mock_call in mock_is_installed.mock_calls) == [ "test-comp-after-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp==1.0.0", ] assert len(mock_inst.mock_calls) == 3 assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [ "test-comp-after-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp==1.0.0", ]
def mock_test_component(): """Ensure a component called 'test' exists.""" set_component('test', MockModule('test'))
async def test_options_flow_with_invalid_data(hass, client): """Test an options flow with invalid_data.""" mock_integration( hass, MockModule("test", async_setup_entry=AsyncMock(return_value=True))) class TestFlow(core_ce.ConfigFlow): @staticmethod @callback def async_get_options_flow(config_entry): class OptionsFlowHandler(data_entry_flow.FlowHandler): async def async_step_init(self, user_input=None): return self.async_show_form( step_id="finish", data_schema=vol.Schema({ vol.Required("choices", default=["invalid", "valid"]): cv.multi_select({"valid": "Valid"}) }), ) async def async_step_finish(self, user_input=None): return self.async_create_entry(title="Enable disable", data=user_input) return OptionsFlowHandler() MockConfigEntry( domain="test", entry_id="test1", source="bla", ).add_to_hass(hass) entry = hass.config_entries.async_entries()[0] with patch.dict(HANDLERS, {"test": TestFlow}): url = "/api/config/config_entries/options/flow" resp = await client.post(url, json={"handler": entry.entry_id}) assert resp.status == HTTPStatus.OK data = await resp.json() flow_id = data.pop("flow_id") assert data == { "type": "form", "handler": "test1", "step_id": "finish", "data_schema": [{ "default": ["invalid", "valid"], "name": "choices", "options": { "valid": "Valid" }, "required": True, "type": "multi_select", }], "description_placeholders": None, "errors": None, "last_step": None, } with patch.dict(HANDLERS, {"test": TestFlow}): resp = await client.post( f"/api/config/config_entries/options/flow/{flow_id}", json={"choices": ["valid", "invalid"]}, ) assert resp.status == HTTPStatus.BAD_REQUEST data = await resp.json() assert data == { "message": "User input malformed: invalid is not a valid option for " "dictionary value @ data['choices']" }
def mock_test_component(hass): """Ensure a component called 'test' exists.""" mock_integration(hass, MockModule("test"))
async def test_get_integration_with_requirements_pip_install_fails_two_passes( opp): """Check getting an integration with loaded requirements and the pip install fails two passes.""" opp.config.skip_pip = False mock_integration( opp, MockModule("test_component_dep", requirements=["test-comp-dep==1.0.0"])) mock_integration( opp, MockModule("test_component_after_dep", requirements=["test-comp-after-dep==1.0.0"]), ) mock_integration( opp, MockModule( "test_component", requirements=["test-comp==1.0.0"], dependencies=["test_component_dep"], partial_manifest={ "after_dependencies": ["test_component_after_dep"] }, ), ) def _mock_install_package(package, **kwargs): if package == "test-comp==1.0.0": return True return False # 1st pass with pytest.raises(RequirementsNotFound), patch( "openpeerpower.util.package.is_installed", return_value=False) as mock_is_installed, patch( "openpeerpower.util.package.install_package", side_effect=_mock_install_package) as mock_inst: integration = await async_get_integration_with_requirements( opp, "test_component") assert integration assert integration.domain == "test_component" assert len(mock_is_installed.mock_calls) == 3 assert sorted(mock_call[1][0] for mock_call in mock_is_installed.mock_calls) == [ "test-comp-after-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp==1.0.0", ] assert len(mock_inst.mock_calls) == 3 assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [ "test-comp-after-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp==1.0.0", ] # 2nd pass with pytest.raises(RequirementsNotFound), patch( "openpeerpower.util.package.is_installed", return_value=False) as mock_is_installed, patch( "openpeerpower.util.package.install_package", side_effect=_mock_install_package) as mock_inst: integration = await async_get_integration_with_requirements( opp, "test_component") assert integration assert integration.domain == "test_component" assert len(mock_is_installed.mock_calls) == 3 assert sorted(mock_call[1][0] for mock_call in mock_is_installed.mock_calls) == [ "test-comp-after-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp==1.0.0", ] assert len(mock_inst.mock_calls) == 3 assert sorted(mock_call[1][0] for mock_call in mock_inst.mock_calls) == [ "test-comp-after-dep==1.0.0", "test-comp-dep==1.0.0", "test-comp==1.0.0", ]
async def test_get_entries(hass, client, clear_handlers): """Test get entries.""" mock_integration(hass, MockModule("comp1")) mock_integration( hass, MockModule("comp2", partial_manifest={"integration_type": "helper"})) mock_integration(hass, MockModule("comp3")) @HANDLERS.register("comp1") class Comp1ConfigFlow: """Config flow with options flow.""" @staticmethod @callback def async_get_options_flow(config_entry): """Get options flow.""" pass @classmethod @callback def async_supports_options_flow(cls, config_entry): """Return options flow support for this handler.""" return True hass.helpers.config_entry_flow.register_discovery_flow( "comp2", "Comp 2", lambda: None) entry = MockConfigEntry( domain="comp1", title="Test 1", source="bla", ) entry.supports_unload = True entry.add_to_hass(hass) MockConfigEntry( domain="comp2", title="Test 2", source="bla2", state=core_ce.ConfigEntryState.SETUP_ERROR, reason="Unsupported API", ).add_to_hass(hass) MockConfigEntry( domain="comp3", title="Test 3", source="bla3", disabled_by=core_ce.ConfigEntryDisabler.USER, ).add_to_hass(hass) resp = await client.get("/api/config/config_entries/entry") assert resp.status == HTTPStatus.OK data = await resp.json() for entry in data: entry.pop("entry_id") assert data == [ { "domain": "comp1", "title": "Test 1", "source": "bla", "state": core_ce.ConfigEntryState.NOT_LOADED.value, "supports_options": True, "supports_remove_device": False, "supports_unload": True, "pref_disable_new_entities": False, "pref_disable_polling": False, "disabled_by": None, "reason": None, }, { "domain": "comp2", "title": "Test 2", "source": "bla2", "state": core_ce.ConfigEntryState.SETUP_ERROR.value, "supports_options": False, "supports_remove_device": False, "supports_unload": False, "pref_disable_new_entities": False, "pref_disable_polling": False, "disabled_by": None, "reason": "Unsupported API", }, { "domain": "comp3", "title": "Test 3", "source": "bla3", "state": core_ce.ConfigEntryState.NOT_LOADED.value, "supports_options": False, "supports_remove_device": False, "supports_unload": False, "pref_disable_new_entities": False, "pref_disable_polling": False, "disabled_by": core_ce.ConfigEntryDisabler.USER, "reason": None, }, ] resp = await client.get("/api/config/config_entries/entry?domain=comp3") assert resp.status == HTTPStatus.OK data = await resp.json() assert len(data) == 1 assert data[0]["domain"] == "comp3" resp = await client.get( "/api/config/config_entries/entry?domain=comp3&type=helper") assert resp.status == HTTPStatus.OK data = await resp.json() assert len(data) == 0 resp = await client.get( "/api/config/config_entries/entry?domain=comp3&type=integration") assert resp.status == HTTPStatus.OK data = await resp.json() assert len(data) == 1 resp = await client.get("/api/config/config_entries/entry?type=integration" ) assert resp.status == HTTPStatus.OK data = await resp.json() assert len(data) == 2 assert data[0]["domain"] == "comp1" assert data[1]["domain"] == "comp3"
async def test_component_failing_setup(hass): """Test component that fails setup.""" mock_integration(hass, MockModule("comp", setup=lambda hass, config: False)) assert not await setup.async_setup_component(hass, "comp", {}) assert "comp" not in hass.config.components
async def test_remove_config_entry_from_device_fails(hass, hass_ws_client): """Test removing config entry from device failing cases.""" assert await async_setup_component(hass, "config", {}) ws_client = await hass_ws_client(hass) device_registry = mock_device_registry(hass) async def async_remove_config_entry_device(hass, config_entry, device_entry): return True mock_integration( hass, MockModule("comp1"), ) mock_integration( hass, MockModule( "comp2", async_remove_config_entry_device=async_remove_config_entry_device), ) entry_1 = MockConfigEntry( domain="comp1", title="Test 1", source="bla", ) entry_1.add_to_hass(hass) entry_2 = MockConfigEntry( domain="comp2", title="Test 1", source="bla", ) entry_2.supports_remove_device = True entry_2.add_to_hass(hass) entry_3 = MockConfigEntry( domain="comp3", title="Test 1", source="bla", ) entry_3.supports_remove_device = True entry_3.add_to_hass(hass) device_registry.async_get_or_create( config_entry_id=entry_1.entry_id, connections={(helpers_dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, ) device_registry.async_get_or_create( config_entry_id=entry_2.entry_id, connections={(helpers_dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, ) device_entry = device_registry.async_get_or_create( config_entry_id=entry_3.entry_id, connections={(helpers_dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, ) assert device_entry.config_entries == { entry_1.entry_id, entry_2.entry_id, entry_3.entry_id, } fake_entry_id = "abc123" assert entry_1.entry_id != fake_entry_id fake_device_id = "abc123" assert device_entry.id != fake_device_id # Try removing a non existing config entry from the device await ws_client.send_json({ "id": 5, "type": "config/device_registry/remove_config_entry", "config_entry_id": fake_entry_id, "device_id": device_entry.id, }) response = await ws_client.receive_json() assert not response["success"] assert response["error"]["code"] == "unknown_error" assert response["error"]["message"] == "Unknown config entry" # Try removing a config entry which does not support removal from the device await ws_client.send_json({ "id": 6, "type": "config/device_registry/remove_config_entry", "config_entry_id": entry_1.entry_id, "device_id": device_entry.id, }) response = await ws_client.receive_json() assert not response["success"] assert response["error"]["code"] == "unknown_error" assert (response["error"]["message"] == "Config entry does not support device removal") # Try removing a config entry from a device which does not exist await ws_client.send_json({ "id": 7, "type": "config/device_registry/remove_config_entry", "config_entry_id": entry_2.entry_id, "device_id": fake_device_id, }) response = await ws_client.receive_json() assert not response["success"] assert response["error"]["code"] == "unknown_error" assert response["error"]["message"] == "Unknown device" # Try removing a config entry from a device which it's not connected to await ws_client.send_json({ "id": 8, "type": "config/device_registry/remove_config_entry", "config_entry_id": entry_2.entry_id, "device_id": device_entry.id, }) response = await ws_client.receive_json() assert response["success"] assert set(response["result"]["config_entries"]) == { entry_1.entry_id, entry_3.entry_id, } await ws_client.send_json({ "id": 9, "type": "config/device_registry/remove_config_entry", "config_entry_id": entry_2.entry_id, "device_id": device_entry.id, }) response = await ws_client.receive_json() assert not response["success"] assert response["error"]["code"] == "unknown_error" assert response["error"]["message"] == "Config entry not in device" # Try removing a config entry which can't be loaded from a device - allowed await ws_client.send_json({ "id": 10, "type": "config/device_registry/remove_config_entry", "config_entry_id": entry_3.entry_id, "device_id": device_entry.id, }) response = await ws_client.receive_json() assert not response["success"] assert response["error"]["code"] == "unknown_error" assert response["error"]["message"] == "Integration not found"
async def test_remove_config_entry_from_device(hass, hass_ws_client): """Test removing config entry from device.""" assert await async_setup_component(hass, "config", {}) ws_client = await hass_ws_client(hass) device_registry = mock_device_registry(hass) can_remove = False async def async_remove_config_entry_device(hass, config_entry, device_entry): return can_remove mock_integration( hass, MockModule( "comp1", async_remove_config_entry_device=async_remove_config_entry_device), ) mock_integration( hass, MockModule( "comp2", async_remove_config_entry_device=async_remove_config_entry_device), ) entry_1 = MockConfigEntry( domain="comp1", title="Test 1", source="bla", ) entry_1.supports_remove_device = True entry_1.add_to_hass(hass) entry_2 = MockConfigEntry( domain="comp1", title="Test 1", source="bla", ) entry_2.supports_remove_device = True entry_2.add_to_hass(hass) device_registry.async_get_or_create( config_entry_id=entry_1.entry_id, connections={(helpers_dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, ) device_entry = device_registry.async_get_or_create( config_entry_id=entry_2.entry_id, connections={(helpers_dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, ) assert device_entry.config_entries == {entry_1.entry_id, entry_2.entry_id} # Try removing a config entry from the device, it should fail because # async_remove_config_entry_device returns False await ws_client.send_json({ "id": 5, "type": "config/device_registry/remove_config_entry", "config_entry_id": entry_1.entry_id, "device_id": device_entry.id, }) response = await ws_client.receive_json() assert not response["success"] assert response["error"]["code"] == "unknown_error" # Make async_remove_config_entry_device return True can_remove = True # Remove the 1st config entry await ws_client.send_json({ "id": 6, "type": "config/device_registry/remove_config_entry", "config_entry_id": entry_1.entry_id, "device_id": device_entry.id, }) response = await ws_client.receive_json() assert response["success"] assert response["result"]["config_entries"] == [entry_2.entry_id] # Check that the config entry was removed from the device assert device_registry.async_get( device_entry.id).config_entries == {entry_2.entry_id} # Remove the 2nd config entry await ws_client.send_json({ "id": 7, "type": "config/device_registry/remove_config_entry", "config_entry_id": entry_2.entry_id, "device_id": device_entry.id, }) response = await ws_client.receive_json() assert response["success"] assert response["result"] is None # This was the last config entry, the device is removed assert not device_registry.async_get(device_entry.id)
async def test_two_step_options_flow(opp, client): """Test we can finish a two step options flow.""" mock_integration( opp, MockModule("test", async_setup_entry=AsyncMock(return_value=True)) ) class TestFlow(core_ce.ConfigFlow): @staticmethod @callback def async_get_options_flow(config_entry): class OptionsFlowHandler(data_entry_flow.FlowHandler): async def async_step_init(self, user_input=None): return self.async_show_form( step_id="finish", data_schema=vol.Schema({"enabled": bool}) ) async def async_step_finish(self, user_input=None): return self.async_create_entry( title="Enable disable", data=user_input ) return OptionsFlowHandler() MockConfigEntry( domain="test", entry_id="test1", source="bla", ).add_to_opp(opp) entry = opp.config_entries.async_entries()[0] with patch.dict(HANDLERS, {"test": TestFlow}): url = "/api/config/config_entries/options/flow" resp = await client.post(url, json={"handler": entry.entry_id}) assert resp.status == 200 data = await resp.json() flow_id = data.pop("flow_id") assert data == { "type": "form", "handler": "test1", "step_id": "finish", "data_schema": [{"name": "enabled", "type": "boolean"}], "description_placeholders": None, "errors": None, "last_step": None, } with patch.dict(HANDLERS, {"test": TestFlow}): resp = await client.post( f"/api/config/config_entries/options/flow/{flow_id}", json={"enabled": True}, ) assert resp.status == 200 data = await resp.json() data.pop("flow_id") assert data == { "handler": "test1", "type": "create_entry", "title": "Enable disable", "version": 1, "description": None, "description_placeholders": None, }
async def test_two_step_options_flow(hass, client): """Test we can finish a two step options flow.""" mock_integration( hass, MockModule('test', async_setup_entry=mock_coro_func(True))) class TestFlow(core_ce.ConfigFlow): @staticmethod @callback def async_get_options_flow(config, options): class OptionsFlowHandler(data_entry_flow.FlowHandler): def __init__(self, config, options): self.config = config self.options = options async def async_step_init(self, user_input=None): return self.async_show_form(step_id='finish', data_schema=vol.Schema( {'enabled': bool})) async def async_step_finish(self, user_input=None): return self.async_create_entry(title='Enable disable', data=user_input) return OptionsFlowHandler(config, options) MockConfigEntry( domain='test', entry_id='test1', source='bla', connection_class=core_ce.CONN_CLASS_LOCAL_POLL, ).add_to_hass(hass) entry = hass.config_entries._entries[0] with patch.dict(HANDLERS, {'test': TestFlow}): url = '/api/config/config_entries/entry/option/flow' resp = await client.post(url, json={'handler': entry.entry_id}) assert resp.status == 200 data = await resp.json() flow_id = data.pop('flow_id') assert data == { 'type': 'form', 'handler': 'test1', 'step_id': 'finish', 'data_schema': [{ 'name': 'enabled', 'type': 'boolean' }], 'description_placeholders': None, 'errors': None } with patch.dict(HANDLERS, {'test': TestFlow}): resp = await client.post( '/api/config/config_entries/options/flow/{}'.format(flow_id), json={'enabled': True}) assert resp.status == 200 data = await resp.json() data.pop('flow_id') assert data == { 'handler': 'test1', 'type': 'create_entry', 'title': 'Enable disable', 'version': 1, 'description': None, 'description_placeholders': None, }
async def test_remove_entry(hass, manager): """Test that we can remove an entry.""" async def mock_setup_entry(hass, entry): """Mock setting up entry.""" hass.async_create_task(hass.config_entries.async_forward_entry_setup( entry, 'light')) return True async def mock_unload_entry(hass, entry): """Mock unloading an entry.""" result = await hass.config_entries.async_forward_entry_unload( entry, 'light') assert result return result mock_remove_entry = MagicMock( side_effect=lambda *args, **kwargs: mock_coro()) entity = MockEntity( unique_id='1234', name='Test Entity', ) async def mock_setup_entry_platform(hass, entry, async_add_entities): """Mock setting up platform.""" async_add_entities([entity]) mock_integration(hass, MockModule( 'test', async_setup_entry=mock_setup_entry, async_unload_entry=mock_unload_entry, async_remove_entry=mock_remove_entry )) mock_entity_platform( hass, 'light.test', MockPlatform(async_setup_entry=mock_setup_entry_platform)) mock_entity_platform(hass, 'config_flow.test', None) MockConfigEntry( domain='test_other', entry_id='test1' ).add_to_manager(manager) entry = MockConfigEntry( domain='test', entry_id='test2', ) entry.add_to_manager(manager) MockConfigEntry( domain='test_other', entry_id='test3' ).add_to_manager(manager) # Check all config entries exist assert [item.entry_id for item in manager.async_entries()] == \ ['test1', 'test2', 'test3'] # Setup entry await entry.async_setup(hass) await hass.async_block_till_done() # Check entity state got added assert hass.states.get('light.test_entity') is not None # Group all_lights, light.test_entity assert len(hass.states.async_all()) == 2 # Check entity got added to entity registry ent_reg = await hass.helpers.entity_registry.async_get_registry() assert len(ent_reg.entities) == 1 entity_entry = list(ent_reg.entities.values())[0] assert entity_entry.config_entry_id == entry.entry_id # Remove entry result = await manager.async_remove('test2') await hass.async_block_till_done() # Check that unload went well and so no need to restart assert result == { 'require_restart': False } # Check the remove callback was invoked. assert mock_remove_entry.call_count == 1 # Check that config entry was removed. assert [item.entry_id for item in manager.async_entries()] == \ ['test1', 'test3'] # Check that entity state has been removed assert hass.states.get('light.test_entity') is None # Just Group all_lights assert len(hass.states.async_all()) == 1 # Check that entity registry entry has been removed entity_entry_list = list(ent_reg.entities.values()) assert not entity_entry_list
def test_saving_and_loading(hass): """Test that we're saving and loading correctly.""" loader.set_component(hass, 'test', MockModule('test')) class TestFlow(data_entry_flow.FlowHandler): VERSION = 5 @asyncio.coroutine def async_step_init(self, user_input=None): return self.async_create_entry( title='Test Title', data={ 'token': 'abcd' } ) with patch.dict(config_entries.HANDLERS, {'test': TestFlow}): yield from hass.config_entries.flow.async_init('test') class Test2Flow(data_entry_flow.FlowHandler): VERSION = 3 @asyncio.coroutine def async_step_init(self, user_input=None): return self.async_create_entry( title='Test 2 Title', data={ 'username': '******' } ) json_path = 'homeassistant.util.json.open' with patch('homeassistant.config_entries.HANDLERS.get', return_value=Test2Flow), \ patch.object(config_entries, 'SAVE_DELAY', 0): yield from hass.config_entries.flow.async_init('test') with patch(json_path, mock_open(), create=True) as mock_write: # To trigger the call_later yield from asyncio.sleep(0, loop=hass.loop) # To execute the save yield from hass.async_block_till_done() # Mock open calls are: open file, context enter, write, context leave written = mock_write.mock_calls[2][1][0] # Now load written data in new config manager manager = config_entries.ConfigEntries(hass, {}) with patch('os.path.isfile', return_value=True), \ patch(json_path, mock_open(read_data=written), create=True): yield from manager.async_load() # Ensure same order for orig, loaded in zip(hass.config_entries.async_entries(), manager.async_entries()): assert orig.version == loaded.version assert orig.domain == loaded.domain assert orig.title == loaded.title assert orig.data == loaded.data assert orig.source == loaded.source
async def test_two_step_options_flow(hass, client): """Test we can finish a two step options flow.""" mock_integration( hass, MockModule("test", async_setup_entry=mock_coro_func(True))) class TestFlow(core_ce.ConfigFlow): @staticmethod @callback def async_get_options_flow(config, options): class OptionsFlowHandler(data_entry_flow.FlowHandler): def __init__(self, config, options): self.config = config self.options = options async def async_step_init(self, user_input=None): return self.async_show_form(step_id="finish", data_schema=vol.Schema( {"enabled": bool})) async def async_step_finish(self, user_input=None): return self.async_create_entry(title="Enable disable", data=user_input) return OptionsFlowHandler(config, options) MockConfigEntry( domain="test", entry_id="test1", source="bla", connection_class=core_ce.CONN_CLASS_LOCAL_POLL, ).add_to_hass(hass) entry = hass.config_entries._entries[0] with patch.dict(HANDLERS, {"test": TestFlow}): url = "/api/config/config_entries/entry/option/flow" resp = await client.post(url, json={"handler": entry.entry_id}) assert resp.status == 200 data = await resp.json() flow_id = data.pop("flow_id") assert data == { "type": "form", "handler": "test1", "step_id": "finish", "data_schema": [{ "name": "enabled", "type": "boolean" }], "description_placeholders": None, "errors": None, } with patch.dict(HANDLERS, {"test": TestFlow}): resp = await client.post( "/api/config/config_entries/options/flow/{}".format(flow_id), json={"enabled": True}, ) assert resp.status == 200 data = await resp.json() data.pop("flow_id") assert data == { "handler": "test1", "type": "create_entry", "title": "Enable disable", "version": 1, "description": None, "description_placeholders": None, }
def test_validate_platform_config(self): """Test validating platform configuration.""" platform_schema = PLATFORM_SCHEMA.extend({ 'hello': str, }) loader.set_component( 'platform_conf', MockModule('platform_conf', platform_schema=platform_schema)) loader.set_component( 'platform_conf.whatever', MockPlatform('whatever')) assert not bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'hello': 'world', 'invalid': 'extra', } }) assert not bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'whatever', 'hello': 'world', }, 'platform_conf 2': { 'invalid': True } }) assert not bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'not_existing', 'hello': 'world', } }) assert bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'whatever', 'hello': 'world', } }) self.hass.config.components.remove('platform_conf') assert bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': [{ 'platform': 'whatever', 'hello': 'world', }] }) self.hass.config.components.remove('platform_conf') # Any falsey paltform config will be ignored (None, {}, etc) assert bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': None }) self.hass.config.components.remove('platform_conf') assert bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': {} })