From ec1dbd39aee49e99d627a54101d61b02da7684e2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 21 Feb 2026 20:37:28 -0600 Subject: [PATCH] tests --- tests/unit_tests/test_automation.py | 159 ++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 tests/unit_tests/test_automation.py diff --git a/tests/unit_tests/test_automation.py b/tests/unit_tests/test_automation.py new file mode 100644 index 0000000000..b1170c74bb --- /dev/null +++ b/tests/unit_tests/test_automation.py @@ -0,0 +1,159 @@ +"""Tests for esphome.automation module.""" + +from collections.abc import Generator +from unittest.mock import patch + +import pytest + +from esphome.automation import has_deferred_actions +from esphome.util import RegistryEntry + + +def _make_registry(deferred_actions: set[str]) -> dict[str, RegistryEntry]: + """Create a mock ACTION_REGISTRY with specified deferred actions.""" + registry: dict[str, RegistryEntry] = {} + for name in deferred_actions: + registry[name] = RegistryEntry(name, lambda: None, None, None, deferred=True) + return registry + + +@pytest.fixture +def mock_registry() -> Generator[dict[str, RegistryEntry]]: + """Fixture that patches ACTION_REGISTRY with delay, wait_until, script.wait as deferred.""" + registry: dict[str, RegistryEntry] = _make_registry( + {"delay", "wait_until", "script.wait"} + ) + registry["logger.log"] = RegistryEntry( + "logger.log", lambda: None, None, None, deferred=False + ) + with patch("esphome.automation.ACTION_REGISTRY", registry): + yield registry + + +def test_has_deferred_actions_empty_list( + mock_registry: dict[str, RegistryEntry], +) -> None: + assert has_deferred_actions([]) is False + + +def test_has_deferred_actions_empty_dict( + mock_registry: dict[str, RegistryEntry], +) -> None: + assert has_deferred_actions({}) is False + + +def test_has_deferred_actions_non_dict_non_list( + mock_registry: dict[str, RegistryEntry], +) -> None: + assert has_deferred_actions("string") is False + assert has_deferred_actions(42) is False + assert has_deferred_actions(None) is False + + +def test_has_deferred_actions_delay(mock_registry: dict[str, RegistryEntry]) -> None: + assert has_deferred_actions([{"delay": "1s"}]) is True + + +def test_has_deferred_actions_wait_until( + mock_registry: dict[str, RegistryEntry], +) -> None: + assert has_deferred_actions([{"wait_until": {"condition": {}}}]) is True + + +def test_has_deferred_actions_script_wait( + mock_registry: dict[str, RegistryEntry], +) -> None: + assert has_deferred_actions([{"script.wait": "script_id"}]) is True + + +def test_has_deferred_actions_non_deferred( + mock_registry: dict[str, RegistryEntry], +) -> None: + assert has_deferred_actions([{"logger.log": "hello"}]) is False + + +def test_has_deferred_actions_unknown(mock_registry: dict[str, RegistryEntry]) -> None: + assert has_deferred_actions([{"unknown.action": "value"}]) is False + + +def test_has_deferred_actions_nested_in_then( + mock_registry: dict[str, RegistryEntry], +) -> None: + """Deferred action nested inside a non-deferred action's then block.""" + actions: list[dict[str, object]] = [ + { + "logger.log": "first", + "then": [{"delay": "1s"}], + } + ] + assert has_deferred_actions(actions) is True + + +def test_has_deferred_actions_deeply_nested( + mock_registry: dict[str, RegistryEntry], +) -> None: + """Deferred action deeply nested in action structure.""" + actions: list[dict[str, object]] = [ + { + "if": { + "then": [ + {"logger.log": "hello"}, + {"delay": "500ms"}, + ] + } + } + ] + assert has_deferred_actions(actions) is True + + +def test_has_deferred_actions_no_deferred_in_nested( + mock_registry: dict[str, RegistryEntry], +) -> None: + """No deferred actions even with nesting.""" + actions: list[dict[str, object]] = [ + { + "if": { + "then": [ + {"logger.log": "hello"}, + ] + } + } + ] + assert has_deferred_actions(actions) is False + + +def test_has_deferred_actions_multiple_one_deferred( + mock_registry: dict[str, RegistryEntry], +) -> None: + assert ( + has_deferred_actions( + [ + {"logger.log": "first"}, + {"delay": "1s"}, + {"logger.log": "second"}, + ] + ) + is True + ) + + +def test_has_deferred_actions_multiple_none_deferred( + mock_registry: dict[str, RegistryEntry], +) -> None: + assert ( + has_deferred_actions( + [ + {"logger.log": "first"}, + {"logger.log": "second"}, + ] + ) + is False + ) + + +def test_has_deferred_actions_dict_input( + mock_registry: dict[str, RegistryEntry], +) -> None: + """Direct dict input (single action).""" + assert has_deferred_actions({"delay": "1s"}) is True + assert has_deferred_actions({"logger.log": "hello"}) is False