async def test_entry_unload_invalid_state(hass, manager, state): """Test that we cannot unload an entry with invalid state.""" entry = MockConfigEntry(domain="comp", state=state) entry.add_to_hass(hass) async_unload_entry = MagicMock(return_value=mock_coro(True)) mock_integration(hass, MockModule("comp", async_unload_entry=async_unload_entry)) with pytest.raises(config_entries.OperationNotAllowed): assert await manager.async_unload(entry.entry_id) assert len(async_unload_entry.mock_calls) == 0 assert entry.state == state
async def test_discovery_requirements_mqtt(hass): """Test that we load discovery requirements.""" hass.config.skip_pip = False mqtt = await loader.async_get_integration(hass, "mqtt") mock_integration( hass, MockModule("mqtt_comp", partial_manifest={"mqtt": ["foo/discovery"]}) ) with patch( "homeassistant.requirements.async_process_requirements", ) as mock_process: await async_get_integration_with_requirements(hass, "mqtt_comp") assert len(mock_process.mock_calls) == 2 # mqtt also depends on http assert mock_process.mock_calls[0][1][2] == mqtt.requirements
async def test_get_built_in_integration_with_missing_after_dependencies(opp): """Check getting a built_in integration with missing after_dependencies results in exception.""" opp.config.skip_pip = False mock_integration( opp, MockModule( "test_component", partial_manifest={ "after_dependencies": ["test_component_after_dep"] }, ), built_in=True, ) with pytest.raises(loader.IntegrationNotFound): await async_get_integration_with_requirements(opp, "test_component")
def test_requirement_installed_in_deps(self, mock_install, mock_denv, mock_venv, mock_dirname): """Test requirement installed in deps directory.""" mock_dirname.return_value = 'ha_package_path' self.hass.config.skip_pip = False mock_integration(self.hass, MockModule('comp', requirements=['package==0.0.1'])) assert setup.setup_component(self.hass, 'comp', {}) assert 'comp' in self.hass.config.components assert mock_install.call_args == call( 'package==0.0.1', target=self.hass.config.path('deps'), constraints=os.path.join('ha_package_path', CONSTRAINT_FILE), no_cache_dir=False, )
def test_requirement_installed_in_deps( self, mock_install, mock_denv, mock_venv, mock_dirname ): """Test requirement installed in deps directory.""" mock_dirname.return_value = "ha_package_path" self.hass.config.skip_pip = False mock_integration(self.hass, MockModule("comp", requirements=["package==0.0.1"])) assert setup.setup_component(self.hass, "comp", {}) assert "comp" in self.hass.config.components assert mock_install.call_args == call( "package==0.0.1", target=self.hass.config.path("deps"), constraints=os.path.join("ha_package_path", CONSTRAINT_FILE), no_cache_dir=False, )
async def test_forward_entry_does_not_setup_entry_if_setup_fails(hass): """Test we do not set up entry if component setup fails.""" entry = MockConfigEntry(domain='original') mock_setup = MagicMock(return_value=mock_coro(False)) mock_setup_entry = MagicMock() mock_integration(hass, MockModule( 'forwarded', async_setup=mock_setup, async_setup_entry=mock_setup_entry, )) await hass.config_entries.async_forward_entry_setup(entry, 'forwarded') assert len(mock_setup.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 0
async def test_component_warn_slow_setup(hass): """Warn we log when a component setup takes a long time.""" mock_integration(hass, MockModule("test_component1")) with mock.patch.object(hass.loop, "call_later", mock.MagicMock()) as mock_call: result = await setup.async_setup_component(hass, "test_component1", {}) assert result assert mock_call.called assert len(mock_call.mock_calls) == 3 timeout, logger_method = mock_call.mock_calls[0][1][:2] assert timeout == setup.SLOW_SETUP_WARNING assert logger_method == setup._LOGGER.warning assert mock_call().cancel.called
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
async def test_create_account(hass, client, enable_custom_integrations): """Test a flow that creates an account.""" mock_entity_platform(hass, "config_flow.test", None) mock_integration( hass, MockModule("test", async_setup_entry=AsyncMock(return_value=True))) class TestFlow(core_ce.ConfigFlow): VERSION = 1 async def async_step_user(self, user_input=None): return self.async_create_entry(title="Test Entry", 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 == HTTPStatus.OK entries = hass.config_entries.async_entries("test") assert len(entries) == 1 data = await resp.json() data.pop("flow_id") assert data == { "handler": "test", "title": "Test Entry", "type": "create_entry", "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": "Test Entry", "reason": None, }, "description": None, "description_placeholders": None, "options": {}, }
async def test_entry_unload_failed_to_load(hass, manager, state): """Test that we can unload an entry.""" entry = MockConfigEntry( domain='comp', state=state, ) entry.add_to_hass(hass) async_unload_entry = MagicMock(return_value=mock_coro(True)) mock_integration(hass, MockModule('comp', async_unload_entry=async_unload_entry)) assert await manager.async_unload(entry.entry_id) assert len(async_unload_entry.mock_calls) == 0 assert entry.state == config_entries.ENTRY_STATE_NOT_LOADED
async def test_hardware_info_fail(hass: HomeAssistant, hass_ws_client, os_info) -> None: """Test async_info raises if os_info is not as expected.""" mock_integration(hass, MockModule("hassio")) hass.data[DATA_OS_INFO] = os_info assert await async_setup_component(hass, DOMAIN, {}) client = await hass_ws_client(hass) await client.send_json({"id": 1, "type": "hardware/info"}) msg = await client.receive_json() assert msg["id"] == 1 assert msg["success"] assert msg["result"] == {"hardware": []}
async def test_call_async_migrate_entry_failure_not_supported(hass): """Test migration fails if async_migrate_entry not implemented.""" entry = MockConfigEntry(domain="comp") entry.version = 2 entry.add_to_hass(hass) mock_setup_entry = MagicMock(return_value=mock_coro(True)) mock_integration(hass, MockModule("comp", async_setup_entry=mock_setup_entry)) mock_entity_platform(hass, "config_flow.comp", None) result = await async_setup_component(hass, "comp", {}) assert result assert len(mock_setup_entry.mock_calls) == 0 assert entry.state == config_entries.ENTRY_STATE_MIGRATION_ERROR
async def test_platform_error_slow_setup(hass, caplog): """Don't block startup more than SLOW_SETUP_MAX_WAIT.""" with patch.object(setup, "SLOW_SETUP_MAX_WAIT", 0.1): called = [] async def async_setup(*args): """Tracking Setup.""" called.append(1) await asyncio.sleep(2) mock_integration(hass, MockModule("test_component1", async_setup=async_setup)) result = await setup.async_setup_component(hass, "test_component1", {}) assert len(called) == 1 assert not result assert "test_component1 is taking longer than 0.1 seconds" in caplog.text
async def test_get_custom_integration_with_missing_after_dependencies(hass): """Check getting a custom integration with missing after_dependencies.""" hass.config.skip_pip = False mock_integration( hass, MockModule( "test_custom_component", partial_manifest={"after_dependencies": ["test_component_after_dep"]}, ), built_in=False, ) integration = await async_get_integration_with_requirements( hass, "test_custom_component" ) assert integration assert integration.domain == "test_custom_component"
async def test_discovery_requirements_zeroconf(hass, partial_manifest): """Test that we load discovery requirements.""" hass.config.skip_pip = False zeroconf = await loader.async_get_integration(hass, "zeroconf") mock_integration( hass, MockModule("comp", partial_manifest=partial_manifest), ) with patch("homeassistant.requirements.async_process_requirements", ) as mock_process: await async_get_integration_with_requirements(hass, "comp") assert len(mock_process.mock_calls) == 2 # zeroconf also depends on http assert mock_process.mock_calls[0][1][2] == zeroconf.requirements
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
async def test_setup_after_deps_via_platform(hass): """Test after_dependencies set up via platform.""" order = [] after_dep_event = asyncio.Event() def gen_domain_setup(domain): async def async_setup(hass, config): if domain == "after_dep_of_platform_int": await after_dep_event.wait() order.append(domain) return True return async_setup mock_integration( hass, MockModule( domain="after_dep_of_platform_int", async_setup=gen_domain_setup("after_dep_of_platform_int"), ), ) mock_integration( hass, MockModule( domain="platform_int", async_setup=gen_domain_setup("platform_int"), partial_manifest={"after_dependencies": ["after_dep_of_platform_int"]}, ), ) mock_entity_platform(hass, "light.platform_int", MockPlatform()) @core.callback def continue_loading(_): """When light component loaded, continue other loading.""" after_dep_event.set() hass.bus.async_listen_once("component_loaded", continue_loading) await bootstrap._async_set_up_integrations( hass, {"light": {"platform": "platform_int"}, "after_dep_of_platform_int": {}} ) assert "light" in hass.config.components assert "after_dep_of_platform_int" in hass.config.components assert "platform_int" in hass.config.components assert order == ["after_dep_of_platform_int", "platform_int"]
async def test_setup_reload_service_when_async_process_component_config_fails( hass): """Test setting up a reload service with the config processing failing.""" component_setup = Mock(return_value=True) setup_called = [] async def setup_platform(*args): setup_called.append(args) mock_integration(hass, MockModule(DOMAIN, setup=component_setup)) mock_integration(hass, MockModule(PLATFORM, dependencies=[DOMAIN])) mock_platform = MockPlatform(async_setup_platform=setup_platform) mock_entity_platform(hass, f"{DOMAIN}.{PLATFORM}", mock_platform) component = EntityComponent(_LOGGER, DOMAIN, hass) await component.async_setup( {DOMAIN: { "platform": PLATFORM, "sensors": None }}) await hass.async_block_till_done() assert component_setup.called assert f"{DOMAIN}.{PLATFORM}" in hass.config.components assert len(setup_called) == 1 await async_setup_reload_service(hass, PLATFORM, [DOMAIN]) yaml_path = path.join( _get_fixtures_base_path(), "fixtures", "helpers/reload_configuration.yaml", ) with patch.object(config, "YAML_CONFIG_FILE", yaml_path), patch.object( config, "async_process_component_config", return_value=None): await hass.services.async_call( PLATFORM, SERVICE_RELOAD, {}, blocking=True, ) await hass.async_block_till_done() assert len(setup_called) == 1
async def test_continue_flow_unauth(hass, client, hass_admin_user): """Test we can't finish a two step flow.""" mock_integration( hass, MockModule('test', async_setup_entry=mock_coro_func(True))) mock_entity_platform(hass, 'config_flow.test', None) class TestFlow(core_ce.ConfigFlow): VERSION = 1 @asyncio.coroutine def async_step_user(self, user_input=None): return self.async_show_form(step_id='account', data_schema=vol.Schema( {'user_title': str})) @asyncio.coroutine 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 } hass_admin_user.groups = [] resp = await client.post( '/api/config/config_entries/flow/{}'.format(flow_id), json={'user_title': 'user-title'}) assert resp.status == 401
async def test_reload_platform(hass): """Test the polling of only updated entities.""" component_setup = Mock(return_value=True) setup_called = [] async def setup_platform(*args): setup_called.append(args) mock_integration(hass, MockModule(DOMAIN, setup=component_setup)) mock_integration(hass, MockModule(PLATFORM, dependencies=[DOMAIN])) mock_platform = MockPlatform(async_setup_platform=setup_platform) mock_entity_platform(hass, f"{DOMAIN}.{PLATFORM}", mock_platform) component = EntityComponent(_LOGGER, DOMAIN, hass) await component.async_setup( {DOMAIN: { "platform": PLATFORM, "sensors": None }}) await hass.async_block_till_done() assert component_setup.called assert f"{DOMAIN}.{PLATFORM}" in hass.config.components assert len(setup_called) == 1 platform = async_get_platform_without_config_entry(hass, PLATFORM, DOMAIN) assert platform.platform_name == PLATFORM assert platform.domain == DOMAIN yaml_path = path.join( _get_fixtures_base_path(), "fixtures", "helpers/reload_configuration.yaml", ) with patch.object(config, "YAML_CONFIG_FILE", yaml_path): await async_reload_integration_platforms(hass, PLATFORM, [DOMAIN]) assert len(setup_called) == 2 existing_platforms = async_get_platforms(hass, PLATFORM) for existing_platform in existing_platforms: existing_platform.config_entry = "abc" assert not async_get_platform_without_config_entry(hass, PLATFORM, DOMAIN)
def test_validate_component_config(self): """Test validating component configuration.""" config_schema = vol.Schema({ 'comp_conf': { 'hello': str } }, required=True) mock_integration( self.hass, MockModule('comp_conf', config_schema=config_schema)) with assert_setup_component(0): assert not setup.setup_component(self.hass, 'comp_conf', {}) self.hass.data.pop(setup.DATA_SETUP) with assert_setup_component(0): assert not setup.setup_component(self.hass, 'comp_conf', { 'comp_conf': None }) self.hass.data.pop(setup.DATA_SETUP) with assert_setup_component(0): assert not setup.setup_component(self.hass, 'comp_conf', { 'comp_conf': {} }) self.hass.data.pop(setup.DATA_SETUP) with assert_setup_component(0): assert not setup.setup_component(self.hass, 'comp_conf', { 'comp_conf': { 'hello': 'world', 'invalid': 'extra', } }) self.hass.data.pop(setup.DATA_SETUP) with assert_setup_component(1): assert setup.setup_component(self.hass, 'comp_conf', { 'comp_conf': { 'hello': 'world', } })
async def test_discovery_requirements_ssdp(hass): """Test that we load discovery requirements.""" hass.config.skip_pip = False ssdp = await loader.async_get_integration(hass, "ssdp") mock_integration( hass, MockModule("ssdp_comp", partial_manifest={"ssdp": [{"st": "roku:ecp"}]}) ) with patch( "homeassistant.requirements.async_process_requirements", ) as mock_process: await async_get_integration_with_requirements(hass, "ssdp_comp") assert len(mock_process.mock_calls) == 4 assert mock_process.mock_calls[0][1][2] == ssdp.requirements # Ensure zeroconf is a dep for ssdp assert mock_process.mock_calls[1][1][1] == "zeroconf"
async def test_forward_entry_sets_up_component(hass): """Test we setup the component entry is forwarded to.""" entry = MockConfigEntry(domain='original') mock_original_setup_entry = MagicMock(return_value=mock_coro(True)) mock_integration( hass, MockModule('original', async_setup_entry=mock_original_setup_entry)) mock_forwarded_setup_entry = MagicMock(return_value=mock_coro(True)) mock_integration( hass, MockModule('forwarded', async_setup_entry=mock_forwarded_setup_entry)) await hass.config_entries.async_forward_entry_setup(entry, 'forwarded') assert len(mock_original_setup_entry.mock_calls) == 0 assert len(mock_forwarded_setup_entry.mock_calls) == 1
async def test_continue_flow_unauth(hass, client, hass_admin_user): """Test we can't finish a two step flow.""" mock_integration( hass, MockModule("test", async_setup_entry=AsyncMock(return_value=True))) mock_entity_platform(hass, "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, } hass_admin_user.groups = [] resp = await client.post( f"/api/config/config_entries/flow/{flow_id}", json={"user_title": "user-title"}, ) assert resp.status == 401
async def test_hardware_info(hass: HomeAssistant, hass_ws_client) -> None: """Test we can get the board info.""" mock_integration(hass, MockModule("hassio")) # Setup the config entry config_entry = MockConfigEntry( data={}, domain=DOMAIN, options={}, title="Hardkernel", ) config_entry.add_to_hass(hass) with patch( "homeassistant.components.hardkernel.get_os_info", return_value={"board": "odroid-n2"}, ): assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() client = await hass_ws_client(hass) with patch( "homeassistant.components.hardkernel.hardware.get_os_info", return_value={"board": "odroid-n2"}, ): await client.send_json({"id": 1, "type": "hardware/info"}) msg = await client.receive_json() assert msg["id"] == 1 assert msg["success"] assert msg["result"] == { "hardware": [ { "board": { "hassio_board_id": "odroid-n2", "manufacturer": "hardkernel", "model": "odroid-n2", "revision": None, }, "dongle": None, "name": "Home Assistant Blue / Hardkernel Odroid-N2", "url": None, } ] }
async def test_setup_dependencies_platform(hass): """Test we setup the dependencies of a platform. We're explicitly testing that we process dependencies even if a component with the same name has already been loaded. """ mock_integration( hass, MockModule("test_component", dependencies=["test_component2"])) mock_integration(hass, MockModule("test_component2")) mock_entity_platform(hass, "test_domain.test_component", MockPlatform()) component = EntityComponent(_LOGGER, DOMAIN, hass) await component.async_setup({DOMAIN: {"platform": "test_component"}}) await hass.async_block_till_done() assert "test_component" in hass.config.components assert "test_component2" in hass.config.components assert "test_domain.test_component" in hass.config.components
async def test_setup_retrying_during_unload(hass): """Test if we unload an entry that is in retry mode.""" entry = MockConfigEntry(domain='test') mock_setup_entry = MagicMock(side_effect=ConfigEntryNotReady) mock_integration( hass, MockModule('test', async_setup_entry=mock_setup_entry)) with patch('homeassistant.helpers.event.async_call_later') as mock_call: await entry.async_setup(hass) assert entry.state == config_entries.ENTRY_STATE_SETUP_RETRY assert len(mock_call.return_value.mock_calls) == 0 await entry.async_unload(hass) assert entry.state == config_entries.ENTRY_STATE_NOT_LOADED assert len(mock_call.return_value.mock_calls) == 1
async def test_discovery_notification_not_created(hass): """Test that we not create a notification when discovery is aborted.""" mock_integration(hass, MockModule('test')) await async_setup_component(hass, 'persistent_notification', {}) class TestFlow(config_entries.ConfigFlow): VERSION = 5 async def async_step_discovery(self, user_input=None): return self.async_abort(reason='test') with patch.dict(config_entries.HANDLERS, {'test': TestFlow}): await hass.config_entries.flow.async_init( 'test', context={'source': config_entries.SOURCE_DISCOVERY}) await hass.async_block_till_done() state = hass.states.get('persistent_notification.config_entry_discovery') assert state is None
async def test_get_integration_with_requirements(hass): """Check getting an integration with loaded requirements.""" hass.config.skip_pip = False mock_integration( hass, MockModule("test_component", requirements=["hello==1.0.0"])) 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) == 1 assert len(mock_inst.mock_calls) == 1
async def test_requirement_installed_in_venv(hass): """Test requirement installed in virtual environment.""" with patch("os.path.dirname", return_value="ha_package_path"), patch( "homeassistant.util.package.is_virtual_env", return_value=True ), patch("homeassistant.util.package.is_docker_env", return_value=False), patch( "homeassistant.util.package.install_package", return_value=True ) as mock_install, patch.dict( os.environ, env_without_wheel_links(), clear=True ): hass.config.skip_pip = False mock_integration(hass, MockModule("comp", requirements=["package==0.0.1"])) assert await setup.async_setup_component(hass, "comp", {}) assert "comp" in hass.config.components assert mock_install.call_args == call( "package==0.0.1", constraints=os.path.join("ha_package_path", CONSTRAINT_FILE), no_cache_dir=False, )