1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-20 10:59:24 +00:00
Files
core/tests/components/template/test_validators.py

925 lines
24 KiB
Python

"""Test template validators."""
from typing import Any
import pytest
from homeassistant.components.template import validators as cv
from homeassistant.components.template.template_entity import TemplateEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers.template import Template
from .conftest import Brewery
def expect_boolean_on(true_value: Any = True) -> list[tuple[Any, Any]]:
"""Tuple of commonn boolean on value expected pairs."""
return [
("on", true_value),
("On", true_value),
("oN", true_value),
("true", true_value),
("yes", true_value),
("enable", true_value),
("1", true_value),
(True, true_value),
(1, true_value),
(8.23432, true_value),
(0.23432, true_value),
]
def expect_boolean_off(false_value: Any = False) -> list[tuple[Any, Any]]:
"""Tuple of commonn boolean off value expected pairs."""
return [
("off", false_value),
("false", false_value),
("no", false_value),
("disable", false_value),
("0", false_value),
(False, false_value),
(0, false_value),
]
def expect_none(*args: Any) -> list[tuple[Any, None]]:
"""Tuple of results that should return None."""
return [(v, None) for v in args]
def check_for_error(value: Any, expected: Any, caplog_text: str, error: str) -> None:
"""Test the validator error."""
if expected is None and value is not None:
assert error in caplog_text
else:
assert error not in caplog_text
def create_test_entity(hass: HomeAssistant, config: dict) -> TemplateEntity:
"""Create a template result handler."""
class Test(TemplateEntity):
_entity_id_format = "test.{}"
config = {
"name": Template("Test", hass),
**config,
}
return Test(hass, config, "something_unique")
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected one of mmmm, beer, is, good",
),
(
{},
"Received invalid state: {} for entity Test, expected one of mmmm, beer, is, good",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("mmmm", Brewery.MMMM),
("MmMM", Brewery.MMMM),
("mmMM", Brewery.MMMM),
("beer", Brewery.BEER),
("is", Brewery.IS),
("good", Brewery.GOOD),
*expect_none(
None,
"mm",
"beeal;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
"7",
"-1",
True,
False,
1,
1.0,
{},
{"junk": "stuff"},
{"junk"},
[],
["stuff"],
),
],
)
async def test_enum(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test enum validator."""
entity = create_test_entity(hass, config)
assert cv.strenum(entity, "state", Brewery)(value) == expected
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
"value",
[
"unknown",
"unavailable",
"UknoWn", # codespell:ignore
"UnavailablE", # codespell:ignore UnavailablE
],
)
async def test_none_on_unknown_and_unavailable(
hass: HomeAssistant,
value: Any,
) -> None:
"""Test enum validator."""
entity = create_test_entity(hass, {})
assert (
cv.strenum(entity, "state", Brewery, none_on_unknown_unavailable=True)(value)
is None
)
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected one of mmmm, beer, is, good, 1, true, yes, on, enable, 0, false, no, off, disable",
),
(
{},
"Received invalid state: {} for entity Test, expected one of mmmm, beer, is, good, 1, true, yes, on, enable, 0, false, no, off, disable",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("mmmm", Brewery.MMMM),
("MmMM", Brewery.MMMM),
("mmMM", Brewery.MMMM),
("beer", Brewery.BEER),
("is", Brewery.IS),
("good", Brewery.GOOD),
*expect_boolean_on(Brewery.MMMM),
*expect_boolean_off(Brewery.BEER),
*expect_none(
None,
"mm",
"beeal;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
"7",
"-1",
{},
{"junk": "stuff"},
{"junk"},
[],
["stuff"],
),
],
)
async def test_enum_with_on_off(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test enum validator."""
entity = create_test_entity(hass, config)
assert (
cv.strenum(entity, "state", Brewery, Brewery.MMMM, Brewery.BEER)(value)
== expected
)
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected one of mmmm, beer, is, good, 1, true, yes, on, enable",
),
(
{},
"Received invalid state: {} for entity Test, expected one of mmmm, beer, is, good, 1, true, yes, on, enable",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("mmmm", Brewery.MMMM),
("MmMM", Brewery.MMMM),
("mmMM", Brewery.MMMM),
("beer", Brewery.BEER),
("is", Brewery.IS),
("good", Brewery.GOOD),
*expect_boolean_on(Brewery.MMMM),
*expect_boolean_off(None),
*expect_none(
None,
"mm",
"beeal;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
"7",
"-1",
{},
{"junk": "stuff"},
{"junk"},
[],
["stuff"],
),
],
)
async def test_enum_with_on(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test enum with state_on validator."""
entity = create_test_entity(hass, config)
assert (
cv.strenum(entity, "state", Brewery, state_on=Brewery.MMMM)(value) == expected
)
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected one of mmmm, beer, is, good, 0, false, no, off, disable",
),
(
{},
"Received invalid state: {} for entity Test, expected one of mmmm, beer, is, good, 0, false, no, off, disable",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("mmmm", Brewery.MMMM),
("MmMM", Brewery.MMMM),
("mmMM", Brewery.MMMM),
("beer", Brewery.BEER),
("is", Brewery.IS),
("good", Brewery.GOOD),
*expect_boolean_on(None),
*expect_boolean_off(Brewery.BEER),
*expect_none(
None,
"mm",
"beeal;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
"7",
"-1",
{},
{"junk": "stuff"},
{"junk"},
[],
["stuff"],
),
],
)
async def test_enum_with_off(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test enum with state_off validator."""
entity = create_test_entity(hass, config)
assert (
cv.strenum(entity, "state", Brewery, state_off=Brewery.BEER)(value) == expected
)
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected one of 1, true, yes, on, enable, 0, false, no, off, disable",
),
(
{},
"Received invalid state: {} for entity Test, expected one of 1, true, yes, on, enable, 0, false, no, off, disable",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
*expect_boolean_on(),
*expect_boolean_off(),
*expect_none(
None,
"al;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
"7",
"-1",
{},
{"junk": "stuff"},
{"junk"},
[],
["stuff"],
),
],
)
async def test_boolean(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test boolean validator."""
entity = create_test_entity(hass, config)
assert cv.boolean(entity, "state")(value) == expected
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected one of 1, true, yes, on, enable, 0, false, no, off, disable",
),
(
{},
"Received invalid state: {} for entity Test, expected one of 1, true, yes, on, enable, 0, false, no, off, disable",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("something_unique", True),
*expect_boolean_on(),
*expect_boolean_off(),
*expect_none(
None,
"al;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
"7",
"-1",
{},
{"junk": "stuff"},
{"junk"},
[],
["stuff"],
),
],
)
async def test_boolean_as_true(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test boolean validator."""
entity = create_test_entity(hass, config)
assert cv.boolean(entity, "state", as_true=("something_unique",))(value) == expected
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected one of 1, true, yes, on, enable, 0, false, no, off, disable, something_unique",
),
(
{},
"Received invalid state: {} for entity Test, expected one of 1, true, yes, on, enable, 0, false, no, off, disable, something_unique",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("something_unique", False),
*expect_boolean_on(),
*expect_boolean_off(),
*expect_none(
None,
"al;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
"7",
"-1",
{},
{"junk": "stuff"},
{"junk"},
[],
["stuff"],
),
],
)
async def test_boolean_as_false(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test boolean validator."""
entity = create_test_entity(hass, config)
assert (
cv.boolean(entity, "state", as_false=("something_unique",))(value) == expected
)
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected a number",
),
(
{},
"Received invalid state: {} for entity Test, expected a number",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("7.5", 7.5),
("0.0", 0.0),
("-324.4564", -324.4564),
("5e-4", 0.0005),
("5e4", 50000.0),
(7.5, 7.5),
(0.0, 0.0),
(-324.4564, -324.4564),
(1, 1.0),
*expect_none(
None,
"al;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
True,
False,
{},
{"junk": "stuff"},
{"junk"},
[],
["stuff"],
),
],
)
async def test_number_as_float(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test number validator."""
entity = create_test_entity(hass, config)
value = cv.number(entity, "state")(value)
assert value == expected
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected a number",
),
(
{},
"Received invalid state: {} for entity Test, expected a number",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("7.5", 7),
("0.0", 0),
("-324.4564", -324),
("5e-4", 0),
("5e4", 50000),
(7.5, 7),
(0.0, 0),
(-324.4564, -324),
(1, 1),
*expect_none(
None,
"al;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
True,
False,
{},
{"junk": "stuff"},
{"junk"},
[],
["stuff"],
),
],
)
async def test_number_as_int(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test number with return_type int validator."""
entity = create_test_entity(hass, config)
value = cv.number(entity, "state", return_type=int)(value)
assert value == expected
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected a number greater than or equal to 0.0",
),
(
{},
"Received invalid state: {} for entity Test, expected a number greater than or equal to 0.0",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("7.5", 7.5),
("0.0", 0),
("5e-4", 0.0005),
("5e4", 50000.0),
(7.5, 7.5),
(0.0, 0),
(1, 1.0),
*expect_none(
None,
"al;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
"-324.4564",
-324.4564,
"-0.00001",
-0.00001,
True,
False,
{},
{"junk": "stuff"},
{"junk"},
[],
["stuff"],
),
],
)
async def test_number_with_minimum(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test number with minimum validator."""
entity = create_test_entity(hass, config)
value = cv.number(entity, "state", minimum=0.0)(value)
assert value == expected
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected a number less than or equal to 0.0",
),
(
{},
"Received invalid state: {} for entity Test, expected a number less than or equal to 0.0",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("-7.5", -7.5),
("0.0", 0),
("-5e-4", -0.0005),
("-5e4", -50000),
(-7.5, -7.5),
(0.0, 0.0),
(-1, -1.0),
*expect_none(
None,
"al;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
"324.4564",
"0.00001",
True,
False,
{},
{"junk": "stuff"},
{"junk"},
),
],
)
async def test_number_with_maximum(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test number with maximum validator."""
entity = create_test_entity(hass, config)
value = cv.number(entity, "state", maximum=0.0)(value)
assert value == expected
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected a number between 0.0 and 100.0",
),
(
{},
"Received invalid state: {} for entity Test, expected a number between 0.0 and 100.0",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("7.5", 7.5),
("0.0", 0),
("0.0012", 0.0012),
("99.0", 99.0),
("100", 100),
*expect_none(
None,
"al;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
"324.4564",
"-5e4101",
True,
False,
{},
{"junk": "stuff"},
{"junk"},
),
],
)
async def test_number_in_range(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test number within a range validator."""
entity = create_test_entity(hass, config)
value = cv.number(entity, "state", minimum=0.0, maximum=100.0)(value)
assert value == expected
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected a list of strings",
),
(
{},
"Received invalid state: {} for entity Test, expected a list of strings",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
(["beer", "is", "good"], ["beer", "is", "good"]),
(["beer", None, True], ["beer", "None", "True"]),
([], []),
(["99.0", 99.0, 99], ["99.0", "99.0", "99"]),
*expect_none(
None,
"al;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
83242.2342,
True,
False,
{},
{"junk": "stuff"},
{"junk"},
),
],
)
async def test_list_of_strings(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test result as a list of strings."""
entity = create_test_entity(hass, config)
value = cv.list_of_strings(entity, "state")(value)
assert value == expected
check_for_error(value, expected, caplog.text, error.format(value))
async def test_list_of_strings_none_on_empty(
hass: HomeAssistant,
) -> None:
"""Test result as a list of strings with an empty list returning None."""
entity = create_test_entity(hass, {"default_entity_id": "test.test"})
value = cv.list_of_strings(entity, "state", none_on_empty=True)([])
assert value is None
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected beer, is, GOOD",
),
(
{},
"Received invalid state: {} for entity Test, expected beer, is, GOOD",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[
("beer", "beer"),
("is", "is"),
("GOOD", "GOOD"),
*expect_none(
None,
"BEER",
"IS",
"good",
"al;dfj",
"unknown",
"unavailable",
"tru", # codespell:ignore tru
83242.2342,
True,
False,
{},
{"junk": "stuff"},
{"junk"},
),
],
)
async def test_item_in_list(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test result is in a list."""
entity = create_test_entity(hass, config)
value = cv.item_in_list(entity, "state", ["beer", "is", "GOOD"])(value)
assert value == expected
check_for_error(value, expected, caplog.text, error.format(value))
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, expected one of beer, is, GOOD",
),
(
{},
"Received invalid state: {} for entity Test, expected one of beer, is, GOOD",
),
],
)
async def test_item_in_list_changes(
hass: HomeAssistant,
config: dict,
error: str,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test test an item is in a list after the list changes."""
entity = create_test_entity(hass, config)
items = ["beer", "is", "GOOD"]
value = cv.item_in_list(entity, "state", items)("mmmm")
assert value is None
assert error.format("mmmm") in caplog.text
items.append("mmmm")
value = cv.item_in_list(entity, "state", items)("mmmm")
assert value == "mmmm"
assert error.format(value) + ", mmmm" not in caplog.text
@pytest.mark.parametrize(
("config", "error"),
[
(
{"default_entity_id": "test.test"},
"Received invalid test state: {} for entity test.test, bar is empty",
),
(
{},
"Received invalid state: {} for entity Test, bar is empty",
),
],
)
@pytest.mark.parametrize(
("value", "expected"),
[("anything", None)],
)
@pytest.mark.parametrize(
("the_list"),
[None, []],
)
async def test_empty_items_in_list(
hass: HomeAssistant,
config: dict,
error: str,
value: Any,
expected: bool | None,
the_list: list | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test result is in a list."""
entity = create_test_entity(hass, config)
value = cv.item_in_list(entity, "state", the_list, "bar")(value)
assert value == expected
check_for_error(value, expected, caplog.text, error.format(value))