async def test_extract_from_service_available_device(opp): """Test the extraction of entity from service and device is available.""" component = EntityComponent(_LOGGER, DOMAIN, opp) await component.async_add_entities( [ MockEntity(name="test_1"), MockEntity(name="test_2", available=False), MockEntity(name="test_3"), MockEntity(name="test_4", available=False), ] ) call_1 = ha.ServiceCall("test", "service", data={"entity_id": ENTITY_MATCH_ALL}) assert ["test_domain.test_1", "test_domain.test_3"] == sorted( ent.entity_id for ent in (await component.async_extract_from_service(call_1)) ) call_2 = ha.ServiceCall( "test", "service", data={"entity_id": ["test_domain.test_3", "test_domain.test_4"]}, ) assert ["test_domain.test_3"] == sorted( ent.entity_id for ent in (await component.async_extract_from_service(call_2)) )
async def test_extract_entity_ids_from_area(opp, area_mock): """Test extract_entity_ids method with areas.""" call = ha.ServiceCall("light", "turn_on", {"area_id": "own-area"}) assert { "light.in_own_area", } == await service.async_extract_entity_ids(opp, call) call = ha.ServiceCall("light", "turn_on", {"area_id": "test-area"}) assert { "light.in_area", "light.assigned_to_area", } == await service.async_extract_entity_ids(opp, call) call = ha.ServiceCall("light", "turn_on", {"area_id": ["test-area", "diff-area"]}) assert { "light.in_area", "light.diff_area", "light.assigned_to_area", } == await service.async_extract_entity_ids(opp, call) assert (await service.async_extract_entity_ids( opp, ha.ServiceCall("light", "turn_on", {"area_id": ENTITY_MATCH_NONE})) == set())
async def test_extract_entity_ids(opp): """Test extract_entity_ids method.""" opp.states.async_set("light.Bowl", STATE_ON) opp.states.async_set("light.Ceiling", STATE_OFF) opp.states.async_set("light.Kitchen", STATE_OFF) assert await async_setup_component(opp, "group", {}) await opp.async_block_till_done() await opp.components.group.Group.async_create_group( opp, "test", ["light.Ceiling", "light.Kitchen"]) call = ha.ServiceCall("light", "turn_on", {ATTR_ENTITY_ID: "light.Bowl"}) assert {"light.bowl"} == await service.async_extract_entity_ids(opp, call) call = ha.ServiceCall("light", "turn_on", {ATTR_ENTITY_ID: "group.test"}) assert {"light.ceiling", "light.kitchen" } == await service.async_extract_entity_ids(opp, call) assert {"group.test" } == await service.async_extract_entity_ids(opp, call, expand_group=False) assert (await service.async_extract_entity_ids( opp, ha.ServiceCall("light", "turn_on", {ATTR_ENTITY_ID: ENTITY_MATCH_NONE}), ) == set())
def test_service_call_repr(): """Test ServiceCall repr.""" call = ha.ServiceCall("openpeerpower", "start") assert str( call) == f"<ServiceCall openpeerpower.start (c:{call.context.id})>" call2 = ha.ServiceCall("openpeerpower", "start", {"fast": "yes"}) assert ( str(call2) == f"<ServiceCall openpeerpower.start (c:{call2.context.id}): fast=yes>")
async def test_call_context_target_specific(opp, mock_handle_entity_call, mock_entities): """Check targeting specific entities.""" with patch( "openpeerpower.auth.AuthManager.async_get_user", return_value=Mock(permissions=PolicyPermissions( {"entities": { "entity_ids": { "light.kitchen": True } }}, None)), ): await service.entity_service_call( opp, [Mock(entities=mock_entities)], Mock(), ha.ServiceCall( "test_domain", "test_service", {"entity_id": "light.kitchen"}, context=ha.Context(user_id="mock-id"), ), ) assert len(mock_handle_entity_call.mock_calls) == 1 assert mock_handle_entity_call.mock_calls[0][1][ 1].entity_id == "light.kitchen"
async def test_extract_from_service_no_group_expand(opp): """Test not expanding a group.""" component = EntityComponent(_LOGGER, DOMAIN, opp) await component.async_add_entities([MockEntity(entity_id="group.test_group")]) call = ha.ServiceCall("test", "service", {"entity_id": ["group.test_group"]}) extracted = await component.async_extract_from_service(call, expand_group=False) assert len(extracted) == 1 assert extracted[0].entity_id == "group.test_group"
async def test_extract_from_service_available_device(opp): """Test the extraction of entity from service and device is available.""" entities = [ MockEntity(name="test_1", entity_id="test_domain.test_1"), MockEntity(name="test_2", entity_id="test_domain.test_2", available=False), MockEntity(name="test_3", entity_id="test_domain.test_3"), MockEntity(name="test_4", entity_id="test_domain.test_4", available=False), ] call_1 = ha.ServiceCall("test", "service", data={"entity_id": ENTITY_MATCH_ALL}) assert ["test_domain.test_1", "test_domain.test_3"] == [ ent.entity_id for ent in ( await service.async_extract_entities(opp, entities, call_1)) ] call_2 = ha.ServiceCall( "test", "service", data={"entity_id": ["test_domain.test_3", "test_domain.test_4"]}, ) assert ["test_domain.test_3"] == [ ent.entity_id for ent in ( await service.async_extract_entities(opp, entities, call_2)) ] assert (await service.async_extract_entities( opp, entities, ha.ServiceCall( "test", "service", data={"entity_id": ENTITY_MATCH_NONE}, ), ) == [])
async def test_call_with_omit_entity_id(opp, mock_handle_entity_call, mock_entities): """Check service call if we do not pass an entity ID.""" await service.entity_service_call( opp, [Mock(entities=mock_entities)], Mock(), ha.ServiceCall("test_domain", "test_service"), ) assert len(mock_handle_entity_call.mock_calls) == 0
async def test_call_with_sync_func(opp, mock_entities): """Test invoking sync service calls.""" test_service_mock = Mock(return_value=None) await service.entity_service_call( opp, [Mock(entities=mock_entities)], test_service_mock, ha.ServiceCall("test_domain", "test_service", {"entity_id": "light.kitchen"}), ) assert test_service_mock.call_count == 1
async def test_extract_entity_ids_from_devices(opp, area_mock): """Test extract_entity_ids method with devices.""" assert await service.async_extract_entity_ids( opp, ha.ServiceCall("light", "turn_on", {"device_id": "device-no-area-id"})) == { "light.no_area", } assert await service.async_extract_entity_ids( opp, ha.ServiceCall("light", "turn_on", {"device_id": "device-area-a-id"})) == { "light.in_area_a", "light.in_area_b", } assert (await service.async_extract_entity_ids( opp, ha.ServiceCall("light", "turn_on", {"device_id": "non-existing-id"})) == set())
async def test_extract_all_omit_entity_id(opp, caplog): """Test extract all with None and *.""" component = EntityComponent(_LOGGER, DOMAIN, opp) await component.async_add_entities( [MockEntity(name="test_1"), MockEntity(name="test_2")] ) call = ha.ServiceCall("test", "service") assert [] == sorted( ent.entity_id for ent in await component.async_extract_from_service(call) )
async def test_extract_from_service_empty_if_no_entity_id(opp): """Test the extraction from service without specifying entity.""" entities = [ MockEntity(name="test_1", entity_id="test_domain.test_1"), MockEntity(name="test_2", entity_id="test_domain.test_2"), ] call = ha.ServiceCall("test", "service") assert [] == [ ent.entity_id for ent in (await service.async_extract_entities(opp, entities, call)) ]
async def test_call_with_match_all(opp, mock_handle_entity_call, mock_entities, caplog): """Check we only target allowed entities if targeting all.""" await service.entity_service_call( opp, [Mock(entities=mock_entities)], Mock(), ha.ServiceCall("test_domain", "test_service", {"entity_id": "all"}), ) assert len(mock_handle_entity_call.mock_calls) == 4 assert [call[1][1] for call in mock_handle_entity_call.mock_calls ] == list(mock_entities.values())
async def test_call_with_both_required_features(opp, mock_entities): """Test service calls invoked only if entity has both features.""" test_service_mock = AsyncMock(return_value=None) await service.entity_service_call( opp, [Mock(entities=mock_entities)], test_service_mock, ha.ServiceCall("test_domain", "test_service", {"entity_id": "all"}), required_features=[SUPPORT_A | SUPPORT_B], ) assert test_service_mock.call_count == 1 assert [call[0][0] for call in test_service_mock.call_args_list ] == [mock_entities["light.bedroom"]]
async def test_extract_from_service_fails_if_no_entity_id(opp): """Test the extraction of everything from service.""" component = EntityComponent(_LOGGER, DOMAIN, opp) await component.async_add_entities( [MockEntity(name="test_1"), MockEntity(name="test_2")] ) assert ( await component.async_extract_from_service(ha.ServiceCall("test", "service")) == [] ) assert ( await component.async_extract_from_service( ha.ServiceCall("test", "service", {"entity_id": ENTITY_MATCH_NONE}) ) == [] ) assert ( await component.async_extract_from_service( ha.ServiceCall("test", "service", {"area_id": ENTITY_MATCH_NONE}) ) == [] )
async def test_extract_from_service_area_id(opp, area_mock): """Test the extraction using area ID as reference.""" entities = [ MockEntity(name="in_area", entity_id="light.in_area"), MockEntity(name="no_area", entity_id="light.no_area"), MockEntity(name="diff_area", entity_id="light.diff_area"), ] call = ha.ServiceCall("light", "turn_on", {"area_id": "test-area"}) extracted = await service.async_extract_entities(opp, entities, call) assert len(extracted) == 1 assert extracted[0].entity_id == "light.in_area" call = ha.ServiceCall("light", "turn_on", {"area_id": ["test-area", "diff-area"]}) extracted = await service.async_extract_entities(opp, entities, call) assert len(extracted) == 2 assert sorted(ent.entity_id for ent in extracted) == [ "light.diff_area", "light.in_area", ] call = ha.ServiceCall( "light", "turn_on", { "area_id": ["test-area", "diff-area"], "device_id": "device-no-area-id" }, ) extracted = await service.async_extract_entities(opp, entities, call) assert len(extracted) == 3 assert sorted(ent.entity_id for ent in extracted) == [ "light.diff_area", "light.in_area", "light.no_area", ]
async def test_call_no_context_target_all(opp, mock_handle_entity_call, mock_entities): """Check we target all if no user context given.""" await service.entity_service_call( opp, [Mock(entities=mock_entities)], Mock(), ha.ServiceCall("test_domain", "test_service", data={"entity_id": ENTITY_MATCH_ALL}), ) assert len(mock_handle_entity_call.mock_calls) == 4 assert [call[1][1] for call in mock_handle_entity_call.mock_calls ] == list(mock_entities.values())
async def test_call_context_user_not_exist(opp): """Check we don't allow deleted users to do things.""" with pytest.raises(exceptions.UnknownUser) as err: await service.entity_service_call( opp, [], Mock(), ha.ServiceCall( "test_domain", "test_service", context=ha.Context(user_id="non-existing"), ), ) assert err.value.context.user_id == "non-existing"
async def test_entity_service_call_warn_referenced(opp, caplog): """Test we only warn for referenced entities in entity_service_call.""" call = ha.ServiceCall( "light", "turn_on", { "area_id": "non-existent-area", "entity_id": "non.existent", "device_id": "non-existent-device", }, ) await service.entity_service_call(opp, {}, "", call) assert ( "Unable to find referenced areas non-existent-area, devices non-existent-device, entities non.existent" in caplog.text)
async def test_extract_all_use_match_all(opp, caplog): """Test extract all with None and *.""" component = EntityComponent(_LOGGER, DOMAIN, opp) await component.async_add_entities( [MockEntity(name="test_1"), MockEntity(name="test_2")] ) call = ha.ServiceCall("test", "service", {"entity_id": "all"}) assert ["test_domain.test_1", "test_domain.test_2"] == sorted( ent.entity_id for ent in await component.async_extract_from_service(call) ) assert ( "Not passing an entity ID to a service to target all entities is deprecated" ) not in caplog.text
async def test_async_extract_entities_warn_referenced(opp, caplog): """Test we only warn for referenced entities in async_extract_entities.""" call = ha.ServiceCall( "light", "turn_on", { "area_id": "non-existent-area", "entity_id": "non.existent", "device_id": "non-existent-device", }, ) extracted = await service.async_extract_entities(opp, {}, call) assert len(extracted) == 0 assert ( "Unable to find referenced areas non-existent-area, devices non-existent-device, entities non.existent" in caplog.text)
async def test_extract_from_service_filter_out_non_existing_entities(opp): """Test the extraction of non existing entities from service.""" component = EntityComponent(_LOGGER, DOMAIN, opp) await component.async_add_entities( [MockEntity(name="test_1"), MockEntity(name="test_2")] ) call = ha.ServiceCall( "test", "service", {"entity_id": ["test_domain.test_2", "test_domain.non_exist"]}, ) assert ["test_domain.test_2"] == [ ent.entity_id for ent in await component.async_extract_from_service(call) ]
async def test_extract_from_service_filter_out_non_existing_entities(opp): """Test the extraction of non existing entities from service.""" entities = [ MockEntity(name="test_1", entity_id="test_domain.test_1"), MockEntity(name="test_2", entity_id="test_domain.test_2"), ] call = ha.ServiceCall( "test", "service", {"entity_id": ["test_domain.test_2", "test_domain.non_exist"]}, ) assert ["test_domain.test_2"] == [ ent.entity_id for ent in (await service.async_extract_entities(opp, entities, call)) ]
async def test_call_no_context_target_specific(opp, mock_handle_entity_call, mock_entities): """Check we can target specified entities.""" await service.entity_service_call( opp, [Mock(entities=mock_entities)], Mock(), ha.ServiceCall( "test_domain", "test_service", {"entity_id": ["light.kitchen", "light.non-existing"]}, ), ) assert len(mock_handle_entity_call.mock_calls) == 1 assert mock_handle_entity_call.mock_calls[0][1][ 1].entity_id == "light.kitchen"
async def test_call_with_required_features(opp, mock_entities): """Test service calls invoked only if entity has required features.""" test_service_mock = AsyncMock(return_value=None) await service.entity_service_call( opp, [Mock(entities=mock_entities)], test_service_mock, ha.ServiceCall("test_domain", "test_service", {"entity_id": "all"}), required_features=[SUPPORT_A], ) assert test_service_mock.call_count == 2 expected = [ mock_entities["light.kitchen"], mock_entities["light.bedroom"], ] actual = [call[0][0] for call in test_service_mock.call_args_list] assert all(entity in actual for entity in expected)
async def test_call_with_sync_attr(opp, mock_entities): """Test invoking sync service calls.""" mock_method = mock_entities["light.kitchen"].sync_method = Mock( return_value=None) await service.entity_service_call( opp, [Mock(entities=mock_entities)], "sync_method", ha.ServiceCall( "test_domain", "test_service", { "entity_id": "light.kitchen", "area_id": "abcd" }, ), ) assert mock_method.call_count == 1 # We pass empty kwargs because both entity_id and area_id are filtered out assert mock_method.mock_calls[0][2] == {}
async def test_turn_on_skips_domains_without_service(opp, caplog): """Test if turn_on is blocking domain with no service.""" await async_setup_component(opp, "openpeerpower", {}) async_mock_service(opp, "light", SERVICE_TURN_ON) opp.states.async_set("light.Bowl", STATE_ON) opp.states.async_set("light.Ceiling", STATE_OFF) # We can't test if our service call results in services being called # because by mocking out the call service method, we mock out all # So we mimic how the service registry calls services service_call = ha.ServiceCall( "openpeerpower", "turn_on", { "entity_id": ["light.test", "sensor.bla", "binary_sensor.blub", "light.bla"] }, ) service = opp.services._services["openpeerpower"]["turn_on"] with patch( "openpeerpower.core.ServiceRegistry.async_call", return_value=None, ) as mock_call: await service.job.target(service_call) assert mock_call.call_count == 1 assert mock_call.call_args_list[0][0] == ( "light", "turn_on", { "entity_id": ["light.bla", "light.test"] }, ) assert mock_call.call_args_list[0][1] == { "blocking": True, "context": service_call.context, } assert ( "The service openpeerpower.turn_on does not support entities binary_sensor.blub, sensor.bla" in caplog.text)
async def test_turn_on_to_not_block_for_domains_without_service(opp): """Test if turn_on is blocking domain with no service.""" await async_setup_component(opp, "openpeerpower", {}) async_mock_service(opp, "light", SERVICE_TURN_ON) opp.states.async_set("light.Bowl", STATE_ON) opp.states.async_set("light.Ceiling", STATE_OFF) # We can't test if our service call results in services being called # because by mocking out the call service method, we mock out all # So we mimic how the service registry calls services service_call = op.ServiceCall( "openpeerpower", "turn_on", {"entity_id": ["light.test", "sensor.bla", "light.bla"]}, ) service = opp.services._services["openpeerpower"]["turn_on"] with patch( "openpeerpower.core.ServiceRegistry.async_call", side_effect=lambda *args: mock_coro(), ) as mock_call: await service.func(service_call) assert mock_call.call_count == 2 assert mock_call.call_args_list[0][0] == ( "light", "turn_on", { "entity_id": ["light.bla", "light.test"] }, True, ) assert mock_call.call_args_list[1][0] == ( "sensor", "turn_on", { "entity_id": ["sensor.bla"] }, False, )
async def test_async_extract_config_entry_ids(opp): """Test we can find devices that have no entities.""" device_no_entities = dev_reg.DeviceEntry(id="device-no-entities", config_entries={"abc"}) call = ha.ServiceCall( "openpeerpower", "reload_config_entry", { "device_id": "device-no-entities", }, ) mock_device_registry( opp, { device_no_entities.id: device_no_entities, }, ) assert await service.async_extract_config_entry_ids(opp, call) == {"abc"}
async def test_call_context_target_specific_no_auth(opp, mock_handle_entity_call, mock_entities): """Check targeting specific entities without auth.""" with pytest.raises(exceptions.Unauthorized) as err, patch( "openpeerpower.auth.AuthManager.async_get_user", return_value=Mock(permissions=PolicyPermissions({}, None)), ): await service.entity_service_call( opp, [Mock(entities=mock_entities)], Mock(), ha.ServiceCall( "test_domain", "test_service", {"entity_id": "light.kitchen"}, context=ha.Context(user_id="mock-id"), ), ) assert err.value.context.user_id == "mock-id" assert err.value.entity_id == "light.kitchen"