mirror of https://github.com/home-assistant/core
1263 lines
36 KiB
Python
1263 lines
36 KiB
Python
"""Test selectors."""
|
|
|
|
from enum import Enum
|
|
from typing import Any
|
|
|
|
import pytest
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.helpers import selector
|
|
from homeassistant.util import yaml
|
|
|
|
FAKE_UUID = "a266a680b608c32770e6c45bfe6b8411"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"schema",
|
|
[
|
|
{"device": None},
|
|
{"entity": None},
|
|
],
|
|
)
|
|
def test_valid_base_schema(schema) -> None:
|
|
"""Test base schema validation."""
|
|
selector.validate_selector(schema)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"schema",
|
|
[
|
|
None,
|
|
"not_a_dict",
|
|
{},
|
|
{"non_existing": {}},
|
|
# Two keys
|
|
{"device": {}, "entity": {}},
|
|
],
|
|
)
|
|
def test_invalid_base_schema(schema) -> None:
|
|
"""Test base schema validation."""
|
|
with pytest.raises(vol.Invalid):
|
|
selector.validate_selector(schema)
|
|
|
|
|
|
def _test_selector(
|
|
selector_type, schema, valid_selections, invalid_selections, converter=None
|
|
):
|
|
"""Help test a selector."""
|
|
|
|
def default_converter(x):
|
|
return x
|
|
|
|
if converter is None:
|
|
converter = default_converter
|
|
|
|
# Validate selector configuration
|
|
config = {selector_type: schema}
|
|
selector.validate_selector(config)
|
|
selector_instance = selector.selector(config)
|
|
assert selector_instance == selector.selector(config)
|
|
assert selector_instance != 5
|
|
# We do not allow enums in the config, as they cannot serialize
|
|
assert not any(isinstance(val, Enum) for val in selector_instance.config.values())
|
|
|
|
# Use selector in schema and validate
|
|
vol_schema = vol.Schema({"selection": selector_instance})
|
|
for selection in valid_selections:
|
|
assert vol_schema({"selection": selection}) == {
|
|
"selection": converter(selection)
|
|
}
|
|
for selection in invalid_selections:
|
|
with pytest.raises(vol.Invalid):
|
|
vol_schema({"selection": selection})
|
|
|
|
# Serialize selector
|
|
selector_instance = selector.selector({selector_type: schema})
|
|
assert selector_instance.serialize() == {
|
|
"selector": {selector_type: selector_instance.config}
|
|
}
|
|
# Test serialized selector can be dumped to YAML
|
|
yaml.dump(selector_instance.serialize())
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(None, ("abc123",), (None,)),
|
|
({}, ("abc123",), (None,)),
|
|
({"integration": "zha"}, ("abc123",), (None,)),
|
|
({"manufacturer": "mock-manuf"}, ("abc123",), (None,)),
|
|
({"model": "mock-model"}, ("abc123",), (None,)),
|
|
({"manufacturer": "mock-manuf", "model": "mock-model"}, ("abc123",), (None,)),
|
|
(
|
|
{"integration": "zha", "manufacturer": "mock-manuf", "model": "mock-model"},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
({"entity": {"device_class": "motion"}}, ("abc123",), (None,)),
|
|
({"entity": {"device_class": ["motion", "temperature"]}}, ("abc123",), (None,)),
|
|
(
|
|
{
|
|
"entity": [
|
|
{"domain": "light"},
|
|
{"domain": "binary_sensor", "device_class": "motion"},
|
|
]
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"integration": "zha",
|
|
"manufacturer": "mock-manuf",
|
|
"model": "mock-model",
|
|
"entity": {"domain": "binary_sensor", "device_class": "motion"},
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{"multiple": True},
|
|
(["abc123", "def456"],),
|
|
("abc123", None, ["abc123", None]),
|
|
),
|
|
(
|
|
{
|
|
"filter": {
|
|
"integration": "zha",
|
|
"manufacturer": "mock-manuf",
|
|
"model": "mock-model",
|
|
}
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"filter": [
|
|
{
|
|
"integration": "zha",
|
|
"manufacturer": "mock-manuf",
|
|
"model": "mock-model",
|
|
},
|
|
{
|
|
"integration": "matter",
|
|
"manufacturer": "other-mock-manuf",
|
|
"model": "other-mock-model",
|
|
},
|
|
]
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
],
|
|
)
|
|
def test_device_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test device selector."""
|
|
_test_selector("device", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
({}, ("sensor.abc123", FAKE_UUID), (None, "abc123")),
|
|
({"integration": "zha"}, ("sensor.abc123", FAKE_UUID), (None, "abc123")),
|
|
({"domain": "light"}, ("light.abc123", FAKE_UUID), (None, "sensor.abc123")),
|
|
(
|
|
{"domain": ["light", "sensor"]},
|
|
("light.abc123", "sensor.abc123", FAKE_UUID),
|
|
(None, "dog.abc123"),
|
|
),
|
|
({"device_class": "motion"}, ("sensor.abc123", FAKE_UUID), (None, "abc123")),
|
|
(
|
|
{"device_class": ["motion", "temperature"]},
|
|
("sensor.abc123", FAKE_UUID),
|
|
(None, "abc123"),
|
|
),
|
|
(
|
|
{"integration": "zha", "domain": "light"},
|
|
("light.abc123", FAKE_UUID),
|
|
(None, "sensor.abc123"),
|
|
),
|
|
(
|
|
{"integration": "zha", "domain": "binary_sensor", "device_class": "motion"},
|
|
("binary_sensor.abc123", FAKE_UUID),
|
|
(None, "sensor.abc123"),
|
|
),
|
|
(
|
|
{"multiple": True, "domain": "sensor"},
|
|
(["sensor.abc123", "sensor.def456"], ["sensor.abc123", FAKE_UUID]),
|
|
(
|
|
"sensor.abc123",
|
|
FAKE_UUID,
|
|
None,
|
|
"abc123",
|
|
["sensor.abc123", "light.def456"],
|
|
),
|
|
),
|
|
(
|
|
{
|
|
"include_entities": ["sensor.abc123", "sensor.def456", "sensor.ghi789"],
|
|
"exclude_entities": ["sensor.ghi789", "sensor.jkl123"],
|
|
},
|
|
("sensor.abc123", FAKE_UUID),
|
|
("sensor.ghi789", "sensor.jkl123"),
|
|
),
|
|
(
|
|
{
|
|
"multiple": True,
|
|
"include_entities": ["sensor.abc123", "sensor.def456", "sensor.ghi789"],
|
|
"exclude_entities": ["sensor.ghi789", "sensor.jkl123"],
|
|
},
|
|
(["sensor.abc123", "sensor.def456"], ["sensor.abc123", FAKE_UUID]),
|
|
(
|
|
["sensor.abc123", "sensor.jkl123"],
|
|
["sensor.abc123", "sensor.ghi789"],
|
|
),
|
|
),
|
|
(
|
|
{"filter": {"domain": "light"}},
|
|
("light.abc123", FAKE_UUID),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"filter": [
|
|
{"domain": "light"},
|
|
{"domain": "binary_sensor", "device_class": "motion"},
|
|
]
|
|
},
|
|
("light.abc123", "binary_sensor.abc123", FAKE_UUID),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"filter": [
|
|
{"supported_features": ["light.LightEntityFeature.EFFECT"]},
|
|
]
|
|
},
|
|
("light.abc123", "blah.blah", FAKE_UUID),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"filter": [
|
|
{
|
|
"supported_features": [
|
|
[
|
|
"light.LightEntityFeature.EFFECT",
|
|
"light.LightEntityFeature.TRANSITION",
|
|
]
|
|
]
|
|
},
|
|
]
|
|
},
|
|
("light.abc123", "blah.blah", FAKE_UUID),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"filter": [
|
|
{
|
|
"supported_features": [
|
|
"light.LightEntityFeature.EFFECT",
|
|
"light.LightEntityFeature.TRANSITION",
|
|
]
|
|
},
|
|
]
|
|
},
|
|
("light.abc123", "blah.blah", FAKE_UUID),
|
|
(None,),
|
|
),
|
|
],
|
|
)
|
|
def test_entity_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test entity selector."""
|
|
_test_selector("entity", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"schema",
|
|
[
|
|
# Feature should be string specifying an enum member, not an int
|
|
{"filter": [{"supported_features": [1]}]},
|
|
# Invalid feature
|
|
{"filter": [{"supported_features": ["blah"]}]},
|
|
# Unknown feature enum
|
|
{"filter": [{"supported_features": ["blah.FooEntityFeature.blah"]}]},
|
|
# Unknown feature enum
|
|
{"filter": [{"supported_features": ["light.FooEntityFeature.blah"]}]},
|
|
# Unknown feature enum member
|
|
{"filter": [{"supported_features": ["light.LightEntityFeature.blah"]}]},
|
|
],
|
|
)
|
|
def test_entity_selector_schema_error(schema) -> None:
|
|
"""Test number selector."""
|
|
with pytest.raises(vol.Invalid):
|
|
selector.validate_selector({"entity": schema})
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
({}, ("abc123",), (None,)),
|
|
({"entity": {}}, ("abc123",), (None,)),
|
|
({"entity": {"domain": "light"}}, ("abc123",), (None,)),
|
|
(
|
|
{"entity": {"domain": "binary_sensor", "device_class": "motion"}},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"entity": {
|
|
"domain": "binary_sensor",
|
|
"device_class": "motion",
|
|
"integration": "demo",
|
|
}
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"entity": [
|
|
{"domain": "light"},
|
|
{"domain": "binary_sensor", "device_class": "motion"},
|
|
]
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{"device": {"integration": "demo", "model": "mock-model"}},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"device": [
|
|
{"integration": "demo", "model": "mock-model"},
|
|
{"integration": "other-demo", "model": "other-mock-model"},
|
|
]
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"entity": {"domain": "binary_sensor", "device_class": "motion"},
|
|
"device": {"integration": "demo", "model": "mock-model"},
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{"multiple": True},
|
|
((["abc123", "def456"],)),
|
|
(None, "abc123", ["abc123", None]),
|
|
),
|
|
],
|
|
)
|
|
def test_area_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test area selector."""
|
|
_test_selector("area", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
("23ouih2iu23ou2", "2j4hp3uy4p87wyrpiuhk34"),
|
|
(None, True, 1),
|
|
),
|
|
],
|
|
)
|
|
def test_assist_pipeline_selector_schema(
|
|
schema, valid_selections, invalid_selections
|
|
) -> None:
|
|
"""Test assist pipeline selector."""
|
|
_test_selector("assist_pipeline", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{"min": 10, "max": 50},
|
|
(
|
|
10,
|
|
50,
|
|
),
|
|
(9, 51),
|
|
),
|
|
({"min": -100, "max": 100, "step": 5}, (), ()),
|
|
({"min": -20, "max": -10, "mode": "box"}, (), ()),
|
|
(
|
|
{"min": 0, "max": 100, "unit_of_measurement": "seconds", "mode": "slider"},
|
|
(),
|
|
(),
|
|
),
|
|
({"min": 10, "max": 1000, "mode": "slider", "step": 0.5}, (), ()),
|
|
({"mode": "box"}, (10,), ()),
|
|
({"mode": "box", "step": "any"}, (), ()),
|
|
({"mode": "slider", "min": 0, "max": 1, "step": "any"}, (), ()),
|
|
],
|
|
)
|
|
def test_number_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test number selector."""
|
|
_test_selector("number", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"schema",
|
|
[
|
|
{}, # Must have mandatory fields
|
|
{"mode": "slider"}, # Must have min+max in slider mode
|
|
],
|
|
)
|
|
def test_number_selector_schema_error(schema) -> None:
|
|
"""Test number selector."""
|
|
with pytest.raises(vol.Invalid):
|
|
selector.validate_selector({"number": schema})
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[({}, ("abc123",), (None,))],
|
|
)
|
|
def test_addon_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test add-on selector."""
|
|
_test_selector("addon", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[({}, ("abc123", "/backup"), (None, "abc@123", "abc 123", ""))],
|
|
)
|
|
def test_backup_location_selector_schema(
|
|
schema, valid_selections, invalid_selections
|
|
) -> None:
|
|
"""Test backup location selector."""
|
|
_test_selector("backup_location", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[({}, (1, "one", None), ())], # Everything can be coerced to bool
|
|
)
|
|
def test_boolean_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test boolean selector."""
|
|
_test_selector(
|
|
"boolean",
|
|
schema,
|
|
valid_selections,
|
|
invalid_selections,
|
|
bool,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
("6b68b250388cbe0d620c92dd3acc93ec", "76f2e8f9a6491a1b580b3a8967c27ddd"),
|
|
(None, True, 1),
|
|
),
|
|
(
|
|
{"integration": "adguard"},
|
|
("6b68b250388cbe0d620c92dd3acc93ec", "76f2e8f9a6491a1b580b3a8967c27ddd"),
|
|
(None, True, 1),
|
|
),
|
|
],
|
|
)
|
|
def test_config_entry_selector_schema(
|
|
schema, valid_selections, invalid_selections
|
|
) -> None:
|
|
"""Test config entry selector."""
|
|
_test_selector("config_entry", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
("NL", "DE"),
|
|
(None, True, 1),
|
|
),
|
|
(
|
|
{"countries": ["NL", "DE"]},
|
|
("NL", "DE"),
|
|
(None, True, 1, "sv", "en"),
|
|
),
|
|
],
|
|
)
|
|
def test_country_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test country selector."""
|
|
_test_selector("country", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[({}, ("00:00:00",), ("blah", None))],
|
|
)
|
|
def test_time_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test time selector."""
|
|
_test_selector("time", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{"entity_id": "sensor.abc"},
|
|
("on", "armed"),
|
|
(None, True, 1),
|
|
),
|
|
],
|
|
)
|
|
def test_state_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test state selector."""
|
|
_test_selector("state", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
({}, ({"entity_id": ["sensor.abc123"]},), ("abc123", None)),
|
|
({"entity": {}}, (), ()),
|
|
({"entity": {"domain": "light"}}, (), ()),
|
|
({"entity": {"domain": "binary_sensor", "device_class": "motion"}}, (), ()),
|
|
(
|
|
{
|
|
"entity": [
|
|
{"domain": "light"},
|
|
{"domain": "binary_sensor", "device_class": "motion"},
|
|
]
|
|
},
|
|
(),
|
|
(),
|
|
),
|
|
(
|
|
{
|
|
"entity": {
|
|
"domain": "binary_sensor",
|
|
"device_class": "motion",
|
|
"integration": "demo",
|
|
}
|
|
},
|
|
(),
|
|
(),
|
|
),
|
|
({"device": {"integration": "demo", "model": "mock-model"}}, (), ()),
|
|
(
|
|
{
|
|
"device": [
|
|
{"integration": "demo", "model": "mock-model"},
|
|
{"integration": "other-demo", "model": "other-mock-model"},
|
|
],
|
|
},
|
|
(),
|
|
(),
|
|
),
|
|
(
|
|
{
|
|
"entity": {"domain": "binary_sensor", "device_class": "motion"},
|
|
"device": {"integration": "demo", "model": "mock-model"},
|
|
},
|
|
(),
|
|
(),
|
|
),
|
|
],
|
|
)
|
|
def test_target_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test target selector."""
|
|
_test_selector("target", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[({}, ("abc123",), ())],
|
|
)
|
|
def test_action_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test action sequence selector."""
|
|
_test_selector("action", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[({}, ("abc123",), ())],
|
|
)
|
|
def test_object_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test object selector."""
|
|
_test_selector("object", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
({}, ("abc123",), (None,)),
|
|
({"multiline": True}, (), ()),
|
|
({"multiline": False, "type": "email"}, (), ()),
|
|
({"prefix": "before", "suffix": "after"}, (), ()),
|
|
(
|
|
{"multiple": True},
|
|
(["abc123", "def456"],),
|
|
("abc123", None, ["abc123", None]),
|
|
),
|
|
],
|
|
)
|
|
def test_text_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test text selector."""
|
|
_test_selector("text", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{"options": ["red", "green", "blue"]},
|
|
("red", "green", "blue"),
|
|
("cat", 0, None, ["red"]),
|
|
),
|
|
(
|
|
{
|
|
"options": [
|
|
{"value": "red", "label": "Ruby Red"},
|
|
{"value": "green", "label": "Emerald Green"},
|
|
]
|
|
},
|
|
("red", "green"),
|
|
("cat", 0, None, ["red"]),
|
|
),
|
|
(
|
|
{
|
|
"options": ["red", "green", "blue"],
|
|
"translation_key": "color",
|
|
},
|
|
("red", "green", "blue"),
|
|
("cat", 0, None, ["red"]),
|
|
),
|
|
(
|
|
{
|
|
"options": [
|
|
{"value": "red", "label": "Ruby Red"},
|
|
{"value": "green", "label": "Emerald Green"},
|
|
],
|
|
"translation_key": "color",
|
|
},
|
|
("red", "green"),
|
|
("cat", 0, None, ["red"]),
|
|
),
|
|
(
|
|
{"options": ["red", "green", "blue"], "multiple": True},
|
|
(["red"], ["green", "blue"], []),
|
|
("cat", 0, None, "red"),
|
|
),
|
|
(
|
|
{
|
|
"options": ["red", "green", "blue"],
|
|
"multiple": True,
|
|
"custom_value": True,
|
|
},
|
|
(["red"], ["green", "blue"], ["red", "cat"], []),
|
|
("cat", 0, None, "red"),
|
|
),
|
|
(
|
|
{"options": ["red", "green", "blue"], "custom_value": True},
|
|
("red", "green", "blue", "cat"),
|
|
(0, None, ["red"]),
|
|
),
|
|
(
|
|
{"options": [], "custom_value": True},
|
|
("red", "cat"),
|
|
(0, None, ["red"]),
|
|
),
|
|
(
|
|
{"options": [], "custom_value": True, "multiple": True, "mode": "list"},
|
|
(["red"], ["green", "blue"], []),
|
|
(0, None, "red"),
|
|
),
|
|
(
|
|
{"options": ["red", "green", "blue"], "sort": True},
|
|
("red", "blue"),
|
|
(0, None, ["red"]),
|
|
),
|
|
],
|
|
)
|
|
def test_select_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test select selector."""
|
|
_test_selector("select", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"schema",
|
|
[
|
|
{}, # Must have options
|
|
{"options": {"hello": "World"}}, # Options must be a list
|
|
# Options must be strings or value / label pairs
|
|
{"options": [{"hello": "World"}]},
|
|
# Options must all be of the same type
|
|
{"options": ["red", {"value": "green", "label": "Emerald Green"}]},
|
|
],
|
|
)
|
|
def test_select_selector_schema_error(schema) -> None:
|
|
"""Test select selector."""
|
|
with pytest.raises(vol.Invalid):
|
|
selector.validate_selector({"select": schema})
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{"entity_id": "sensor.abc"},
|
|
("friendly_name", "device_class"),
|
|
(None,),
|
|
),
|
|
(
|
|
{"entity_id": "sensor.abc", "hide_attributes": ["friendly_name"]},
|
|
("device_class", "state_class"),
|
|
(None,),
|
|
),
|
|
],
|
|
)
|
|
def test_attribute_selector_schema(
|
|
schema, valid_selections, invalid_selections
|
|
) -> None:
|
|
"""Test attribute selector."""
|
|
_test_selector("attribute", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
(
|
|
{"seconds": 10},
|
|
{"days": 10}, # Days is allowed also if `enable_day` is not set
|
|
{"milliseconds": 500},
|
|
),
|
|
(None, {}),
|
|
),
|
|
(
|
|
{"enable_day": True, "enable_millisecond": True},
|
|
({"seconds": 10}, {"days": 10}, {"milliseconds": 500}),
|
|
(None, {}),
|
|
),
|
|
(
|
|
{"allow_negative": False},
|
|
({"seconds": 10}, {"days": 10}),
|
|
(None, {}, {"seconds": -1}),
|
|
),
|
|
],
|
|
)
|
|
def test_duration_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test duration selector."""
|
|
_test_selector("duration", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
("mdi:abc",),
|
|
(None,),
|
|
),
|
|
],
|
|
)
|
|
def test_icon_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test icon selector."""
|
|
_test_selector("icon", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
("abc",),
|
|
(None,),
|
|
),
|
|
(
|
|
{"include_default": True},
|
|
("abc",),
|
|
(None,),
|
|
),
|
|
],
|
|
)
|
|
def test_theme_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test theme selector."""
|
|
_test_selector("theme", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
(
|
|
{
|
|
"entity_id": "sensor.abc",
|
|
"media_content_id": "abc",
|
|
"media_content_type": "def",
|
|
},
|
|
{
|
|
"entity_id": "sensor.abc",
|
|
"media_content_id": "abc",
|
|
"media_content_type": "def",
|
|
"metadata": {},
|
|
},
|
|
),
|
|
(None, "abc", {}),
|
|
),
|
|
],
|
|
)
|
|
def test_media_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test media selector."""
|
|
|
|
def drop_metadata(data):
|
|
"""Drop metadata key from the input."""
|
|
data.pop("metadata", None)
|
|
return data
|
|
|
|
_test_selector(
|
|
"media",
|
|
schema,
|
|
valid_selections,
|
|
invalid_selections,
|
|
drop_metadata,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
("nl", "fr"),
|
|
(None, True, 1),
|
|
),
|
|
(
|
|
{"languages": ["nl", "fr"]},
|
|
("nl", "fr"),
|
|
(None, True, 1, "de", "en"),
|
|
),
|
|
],
|
|
)
|
|
def test_language_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test language selector."""
|
|
_test_selector("language", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
(
|
|
{
|
|
"latitude": 1.0,
|
|
"longitude": 2.0,
|
|
},
|
|
{
|
|
"latitude": 1.0,
|
|
"longitude": 2.0,
|
|
"radius": 3.0,
|
|
},
|
|
{
|
|
"latitude": 1,
|
|
"longitude": 2,
|
|
"radius": 3,
|
|
},
|
|
),
|
|
(
|
|
None,
|
|
"abc",
|
|
{},
|
|
{"latitude": 1.0},
|
|
{"longitude": 1.0},
|
|
),
|
|
),
|
|
],
|
|
)
|
|
def test_location_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test location selector."""
|
|
|
|
_test_selector("location", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
([0, 0, 0], [255, 255, 255], [0.0, 0.0, 0.0], [255.0, 255.0, 255.0]),
|
|
(None, "abc", [0, 0, "nil"], (255, 255, 255)),
|
|
),
|
|
],
|
|
)
|
|
def test_rgb_color_selector_schema(
|
|
schema, valid_selections, invalid_selections
|
|
) -> None:
|
|
"""Test color_rgb selector."""
|
|
|
|
_test_selector("color_rgb", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
(100, 100.0),
|
|
(None, "abc", [100]),
|
|
),
|
|
(
|
|
{"min_mireds": 100, "max_mireds": 200},
|
|
(100, 200),
|
|
(99, 201),
|
|
),
|
|
(
|
|
{"unit": "mired", "min": 100, "max": 200},
|
|
(100, 200),
|
|
(99, 201),
|
|
),
|
|
(
|
|
{"unit": "kelvin", "min": 1000, "max": 2000},
|
|
(1000, 2000),
|
|
(999, 2001),
|
|
),
|
|
],
|
|
)
|
|
def test_color_tempselector_schema(
|
|
schema, valid_selections, invalid_selections
|
|
) -> None:
|
|
"""Test color_temp selector."""
|
|
|
|
_test_selector("color_temp", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
("2022-03-24",),
|
|
(None, "abc", "00:00", "2022-03-24 00:00", "2022-03-32"),
|
|
),
|
|
],
|
|
)
|
|
def test_date_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test date selector."""
|
|
|
|
_test_selector("date", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
("2022-03-24 00:00", "2022-03-24"),
|
|
(None, "abc", "00:00", "2022-03-24 24:01"),
|
|
),
|
|
],
|
|
)
|
|
def test_datetime_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test datetime selector."""
|
|
|
|
_test_selector("datetime", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[({}, ("abc123", "{{ now() }}"), (None, "{{ incomplete }", "{% if True %}Hi!"))],
|
|
)
|
|
def test_template_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test template selector."""
|
|
_test_selector("template", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{"accept": "image/*"},
|
|
("0182a1b99dbc5ae24aecd90c346605fa",),
|
|
(None, "not-a-uuid", "abcd", 1),
|
|
),
|
|
],
|
|
)
|
|
def test_file_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test file selector."""
|
|
|
|
_test_selector("file", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{"value": True, "label": "Blah"},
|
|
(True, 1),
|
|
(None, False, 0, "abc", "def"),
|
|
),
|
|
(
|
|
{"value": False},
|
|
(False, 0),
|
|
(None, True, 1, "abc", "def"),
|
|
),
|
|
(
|
|
{"value": 0},
|
|
(0, False),
|
|
(None, True, 1, "abc", "def"),
|
|
),
|
|
(
|
|
{"value": 1},
|
|
(1, True),
|
|
(None, False, 0, "abc", "def"),
|
|
),
|
|
(
|
|
{"value": 4},
|
|
(4,),
|
|
(None, False, True, 0, 1, "abc", "def"),
|
|
),
|
|
(
|
|
{"value": "dog"},
|
|
("dog",),
|
|
(None, False, True, 0, 1, "abc", "def"),
|
|
),
|
|
],
|
|
)
|
|
def test_constant_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test constant selector."""
|
|
_test_selector("constant", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"schema",
|
|
[
|
|
{}, # Value is mandatory
|
|
{"value": []}, # Value must be str, int or bool
|
|
{"value": 123, "label": 123}, # Label must be str
|
|
],
|
|
)
|
|
def test_constant_selector_schema_error(schema) -> None:
|
|
"""Test constant selector."""
|
|
with pytest.raises(vol.Invalid):
|
|
selector.validate_selector({"constant": schema})
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
("home_assistant", "2j4hp3uy4p87wyrpiuhk34"),
|
|
(None, True, 1),
|
|
),
|
|
(
|
|
{"language": "nl"},
|
|
("home_assistant", "2j4hp3uy4p87wyrpiuhk34"),
|
|
(None, True, 1),
|
|
),
|
|
],
|
|
)
|
|
def test_conversation_agent_selector_schema(
|
|
schema, valid_selections, invalid_selections
|
|
) -> None:
|
|
"""Test conversation agent selector."""
|
|
_test_selector("conversation_agent", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
(
|
|
[
|
|
{
|
|
"condition": "numeric_state",
|
|
"entity_id": ["sensor.temperature"],
|
|
"below": 20,
|
|
}
|
|
],
|
|
[],
|
|
),
|
|
("abc"),
|
|
),
|
|
],
|
|
)
|
|
def test_condition_selector_schema(
|
|
schema, valid_selections, invalid_selections
|
|
) -> None:
|
|
"""Test condition sequence selector."""
|
|
_test_selector("condition", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{},
|
|
(
|
|
[
|
|
{
|
|
"platform": "numeric_state",
|
|
"entity_id": ["sensor.temperature"],
|
|
"below": 20,
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"platform": "numeric_state",
|
|
"entity_id": ["sensor.temperature"],
|
|
"below": 20,
|
|
}
|
|
],
|
|
[],
|
|
),
|
|
("abc"),
|
|
),
|
|
],
|
|
)
|
|
def test_trigger_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test trigger sequence selector."""
|
|
|
|
def _custom_trigger_serializer(
|
|
triggers: list[dict[str, Any]],
|
|
) -> list[dict[str, Any]]:
|
|
res = []
|
|
for trigger in triggers:
|
|
if "trigger" in trigger:
|
|
trigger["platform"] = trigger.pop("trigger")
|
|
res.append(trigger)
|
|
return res
|
|
|
|
_test_selector(
|
|
"trigger",
|
|
schema,
|
|
valid_selections,
|
|
invalid_selections,
|
|
_custom_trigger_serializer,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
(
|
|
{"data": "test", "scale": 5},
|
|
("test",),
|
|
(False, 0, []),
|
|
),
|
|
(
|
|
{"data": "test"},
|
|
("test",),
|
|
(True, 1, []),
|
|
),
|
|
(
|
|
{
|
|
"data": "test",
|
|
"scale": 5,
|
|
"error_correction_level": selector.QrErrorCorrectionLevel.HIGH,
|
|
},
|
|
("test",),
|
|
(True, 1, []),
|
|
),
|
|
],
|
|
)
|
|
def test_qr_code_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test QR code selector."""
|
|
_test_selector("qr_code", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
({}, ("abc123",), (None,)),
|
|
(
|
|
{"multiple": True},
|
|
((["abc123", "def456"],)),
|
|
(None, "abc123", ["abc123", None]),
|
|
),
|
|
],
|
|
)
|
|
def test_label_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test label selector."""
|
|
_test_selector("label", schema, valid_selections, invalid_selections)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("schema", "valid_selections", "invalid_selections"),
|
|
[
|
|
({}, ("abc123",), (None,)),
|
|
({"entity": {}}, ("abc123",), (None,)),
|
|
({"entity": {"domain": "light"}}, ("abc123",), (None,)),
|
|
(
|
|
{"entity": {"domain": "binary_sensor", "device_class": "motion"}},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"entity": {
|
|
"domain": "binary_sensor",
|
|
"device_class": "motion",
|
|
"integration": "demo",
|
|
}
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"entity": [
|
|
{"domain": "light"},
|
|
{"domain": "binary_sensor", "device_class": "motion"},
|
|
]
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{"device": {"integration": "demo", "model": "mock-model"}},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"device": [
|
|
{"integration": "demo", "model": "mock-model"},
|
|
{"integration": "other-demo", "model": "other-mock-model"},
|
|
]
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{
|
|
"entity": {"domain": "binary_sensor", "device_class": "motion"},
|
|
"device": {"integration": "demo", "model": "mock-model"},
|
|
},
|
|
("abc123",),
|
|
(None,),
|
|
),
|
|
(
|
|
{"multiple": True},
|
|
((["abc123", "def456"],)),
|
|
(None, "abc123", ["abc123", None]),
|
|
),
|
|
],
|
|
)
|
|
def test_floor_selector_schema(schema, valid_selections, invalid_selections) -> None:
|
|
"""Test floor selector."""
|
|
_test_selector("floor", schema, valid_selections, invalid_selections)
|