Add async friendly helper for validating config schemas (#123800)

* Add async friendly helper for validating config schemas

* Improve docstrings

* Add tests
This commit is contained in:
Erik Montnemery 2024-08-17 11:01:49 +02:00 committed by GitHub
parent a7bca9bcea
commit 533442f33e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 161 additions and 7 deletions

View file

@ -3,13 +3,16 @@
from collections import OrderedDict
from datetime import date, datetime, timedelta
import enum
from functools import partial
import logging
import os
from socket import _GLOBAL_DEFAULT_TIMEOUT
import threading
from typing import Any
from unittest.mock import Mock, patch
from unittest.mock import ANY, Mock, patch
import uuid
import py
import pytest
import voluptuous as vol
@ -1738,3 +1741,67 @@ def test_determine_script_action_ambiguous() -> None:
def test_determine_script_action_non_ambiguous() -> None:
"""Test determine script action with a non ambiguous action."""
assert cv.determine_script_action({"delay": "00:00:05"}) == "delay"
async def test_async_validate(hass: HomeAssistant, tmpdir: py.path.local) -> None:
"""Test the async_validate helper."""
validator_calls: dict[str, list[int]] = {}
def _mock_validator_schema(real_func, *args):
calls = validator_calls.setdefault(real_func.__name__, [])
calls.append(threading.get_ident())
return real_func(*args)
CV_PREFIX = "homeassistant.helpers.config_validation"
with (
patch(f"{CV_PREFIX}.isdir", wraps=partial(_mock_validator_schema, cv.isdir)),
patch(f"{CV_PREFIX}.string", wraps=partial(_mock_validator_schema, cv.string)),
):
# Assert validation in event loop when not decorated with not_async_friendly
await cv.async_validate(hass, cv.string, "abcd")
assert validator_calls == {"string": [hass.loop_thread_id]}
validator_calls = {}
# Assert validation in executor when decorated with not_async_friendly
await cv.async_validate(hass, cv.isdir, tmpdir)
assert validator_calls == {"isdir": [hass.loop_thread_id, ANY]}
assert validator_calls["isdir"][1] != hass.loop_thread_id
validator_calls = {}
# Assert validation in executor when decorated with not_async_friendly
await cv.async_validate(hass, vol.All(cv.isdir, cv.string), tmpdir)
assert validator_calls == {"isdir": [hass.loop_thread_id, ANY], "string": [ANY]}
assert validator_calls["isdir"][1] != hass.loop_thread_id
assert validator_calls["string"][0] != hass.loop_thread_id
validator_calls = {}
# Assert validation in executor when decorated with not_async_friendly
await cv.async_validate(hass, vol.All(cv.string, cv.isdir), tmpdir)
assert validator_calls == {
"isdir": [hass.loop_thread_id, ANY],
"string": [hass.loop_thread_id, ANY],
}
assert validator_calls["isdir"][1] != hass.loop_thread_id
assert validator_calls["string"][1] != hass.loop_thread_id
validator_calls = {}
# Assert validation in event loop when not using cv.async_validate
cv.isdir(tmpdir)
assert validator_calls == {"isdir": [hass.loop_thread_id]}
validator_calls = {}
# Assert validation in event loop when not using cv.async_validate
vol.All(cv.isdir, cv.string)(tmpdir)
assert validator_calls == {
"isdir": [hass.loop_thread_id],
"string": [hass.loop_thread_id],
}
validator_calls = {}
# Assert validation in event loop when not using cv.async_validate
vol.All(cv.string, cv.isdir)(tmpdir)
assert validator_calls == {
"isdir": [hass.loop_thread_id],
"string": [hass.loop_thread_id],
}
validator_calls = {}