mirror of
https://github.com/home-assistant/core.git
synced 2025-11-09 10:59:40 +00:00
Allow core integrations to describe their triggers (#147075)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
"""The tests for the trigger helper."""
|
||||
|
||||
import io
|
||||
from unittest.mock import ANY, AsyncMock, MagicMock, Mock, call, patch
|
||||
|
||||
import pytest
|
||||
from pytest_unordered import unordered
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sun import DOMAIN as DOMAIN_SUN
|
||||
from homeassistant.components.system_health import DOMAIN as DOMAIN_SYSTEM_HEALTH
|
||||
from homeassistant.components.tag import DOMAIN as DOMAIN_TAG
|
||||
from homeassistant.core import (
|
||||
CALLBACK_TYPE,
|
||||
Context,
|
||||
@@ -12,6 +17,8 @@ from homeassistant.core import (
|
||||
ServiceCall,
|
||||
callback,
|
||||
)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import trigger
|
||||
from homeassistant.helpers.trigger import (
|
||||
DATA_PLUGGABLE_ACTIONS,
|
||||
PluggableAction,
|
||||
@@ -23,9 +30,11 @@ from homeassistant.helpers.trigger import (
|
||||
async_validate_trigger_config,
|
||||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.loader import Integration, async_get_integration
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util.yaml.loader import parse_yaml
|
||||
|
||||
from tests.common import MockModule, mock_integration, mock_platform
|
||||
from tests.common import MockModule, MockPlatform, mock_integration, mock_platform
|
||||
|
||||
|
||||
async def test_bad_trigger_platform(hass: HomeAssistant) -> None:
|
||||
@@ -519,3 +528,213 @@ async def test_platform_multiple_triggers(hass: HomeAssistant) -> None:
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
await async_initialize_triggers(hass, config_3, cb_action, "test", "", log_cb)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"sun_trigger_descriptions",
|
||||
[
|
||||
"""
|
||||
sun:
|
||||
fields:
|
||||
event:
|
||||
example: sunrise
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- sunrise
|
||||
- sunset
|
||||
offset:
|
||||
selector:
|
||||
time: null
|
||||
""",
|
||||
"""
|
||||
.anchor: &anchor
|
||||
- sunrise
|
||||
- sunset
|
||||
sun:
|
||||
fields:
|
||||
event:
|
||||
example: sunrise
|
||||
selector:
|
||||
select:
|
||||
options: *anchor
|
||||
offset:
|
||||
selector:
|
||||
time: null
|
||||
""",
|
||||
],
|
||||
)
|
||||
async def test_async_get_all_descriptions(
|
||||
hass: HomeAssistant, sun_trigger_descriptions: str
|
||||
) -> None:
|
||||
"""Test async_get_all_descriptions."""
|
||||
tag_trigger_descriptions = """
|
||||
tag: {}
|
||||
"""
|
||||
|
||||
assert await async_setup_component(hass, DOMAIN_SUN, {})
|
||||
assert await async_setup_component(hass, DOMAIN_SYSTEM_HEALTH, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
def _load_yaml(fname, secrets=None):
|
||||
if fname.endswith("sun/triggers.yaml"):
|
||||
trigger_descriptions = sun_trigger_descriptions
|
||||
elif fname.endswith("tag/triggers.yaml"):
|
||||
trigger_descriptions = tag_trigger_descriptions
|
||||
with io.StringIO(trigger_descriptions) as file:
|
||||
return parse_yaml(file)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.helpers.trigger._load_triggers_files",
|
||||
side_effect=trigger._load_triggers_files,
|
||||
) as proxy_load_triggers_files,
|
||||
patch(
|
||||
"annotatedyaml.loader.load_yaml",
|
||||
side_effect=_load_yaml,
|
||||
),
|
||||
patch.object(Integration, "has_triggers", return_value=True),
|
||||
):
|
||||
descriptions = await trigger.async_get_all_descriptions(hass)
|
||||
|
||||
# Test we only load triggers.yaml for integrations with triggers,
|
||||
# system_health has no triggers
|
||||
assert proxy_load_triggers_files.mock_calls[0][1][1] == unordered(
|
||||
[
|
||||
await async_get_integration(hass, DOMAIN_SUN),
|
||||
]
|
||||
)
|
||||
|
||||
# system_health does not have triggers and should not be in descriptions
|
||||
assert descriptions == {
|
||||
DOMAIN_SUN: {
|
||||
"fields": {
|
||||
"event": {
|
||||
"example": "sunrise",
|
||||
"selector": {"select": {"options": ["sunrise", "sunset"]}},
|
||||
},
|
||||
"offset": {"selector": {"time": None}},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Verify the cache returns the same object
|
||||
assert await trigger.async_get_all_descriptions(hass) is descriptions
|
||||
|
||||
# Load the tag integration and check a new cache object is created
|
||||
assert await async_setup_component(hass, DOMAIN_TAG, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"annotatedyaml.loader.load_yaml",
|
||||
side_effect=_load_yaml,
|
||||
),
|
||||
patch.object(Integration, "has_triggers", return_value=True),
|
||||
):
|
||||
new_descriptions = await trigger.async_get_all_descriptions(hass)
|
||||
assert new_descriptions is not descriptions
|
||||
assert new_descriptions == {
|
||||
DOMAIN_SUN: {
|
||||
"fields": {
|
||||
"event": {
|
||||
"example": "sunrise",
|
||||
"selector": {"select": {"options": ["sunrise", "sunset"]}},
|
||||
},
|
||||
"offset": {"selector": {"time": None}},
|
||||
}
|
||||
},
|
||||
DOMAIN_TAG: {
|
||||
"fields": {},
|
||||
},
|
||||
}
|
||||
|
||||
# Verify the cache returns the same object
|
||||
assert await trigger.async_get_all_descriptions(hass) is new_descriptions
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("yaml_error", "expected_message"),
|
||||
[
|
||||
(
|
||||
FileNotFoundError("Blah"),
|
||||
"Unable to find triggers.yaml for the sun integration",
|
||||
),
|
||||
(
|
||||
HomeAssistantError("Test error"),
|
||||
"Unable to parse triggers.yaml for the sun integration: Test error",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_async_get_all_descriptions_with_yaml_error(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
yaml_error: Exception,
|
||||
expected_message: str,
|
||||
) -> None:
|
||||
"""Test async_get_all_descriptions."""
|
||||
assert await async_setup_component(hass, DOMAIN_SUN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
def _load_yaml_dict(fname, secrets=None):
|
||||
raise yaml_error
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.helpers.trigger.load_yaml_dict",
|
||||
side_effect=_load_yaml_dict,
|
||||
),
|
||||
patch.object(Integration, "has_triggers", return_value=True),
|
||||
):
|
||||
descriptions = await trigger.async_get_all_descriptions(hass)
|
||||
|
||||
assert descriptions == {DOMAIN_SUN: None}
|
||||
|
||||
assert expected_message in caplog.text
|
||||
|
||||
|
||||
async def test_async_get_all_descriptions_with_bad_description(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test async_get_all_descriptions."""
|
||||
sun_service_descriptions = """
|
||||
sun:
|
||||
fields: not_a_dict
|
||||
"""
|
||||
|
||||
assert await async_setup_component(hass, DOMAIN_SUN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
def _load_yaml(fname, secrets=None):
|
||||
with io.StringIO(sun_service_descriptions) as file:
|
||||
return parse_yaml(file)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"annotatedyaml.loader.load_yaml",
|
||||
side_effect=_load_yaml,
|
||||
),
|
||||
patch.object(Integration, "has_triggers", return_value=True),
|
||||
):
|
||||
descriptions = await trigger.async_get_all_descriptions(hass)
|
||||
|
||||
assert descriptions == {DOMAIN_SUN: None}
|
||||
|
||||
assert (
|
||||
"Unable to parse triggers.yaml for the sun integration: "
|
||||
"expected a dictionary for dictionary value @ data['sun']['fields']"
|
||||
) in caplog.text
|
||||
|
||||
|
||||
async def test_invalid_trigger_platform(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test invalid trigger platform."""
|
||||
mock_integration(hass, MockModule("test", async_setup=AsyncMock(return_value=True)))
|
||||
mock_platform(hass, "test.trigger", MockPlatform())
|
||||
|
||||
await async_setup_component(hass, "test", {})
|
||||
|
||||
assert "Integration test does not provide trigger support, skipping" in caplog.text
|
||||
|
||||
Reference in New Issue
Block a user