async def test_ignore_flow(hass, hass_ws_client): """Test we can ignore a flow.""" assert await async_setup_component(hass, "config", {}) 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): await self.async_set_unique_id("mock-unique-id") return self.async_show_form(step_id="account", data_schema=vol.Schema({})) ws_client = await hass_ws_client(hass) with patch.dict(HANDLERS, {"test": TestFlow}): result = await hass.config_entries.flow.async_init( "test", context={"source": core_ce.SOURCE_USER} ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM await ws_client.send_json( { "id": 5, "type": "config_entries/ignore_flow", "flow_id": result["flow_id"], "title": "Test Integration", } ) response = await ws_client.receive_json() assert response["success"] assert len(hass.config_entries.flow.async_progress()) == 0 entry = hass.config_entries.async_entries("test")[0] assert entry.source == "ignore" assert entry.unique_id == "mock-unique-id" assert entry.title == "Test Integration"
def test_setup_two_platforms(self): """Test with bad configuration.""" # Test if switch component returns 0 switches test_platform = getattr(self.hass.components, "test.switch") test_platform.init(True) mock_entity_platform(self.hass, "switch.test2", test_platform) test_platform.init(False) assert setup_component( self.hass, switch.DOMAIN, { switch.DOMAIN: { CONF_PLATFORM: "test" }, "{} 2".format(switch.DOMAIN): { CONF_PLATFORM: "test2" }, }, )
async def test_call_async_migrate_entry(hass): """Test we call <component>.async_migrate_entry when version mismatch.""" entry = MockConfigEntry(domain='comp') entry.version = 2 entry.add_to_hass(hass) mock_migrate_entry = MagicMock(return_value=mock_coro(True)) mock_setup_entry = MagicMock(return_value=mock_coro(True)) mock_integration( hass, MockModule('comp', async_setup_entry=mock_setup_entry, async_migrate_entry=mock_migrate_entry)) mock_entity_platform(hass, 'config_flow.comp', None) result = await async_setup_component(hass, 'comp', {}) assert result assert len(mock_migrate_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1 assert entry.state == config_entries.ENTRY_STATE_LOADED
async def test_platform_warn_slow_setup(hass): """Warn we log when platform setup takes a long time.""" platform = MockPlatform() mock_entity_platform(hass, "test_domain.platform", platform) component = EntityComponent(_LOGGER, DOMAIN, hass) with patch.object(hass.loop, "call_later") as mock_call: await component.async_setup({DOMAIN: {"platform": "platform"}}) await hass.async_block_till_done() assert mock_call.called # mock_calls[0] is the warning message for component setup # mock_calls[4] is the warning message for platform setup timeout, logger_method = mock_call.mock_calls[4][1][:2] assert timeout == entity_platform.SLOW_SETUP_WARNING assert logger_method == _LOGGER.warning assert mock_call().cancel.called
def test_set_scan_interval_via_config(self, mock_track): """Test the setting of the scan interval via configuration.""" def platform_setup(hass, config, add_entities, discovery_info=None): """Test the platform setup.""" add_entities([MockEntity(should_poll=True)]) mock_entity_platform(self.hass, 'test_domain.platform', MockPlatform(platform_setup)) component = EntityComponent(_LOGGER, DOMAIN, self.hass) component.setup({ DOMAIN: { 'platform': 'platform', 'scan_interval': timedelta(seconds=30), } }) self.hass.block_till_done() assert mock_track.called assert timedelta(seconds=30) == mock_track.call_args[0][2]
async def test_set_scan_interval_via_config(mock_track, hass): """Test the setting of the scan interval via configuration.""" def platform_setup(hass, config, add_entities, discovery_info=None): """Test the platform setup.""" add_entities([MockEntity(should_poll=True)]) mock_entity_platform(hass, "test_domain.platform", MockPlatform(platform_setup)) component = EntityComponent(_LOGGER, DOMAIN, hass) component.setup({ DOMAIN: { "platform": "platform", "scan_interval": timedelta(seconds=30) } }) await hass.async_block_till_done() assert mock_track.called assert timedelta(seconds=30) == mock_track.call_args[0][2]
async def test_setup_loads_platforms(hass): """Test the loading of the platforms.""" component_setup = Mock(return_value=True) platform_setup = Mock(return_value=None) mock_integration(hass, MockModule("test_component", setup=component_setup)) # mock the dependencies mock_integration(hass, MockModule("mod2", dependencies=["test_component"])) mock_entity_platform(hass, "test_domain.mod2", MockPlatform(platform_setup)) component = EntityComponent(_LOGGER, DOMAIN, hass) assert not component_setup.called assert not platform_setup.called component.setup({DOMAIN: {"platform": "mod2"}}) await hass.async_block_till_done() assert component_setup.called assert platform_setup.called
async def test_set_entity_namespace_via_config(opp): """Test setting an entity namespace.""" def platform_setup(opp, config, add_entities, discovery_info=None): """Test the platform setup.""" add_entities([MockEntity(name="beer"), MockEntity(name=None)]) platform = MockPlatform(platform_setup) mock_entity_platform(opp, "test_domain.platform", platform) component = EntityComponent(_LOGGER, DOMAIN, opp) component.setup({DOMAIN: {"platform": "platform", "entity_namespace": "yummy"}}) await opp.async_block_till_done() assert sorted(opp.states.async_entity_ids()) == [ "test_domain.yummy_beer", "test_domain.yummy_unnamed_device", ]
async def test_setup_entry(hass): """Test setup entry calls async_setup_entry on platform.""" mock_setup_entry = AsyncMock(return_value=True) mock_entity_platform( hass, "test_domain.entry_domain", MockPlatform(async_setup_entry=mock_setup_entry, scan_interval=timedelta(seconds=5)), ) component = EntityComponent(_LOGGER, DOMAIN, hass) entry = MockConfigEntry(domain="entry_domain") assert await component.async_setup_entry(entry) assert len(mock_setup_entry.mock_calls) == 1 p_hass, p_entry, _ = mock_setup_entry.mock_calls[0][1] assert p_hass is hass assert p_entry is entry assert component._platforms[entry.entry_id].scan_interval == timedelta( seconds=5)
async def test_platforms_shutdown_on_stop(hass): """Test that we shutdown platforms on stop.""" platform1_setup = Mock( side_effect=[PlatformNotReady, PlatformNotReady, None]) mock_integration(hass, MockModule("mod1")) mock_entity_platform(hass, "test_domain.mod1", MockPlatform(platform1_setup)) component = EntityComponent(_LOGGER, DOMAIN, hass) await component.async_setup({DOMAIN: {"platform": "mod1"}}) await hass.async_block_till_done() assert len(platform1_setup.mock_calls) == 1 assert "test_domain.mod1" not in hass.config.components with patch.object(component._platforms[DOMAIN], "async_shutdown") as mock_async_shutdown: hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) await hass.async_block_till_done() assert mock_async_shutdown.called
async def test_abort(hass, client): """Test a flow that aborts.""" mock_entity_platform(hass, "config_flow.test", None) class TestFlow(core_ce.ConfigFlow): async def async_step_user(self, user_input=None): return self.async_abort(reason="bla") 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() data.pop("flow_id") assert data == { "description_placeholders": None, "handler": "test", "reason": "bla", "type": "abort", }
def test_setup_dependencies_platform(hass): """Test we setup the dependencies of a platform. We're explictely 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) yield from component.async_setup( {DOMAIN: { 'platform': 'test_component', }}) 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_entry_setup_succeed(hass, manager): """Test that we can setup an entry.""" entry = MockConfigEntry(domain="comp", state=config_entries.ENTRY_STATE_NOT_LOADED) entry.add_to_hass(hass) mock_setup = MagicMock(return_value=mock_coro(True)) mock_setup_entry = MagicMock(return_value=mock_coro(True)) mock_integration( hass, MockModule("comp", async_setup=mock_setup, async_setup_entry=mock_setup_entry), ) mock_entity_platform(hass, "config_flow.comp", None) assert await manager.async_setup(entry.entry_id) assert len(mock_setup.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1 assert entry.state == config_entries.ENTRY_STATE_LOADED
async def test_call_async_migrate_entry_failure_not_bool(hass): """Test migration fails if boolean not returned.""" entry = MockConfigEntry(domain='comp') entry.version = 2 entry.add_to_hass(hass) mock_migrate_entry = MagicMock( return_value=mock_coro()) mock_setup_entry = MagicMock(return_value=mock_coro(True)) mock_integration( hass, MockModule('comp', async_setup_entry=mock_setup_entry, async_migrate_entry=mock_migrate_entry)) mock_entity_platform(hass, 'config_flow.comp', None) result = await async_setup_component(hass, 'comp', {}) assert result assert len(mock_migrate_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 0 assert entry.state == config_entries.ENTRY_STATE_MIGRATION_ERROR
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 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, } 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_parallel_updates_sync_platform(hass): """Test sync platform parallel_updates default set to 1.""" platform = MockPlatform() mock_entity_platform(hass, "test_domain.platform", platform) component = EntityComponent(_LOGGER, DOMAIN, hass) component._platforms = {} await component.async_setup({DOMAIN: {"platform": "platform"}}) handle = list(component._platforms.values())[-1] class SyncEntity(MockEntity): """Mock entity that has update.""" async def update(self): pass entity = SyncEntity() await handle.async_add_entities([entity]) assert entity.parallel_updates is not None assert entity.parallel_updates._value == 1
def test_abort(hass, client): """Test a flow that aborts.""" mock_entity_platform(hass, 'config_flow.test', None) class TestFlow(core_ce.ConfigFlow): @asyncio.coroutine def async_step_user(self, user_input=None): return self.async_abort(reason='bla') with patch.dict(HANDLERS, {'test': TestFlow}): resp = yield from client.post('/api/config/config_entries/flow', json={'handler': 'test'}) assert resp.status == 200 data = yield from resp.json() data.pop('flow_id') assert data == { 'description_placeholders': None, 'handler': 'test', 'reason': 'bla', 'type': 'abort' }
async def test_platform_not_ready(hass, legacy_patchable_time): """Test that we retry when platform not ready.""" platform1_setup = Mock( side_effect=[PlatformNotReady, PlatformNotReady, None]) mock_integration(hass, MockModule("mod1")) mock_entity_platform(hass, "test_domain.mod1", MockPlatform(platform1_setup)) component = EntityComponent(_LOGGER, DOMAIN, hass) await component.async_setup({DOMAIN: {"platform": "mod1"}}) await hass.async_block_till_done() assert len(platform1_setup.mock_calls) == 1 assert "test_domain.mod1" not in hass.config.components utcnow = dt_util.utcnow() with patch("homeassistant.util.dt.utcnow", return_value=utcnow): # Should not trigger attempt 2 async_fire_time_changed(hass, utcnow + timedelta(seconds=29)) await hass.async_block_till_done() assert len(platform1_setup.mock_calls) == 1 # Should trigger attempt 2 async_fire_time_changed(hass, utcnow + timedelta(seconds=30)) await hass.async_block_till_done() assert len(platform1_setup.mock_calls) == 2 assert "test_domain.mod1" not in hass.config.components # This should not trigger attempt 3 async_fire_time_changed(hass, utcnow + timedelta(seconds=59)) await hass.async_block_till_done() assert len(platform1_setup.mock_calls) == 2 # Trigger attempt 3, which succeeds async_fire_time_changed(hass, utcnow + timedelta(seconds=60)) await hass.async_block_till_done() assert len(platform1_setup.mock_calls) == 3 assert "test_domain.mod1" in hass.config.components
async def test_entry_reload_not_loaded(hass, manager, state): """Test that we can reload an entry.""" entry = MockConfigEntry(domain='comp', state=state) entry.add_to_hass(hass) async_setup = MagicMock(return_value=mock_coro(True)) async_setup_entry = MagicMock(return_value=mock_coro(True)) async_unload_entry = MagicMock(return_value=mock_coro(True)) mock_integration( hass, MockModule('comp', async_setup=async_setup, async_setup_entry=async_setup_entry, async_unload_entry=async_unload_entry)) mock_entity_platform(hass, 'config_flow.comp', None) assert await manager.async_reload(entry.entry_id) assert len(async_unload_entry.mock_calls) == 0 assert len(async_setup.mock_calls) == 1 assert len(async_setup_entry.mock_calls) == 1 assert entry.state == config_entries.ENTRY_STATE_LOADED
async def test_unload_entry_resets_platform(hass): """Test unloading an entry removes all entities.""" mock_setup_entry = AsyncMock(return_value=True) mock_entity_platform( hass, "test_domain.entry_domain", MockPlatform(async_setup_entry=mock_setup_entry), ) component = EntityComponent(_LOGGER, DOMAIN, hass) entry = MockConfigEntry(domain="entry_domain") assert await component.async_setup_entry(entry) assert len(mock_setup_entry.mock_calls) == 1 add_entities = mock_setup_entry.mock_calls[0][1][2] add_entities([MockEntity()]) await hass.async_block_till_done() assert len(hass.states.async_entity_ids()) == 1 assert await component.async_unload_entry(entry) assert len(hass.states.async_entity_ids()) == 0
async def test_parallel_updates_async_platform(hass): """Test async platform does not have parallel_updates limit by default.""" platform = MockPlatform() mock_entity_platform(hass, "test_domain.platform", platform) component = EntityComponent(_LOGGER, DOMAIN, hass) component._platforms = {} await component.async_setup({DOMAIN: {"platform": "platform"}}) handle = list(component._platforms.values())[-1] assert handle.parallel_updates is None class AsyncEntity(MockEntity): """Mock entity that has async_update.""" async def async_update(self): pass entity = AsyncEntity() await handle.async_add_entities([entity]) assert entity.parallel_updates is None
async def test_mqtt_integration_discovery_subscribe_unsubscribe( hass, mqtt_client_mock, mqtt_mock_entry_no_yaml_config): """Check MQTT integration discovery subscribe and unsubscribe.""" mqtt_mock = await mqtt_mock_entry_no_yaml_config() mock_entity_platform(hass, "config_flow.comp", None) entry = hass.config_entries.async_entries("mqtt")[0] mqtt_mock().connected = True with patch( "homeassistant.components.mqtt.discovery.async_get_mqtt", return_value={"comp": ["comp/discovery/#"]}, ): await async_start(hass, "homeassistant", entry) await hass.async_block_till_done() mqtt_client_mock.subscribe.assert_any_call("comp/discovery/#", 0) assert not mqtt_client_mock.unsubscribe.called class TestFlow(config_entries.ConfigFlow): """Test flow.""" async def async_step_mqtt(self, discovery_info): """Test mqtt step.""" return self.async_abort(reason="already_configured") with patch.dict(config_entries.HANDLERS, {"comp": TestFlow}): mqtt_client_mock.subscribe.assert_any_call("comp/discovery/#", 0) assert not mqtt_client_mock.unsubscribe.called async_fire_mqtt_message(hass, "comp/discovery/bla/config", "") await hass.async_block_till_done() mqtt_client_mock.unsubscribe.assert_called_once_with( "comp/discovery/#") mqtt_client_mock.unsubscribe.reset_mock() async_fire_mqtt_message(hass, "comp/discovery/bla/config", "") await hass.async_block_till_done() assert not mqtt_client_mock.unsubscribe.called
def test_create_account(hass, client): """Test a flow that creates an account.""" mock_entity_platform(hass, 'config_flow.test', None) mock_integration( hass, MockModule('test', async_setup_entry=mock_coro_func(True))) class TestFlow(core_ce.ConfigFlow): VERSION = 1 @asyncio.coroutine 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 = yield from client.post('/api/config/config_entries/flow', json={'handler': 'test'}) assert resp.status == 200 entries = hass.config_entries.async_entries('test') assert len(entries) == 1 data = yield from resp.json() data.pop('flow_id') assert data == { 'handler': 'test', 'title': 'Test Entry', 'type': 'create_entry', 'version': 1, 'result': entries[0].entry_id, 'description': None, 'description_placeholders': None, }
async def test_unique_id_ignore(hass, manager): """Test that we can ignore flows that are in progress and have a unique ID.""" async_setup_entry = MagicMock(return_value=mock_coro(False)) mock_integration(hass, MockModule("comp", async_setup_entry=async_setup_entry)) mock_entity_platform(hass, "config_flow.comp", None) class TestFlow(config_entries.ConfigFlow): VERSION = 1 async def async_step_user(self, user_input=None): await self.async_set_unique_id("mock-unique-id") return self.async_show_form(step_id="discovery") with patch.dict(config_entries.HANDLERS, {"comp": TestFlow}): # Create one to be in progress result = await manager.flow.async_init( "comp", context={"source": config_entries.SOURCE_USER}) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM result2 = await manager.flow.async_init( "comp", context={"source": config_entries.SOURCE_IGNORE}, data={"unique_id": "mock-unique-id"}, ) assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY # assert len(hass.config_entries.flow.async_progress()) == 0 # We should never set up an ignored entry. assert len(async_setup_entry.mock_calls) == 0 entry = hass.config_entries.async_entries("comp")[0] assert entry.source == "ignore" assert entry.unique_id == "mock-unique-id"
async def test_parallel_updates_async_platform_with_constant(hass): """Test async platform can set parallel_updates limit.""" platform = MockPlatform() platform.PARALLEL_UPDATES = 2 mock_entity_platform(hass, "test_domain.platform", platform) component = EntityComponent(_LOGGER, DOMAIN, hass) component._platforms = {} await component.async_setup({DOMAIN: {"platform": "platform"}}) handle = list(component._platforms.values())[-1] class AsyncEntity(MockEntity): """Mock entity that has async_update.""" async def async_update(self): pass entity = AsyncEntity() await handle.async_add_entities([entity]) assert entity.parallel_updates is not None assert entity.parallel_updates._value == 2
async def test_user_has_confirmation(opp, discovery_flow_conf): """Test user requires confirmation to setup.""" discovery_flow_conf["discovered"] = True mock_entity_platform(opp, "config_flow.test", None) result = await opp.config_entries.flow.async_init( "test", context={"source": config_entries.SOURCE_USER}, data={}) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "confirm" progress = opp.config_entries.flow.async_progress() assert len(progress) == 1 assert progress[0]["flow_id"] == result["flow_id"] assert progress[0]["context"] == { "confirm_only": True, "source": config_entries.SOURCE_USER, "unique_id": "test", } result = await opp.config_entries.flow.async_configure( result["flow_id"], {}) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
async def test_call_setup_entry(hass): """Test we call <component>.setup_entry.""" entry = MockConfigEntry(domain="comp") entry.add_to_hass(hass) mock_setup_entry = MagicMock(return_value=mock_coro(True)) mock_migrate_entry = MagicMock(return_value=mock_coro(True)) mock_integration( hass, MockModule( "comp", async_setup_entry=mock_setup_entry, async_migrate_entry=mock_migrate_entry, ), ) mock_entity_platform(hass, "config_flow.comp", None) result = await async_setup_component(hass, "comp", {}) assert result assert len(mock_migrate_entry.mock_calls) == 0 assert len(mock_setup_entry.mock_calls) == 1 assert entry.state == config_entries.ENTRY_STATE_LOADED
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
def test_platform_warn_slow_setup(hass): """Warn we log when platform setup takes a long time.""" platform = MockPlatform() mock_entity_platform(hass, 'test_domain.platform', platform) component = EntityComponent(_LOGGER, DOMAIN, hass) with patch.object(hass.loop, 'call_later', MagicMock()) \ as mock_call: yield from component.async_setup({DOMAIN: { 'platform': 'platform', }}) assert mock_call.called # mock_calls[0] is the warning message for component setup # mock_calls[3] is the warning message for platform setup timeout, logger_method = mock_call.mock_calls[3][1][:2] assert timeout == entity_platform.SLOW_SETUP_WARNING assert logger_method == _LOGGER.warning assert mock_call().cancel.called
async def test_discovery_notification(hass): """Test that we create/dismiss a notification when source is discovery.""" mock_integration(hass, MockModule('test')) mock_entity_platform(hass, 'config_flow.test', None) await async_setup_component(hass, 'persistent_notification', {}) class TestFlow(config_entries.ConfigFlow): VERSION = 5 async def async_step_discovery(self, user_input=None): if user_input is not None: return self.async_create_entry( title='Test Title', data={ 'token': 'abcd' } ) return self.async_show_form( step_id='discovery', ) with patch.dict(config_entries.HANDLERS, {'test': TestFlow}): result = 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 not None result = await hass.config_entries.flow.async_configure( result['flow_id'], {}) assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY await hass.async_block_till_done() state = hass.states.get('persistent_notification.config_entry_discovery') assert state is None