Improve test of REST endpoint /api/services (#150897)

This commit is contained in:
Erik Montnemery
2025-08-19 17:32:52 +02:00
committed by GitHub
parent 7ecf32390c
commit 26582cecbd
2 changed files with 211 additions and 6 deletions

View File

@@ -0,0 +1,144 @@
# serializer version: 1
# name: test_api_get_services
list([
dict({
'domain': 'group',
'services': dict({
'reload': dict({
'description': 'Reloads group configuration, entities, and notify services from YAML-configuration.',
'fields': dict({
}),
'name': 'Reload',
}),
'remove': dict({
'description': 'Removes a group.',
'fields': dict({
'object_id': dict({
'description': 'Object ID of this group. This object ID is used as part of the entity ID. Entity ID format: [domain].[object_id].',
'example': 'test_group',
'name': 'Object ID',
'required': True,
'selector': dict({
'object': dict({
}),
}),
}),
}),
'name': 'Remove',
}),
'set': dict({
'description': 'Creates/Updates a group.',
'fields': dict({
'add_entities': dict({
'description': 'List of members to be added to the group. Cannot be used in combination with `Entities` or `Remove entities`.',
'example': 'domain.entity_id1, domain.entity_id2',
'name': 'Add entities',
'selector': dict({
'entity': dict({
'multiple': True,
'reorder': False,
}),
}),
}),
'all': dict({
'description': 'Enable this option if the group should only be used when all entities are in state `on`.',
'name': 'All',
'selector': dict({
'boolean': dict({
}),
}),
}),
'entities': dict({
'description': 'List of all members in the group. Cannot be used in combination with `Add entities` or `Remove entities`.',
'example': 'domain.entity_id1, domain.entity_id2',
'name': 'Entities',
'selector': dict({
'entity': dict({
'multiple': True,
'reorder': False,
}),
}),
}),
'icon': dict({
'description': 'Name of the icon for the group.',
'example': 'mdi:camera',
'name': 'Icon',
'selector': dict({
'icon': dict({
}),
}),
}),
'name': dict({
'description': 'Name of the group.',
'example': 'My test group',
'name': 'Name',
'selector': dict({
'text': dict({
}),
}),
}),
'object_id': dict({
'description': 'Object ID of this group. This object ID is used as part of the entity ID. Entity ID format: [domain].[object_id].',
'example': 'test_group',
'name': 'Object ID',
'required': True,
'selector': dict({
'text': dict({
}),
}),
}),
'remove_entities': dict({
'description': 'List of members to be removed from a group. Cannot be used in combination with `Entities` or `Add entities`.',
'example': 'domain.entity_id1, domain.entity_id2',
'name': 'Remove entities',
'selector': dict({
'entity': dict({
'multiple': True,
'reorder': False,
}),
}),
}),
}),
'name': 'Set',
}),
}),
}),
])
# ---
# name: test_api_get_services.1
dict({
'domain': 'logger',
'services': dict({
'set_default_level': dict({
'description': 'Translated description',
'fields': dict({
'level': dict({
'description': 'Field description',
'example': 'Field example',
'name': 'Field name',
'selector': dict({
'select': dict({
'options': list([
'debug',
'info',
'warning',
'error',
'fatal',
'critical',
]),
'translation_key': 'level',
}),
}),
}),
}),
'name': 'Translated name',
}),
'set_level': dict({
'description': '',
'fields': dict({
}),
'name': '',
}),
}),
})
# ---

View File

@@ -4,18 +4,24 @@ import asyncio
from http import HTTPStatus from http import HTTPStatus
import json import json
from typing import Any from typing import Any
from unittest.mock import patch from unittest.mock import ANY, patch
from aiohttp import ServerDisconnectedError, web from aiohttp import ServerDisconnectedError, web
from aiohttp.test_utils import TestClient from aiohttp.test_utils import TestClient
import pytest import pytest
from syrupy.assertion import SnapshotAssertion
import voluptuous as vol import voluptuous as vol
from homeassistant import const, core as ha from homeassistant import const, core as ha
from homeassistant.auth.models import Credentials from homeassistant.auth.models import Credentials
from homeassistant.bootstrap import DATA_LOGGING from homeassistant.bootstrap import DATA_LOGGING
from homeassistant.components.group import DOMAIN as DOMAIN_GROUP
from homeassistant.components.logger import DOMAIN as DOMAIN_LOGGER
from homeassistant.components.system_health import DOMAIN as DOMAIN_SYSTEM_HEALTH
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.loader import Integration
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.util.yaml.loader import JSON_TYPE
from tests.common import CLIENT_ID, MockUser, async_mock_service from tests.common import CLIENT_ID, MockUser, async_mock_service
from tests.typing import ClientSessionGenerator from tests.typing import ClientSessionGenerator
@@ -315,17 +321,72 @@ async def test_api_get_event_listeners(
async def test_api_get_services( async def test_api_get_services(
hass: HomeAssistant, mock_api_client: TestClient hass: HomeAssistant,
mock_api_client: TestClient,
snapshot: SnapshotAssertion,
) -> None: ) -> None:
"""Test if we can get a dict describing current services.""" """Test if we can get a dict describing current services."""
# Set up an integration that has services
assert await async_setup_component(hass, DOMAIN_GROUP, {DOMAIN_GROUP: {}})
# Set up an integration that has no services
assert await async_setup_component(hass, DOMAIN_SYSTEM_HEALTH, {})
resp = await mock_api_client.get(const.URL_API_SERVICES) resp = await mock_api_client.get(const.URL_API_SERVICES)
data = await resp.json() data = await resp.json()
local_services = hass.services.async_services()
for serv_domain in data: assert data == snapshot
local = local_services.pop(serv_domain["domain"])
assert serv_domain["services"].keys() == local.keys() # Set up an integration with legacy translations in services.yaml
def _load_services_file(hass: HomeAssistant, integration: Integration) -> JSON_TYPE:
return {
"set_default_level": {
"description": "Translated description",
"fields": {
"level": {
"description": "Field description",
"example": "Field example",
"name": "Field name",
"selector": {
"select": {
"options": [
"debug",
"info",
"warning",
"error",
"fatal",
"critical",
],
"translation_key": "level",
}
},
}
},
"name": "Translated name",
},
"set_level": None,
}
await async_setup_component(hass, DOMAIN_LOGGER, {DOMAIN_LOGGER: {}})
await hass.async_block_till_done()
with (
patch(
"homeassistant.helpers.service._load_services_file",
side_effect=_load_services_file,
),
patch(
"homeassistant.helpers.service.translation.async_get_translations",
return_value={},
),
):
resp = await mock_api_client.get(const.URL_API_SERVICES)
data2 = await resp.json()
assert data2 == [*data, {"domain": DOMAIN_LOGGER, "services": ANY}]
assert data2[-1] == snapshot
async def test_api_call_service_no_data( async def test_api_call_service_no_data(