966 lines
26 KiB
Python
966 lines
26 KiB
Python
"""Test config validators."""
|
|
from datetime import timedelta, datetime, date
|
|
import enum
|
|
import os
|
|
from socket import _GLOBAL_DEFAULT_TIMEOUT
|
|
from unittest.mock import Mock, patch
|
|
|
|
import homeassistant
|
|
import pytest
|
|
import voluptuous as vol
|
|
|
|
import homeassistant.helpers.config_validation as cv
|
|
|
|
|
|
def test_boolean():
|
|
"""Test boolean validation."""
|
|
schema = vol.Schema(cv.boolean)
|
|
|
|
for value in ('T', 'negative', 'lock'):
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
for value in ('true', 'On', '1', 'YES', 'enable', 1, True):
|
|
assert schema(value)
|
|
|
|
for value in ('false', 'Off', '0', 'NO', 'disable', 0, False):
|
|
assert not schema(value)
|
|
|
|
|
|
def test_latitude():
|
|
"""Test latitude validation."""
|
|
schema = vol.Schema(cv.latitude)
|
|
|
|
for value in ('invalid', None, -91, 91, '-91', '91', '123.01A'):
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
for value in ('-89', 89, '12.34'):
|
|
schema(value)
|
|
|
|
|
|
def test_longitude():
|
|
"""Test longitude validation."""
|
|
schema = vol.Schema(cv.longitude)
|
|
|
|
for value in ('invalid', None, -181, 181, '-181', '181', '123.01A'):
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
for value in ('-179', 179, '12.34'):
|
|
schema(value)
|
|
|
|
|
|
def test_port():
|
|
"""Test TCP/UDP network port."""
|
|
schema = vol.Schema(cv.port)
|
|
|
|
for value in ('invalid', None, -1, 0, 80000, '81000'):
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
for value in ('1000', 21, 24574):
|
|
schema(value)
|
|
|
|
|
|
def test_isfile():
|
|
"""Validate that the value is an existing file."""
|
|
schema = vol.Schema(cv.isfile)
|
|
|
|
fake_file = 'this-file-does-not.exist'
|
|
assert not os.path.isfile(fake_file)
|
|
|
|
for value in ('invalid', None, -1, 0, 80000, fake_file):
|
|
with pytest.raises(vol.Invalid):
|
|
schema(value)
|
|
|
|
# patching methods that allow us to fake a file existing
|
|
# with write access
|
|
with patch('os.path.isfile', Mock(return_value=True)), \
|
|
patch('os.access', Mock(return_value=True)):
|
|
schema('test.txt')
|
|
|
|
|
|
def test_url():
|
|
"""Test URL."""
|
|
schema = vol.Schema(cv.url)
|
|
|
|
for value in ('invalid', None, 100, 'htp://ha.io', 'http//ha.io',
|
|
'http://??,**', 'https://??,**'):
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
for value in ('http://localhost', 'https://localhost/test/index.html',
|
|
'http://home-assistant.io', 'http://home-assistant.io/test/',
|
|
'https://community.home-assistant.io/'):
|
|
assert schema(value)
|
|
|
|
|
|
def test_platform_config():
|
|
"""Test platform config validation."""
|
|
options = (
|
|
{},
|
|
{'hello': 'world'},
|
|
)
|
|
for value in options:
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
cv.PLATFORM_SCHEMA(value)
|
|
|
|
options = (
|
|
{'platform': 'mqtt'},
|
|
{'platform': 'mqtt', 'beer': 'yes'},
|
|
)
|
|
for value in options:
|
|
cv.PLATFORM_SCHEMA_BASE(value)
|
|
|
|
|
|
def test_ensure_list():
|
|
"""Test ensure_list."""
|
|
schema = vol.Schema(cv.ensure_list)
|
|
assert [] == schema(None)
|
|
assert [1] == schema(1)
|
|
assert [1] == schema([1])
|
|
assert ['1'] == schema('1')
|
|
assert ['1'] == schema(['1'])
|
|
assert [{'1': '2'}] == schema({'1': '2'})
|
|
|
|
|
|
def test_entity_id():
|
|
"""Test entity ID validation."""
|
|
schema = vol.Schema(cv.entity_id)
|
|
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema('invalid_entity')
|
|
|
|
assert schema('sensor.LIGHT') == 'sensor.light'
|
|
|
|
|
|
def test_entity_ids():
|
|
"""Test entity ID validation."""
|
|
schema = vol.Schema(cv.entity_ids)
|
|
|
|
options = (
|
|
'invalid_entity',
|
|
'sensor.light,sensor_invalid',
|
|
['invalid_entity'],
|
|
['sensor.light', 'sensor_invalid'],
|
|
['sensor.light,sensor_invalid'],
|
|
)
|
|
for value in options:
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
options = (
|
|
[],
|
|
['sensor.light'],
|
|
'sensor.light'
|
|
)
|
|
for value in options:
|
|
schema(value)
|
|
|
|
assert schema('sensor.LIGHT, light.kitchen ') == [
|
|
'sensor.light', 'light.kitchen'
|
|
]
|
|
|
|
|
|
def test_entity_domain():
|
|
"""Test entity domain validation."""
|
|
schema = vol.Schema(cv.entity_domain('sensor'))
|
|
|
|
options = (
|
|
'invalid_entity',
|
|
'cover.demo',
|
|
)
|
|
|
|
for value in options:
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
print(value)
|
|
schema(value)
|
|
|
|
assert schema('sensor.LIGHT') == 'sensor.light'
|
|
|
|
|
|
def test_entities_domain():
|
|
"""Test entities domain validation."""
|
|
schema = vol.Schema(cv.entities_domain('sensor'))
|
|
|
|
options = (
|
|
None,
|
|
'',
|
|
'invalid_entity',
|
|
['sensor.light', 'cover.demo'],
|
|
['sensor.light', 'sensor_invalid'],
|
|
)
|
|
|
|
for value in options:
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
options = (
|
|
'sensor.light',
|
|
['SENSOR.light'],
|
|
['sensor.light', 'sensor.demo']
|
|
)
|
|
for value in options:
|
|
schema(value)
|
|
|
|
assert schema('sensor.LIGHT, sensor.demo ') == [
|
|
'sensor.light', 'sensor.demo'
|
|
]
|
|
assert schema(['sensor.light', 'SENSOR.demo']) == [
|
|
'sensor.light', 'sensor.demo'
|
|
]
|
|
|
|
|
|
def test_ensure_list_csv():
|
|
"""Test ensure_list_csv."""
|
|
schema = vol.Schema(cv.ensure_list_csv)
|
|
|
|
options = (
|
|
None,
|
|
12,
|
|
[],
|
|
['string'],
|
|
'string1,string2'
|
|
)
|
|
for value in options:
|
|
schema(value)
|
|
|
|
assert schema('string1, string2 ') == [
|
|
'string1', 'string2'
|
|
]
|
|
|
|
|
|
def test_event_schema():
|
|
"""Test event_schema validation."""
|
|
options = (
|
|
{}, None,
|
|
{
|
|
'event_data': {},
|
|
},
|
|
{
|
|
'event': 'state_changed',
|
|
'event_data': 1,
|
|
},
|
|
)
|
|
for value in options:
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
cv.EVENT_SCHEMA(value)
|
|
|
|
options = (
|
|
{'event': 'state_changed'},
|
|
{'event': 'state_changed', 'event_data': {'hello': 'world'}},
|
|
)
|
|
for value in options:
|
|
cv.EVENT_SCHEMA(value)
|
|
|
|
|
|
def test_icon():
|
|
"""Test icon validation."""
|
|
schema = vol.Schema(cv.icon)
|
|
|
|
for value in (False, 'work'):
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
schema('mdi:work')
|
|
schema('custom:prefix')
|
|
|
|
|
|
def test_time_period():
|
|
"""Test time_period validation."""
|
|
schema = vol.Schema(cv.time_period)
|
|
|
|
options = (
|
|
None, '', 'hello:world', '12:', '12:34:56:78',
|
|
{}, {'wrong_key': -10}
|
|
)
|
|
for value in options:
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
options = (
|
|
'8:20', '23:59', '-8:20', '-23:59:59', '-48:00', {'minutes': 5}, 1, '5'
|
|
)
|
|
for value in options:
|
|
schema(value)
|
|
|
|
assert timedelta(seconds=180) == schema('180')
|
|
assert timedelta(hours=23, minutes=59) == schema('23:59')
|
|
assert -1 * timedelta(hours=1, minutes=15) == schema('-1:15')
|
|
|
|
|
|
def test_service():
|
|
"""Test service validation."""
|
|
schema = vol.Schema(cv.service)
|
|
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema('invalid_turn_on')
|
|
|
|
schema('homeassistant.turn_on')
|
|
|
|
|
|
def test_service_schema():
|
|
"""Test service_schema validation."""
|
|
options = (
|
|
{}, None,
|
|
{
|
|
'service': 'homeassistant.turn_on',
|
|
'service_template': 'homeassistant.turn_on'
|
|
},
|
|
{
|
|
'data': {'entity_id': 'light.kitchen'},
|
|
},
|
|
{
|
|
'service': 'homeassistant.turn_on',
|
|
'data': None
|
|
},
|
|
{
|
|
'service': 'homeassistant.turn_on',
|
|
'data_template': {
|
|
'brightness': '{{ no_end'
|
|
}
|
|
},
|
|
)
|
|
for value in options:
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
cv.SERVICE_SCHEMA(value)
|
|
|
|
options = (
|
|
{'service': 'homeassistant.turn_on'},
|
|
{
|
|
'service': 'homeassistant.turn_on',
|
|
'entity_id': 'light.kitchen',
|
|
},
|
|
{
|
|
'service': 'light.turn_on',
|
|
'entity_id': 'all',
|
|
},
|
|
{
|
|
'service': 'homeassistant.turn_on',
|
|
'entity_id': ['light.kitchen', 'light.ceiling'],
|
|
},
|
|
)
|
|
for value in options:
|
|
cv.SERVICE_SCHEMA(value)
|
|
|
|
|
|
def test_slug():
|
|
"""Test slug validation."""
|
|
schema = vol.Schema(cv.slug)
|
|
|
|
for value in (None, 'hello world'):
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
for value in (12345, 'hello'):
|
|
schema(value)
|
|
|
|
|
|
def test_string():
|
|
"""Test string validation."""
|
|
schema = vol.Schema(cv.string)
|
|
|
|
with pytest.raises(vol.Invalid):
|
|
schema(None)
|
|
|
|
with pytest.raises(vol.Invalid):
|
|
schema([])
|
|
|
|
with pytest.raises(vol.Invalid):
|
|
schema({})
|
|
|
|
for value in (True, 1, 'hello'):
|
|
schema(value)
|
|
|
|
|
|
def test_temperature_unit():
|
|
"""Test temperature unit validation."""
|
|
schema = vol.Schema(cv.temperature_unit)
|
|
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema('K')
|
|
|
|
schema('C')
|
|
schema('F')
|
|
|
|
|
|
def test_x10_address():
|
|
"""Test x10 addr validator."""
|
|
schema = vol.Schema(cv.x10_address)
|
|
with pytest.raises(vol.Invalid):
|
|
schema('Q1')
|
|
schema('q55')
|
|
schema('garbage_addr')
|
|
|
|
schema('a1')
|
|
schema('C11')
|
|
|
|
|
|
def test_template():
|
|
"""Test template validator."""
|
|
schema = vol.Schema(cv.template)
|
|
|
|
for value in (None, '{{ partial_print }', '{% if True %}Hello', ['test']):
|
|
with pytest.raises(vol.Invalid):
|
|
schema(value)
|
|
|
|
options = (
|
|
1, 'Hello',
|
|
'{{ beer }}',
|
|
'{% if 1 == 1 %}Hello{% else %}World{% endif %}',
|
|
)
|
|
for value in options:
|
|
schema(value)
|
|
|
|
|
|
def test_template_complex():
|
|
"""Test template_complex validator."""
|
|
schema = vol.Schema(cv.template_complex)
|
|
|
|
for value in (None, '{{ partial_print }', '{% if True %}Hello'):
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
options = (
|
|
1, 'Hello',
|
|
'{{ beer }}',
|
|
'{% if 1 == 1 %}Hello{% else %}World{% endif %}',
|
|
{'test': 1, 'test2': '{{ beer }}'},
|
|
['{{ beer }}', 1]
|
|
)
|
|
for value in options:
|
|
schema(value)
|
|
|
|
# ensure the validator didn't mutate the input
|
|
assert options == (
|
|
1, 'Hello',
|
|
'{{ beer }}',
|
|
'{% if 1 == 1 %}Hello{% else %}World{% endif %}',
|
|
{'test': 1, 'test2': '{{ beer }}'},
|
|
['{{ beer }}', 1]
|
|
)
|
|
|
|
|
|
def test_time_zone():
|
|
"""Test time zone validation."""
|
|
schema = vol.Schema(cv.time_zone)
|
|
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema('America/Do_Not_Exist')
|
|
|
|
schema('America/Los_Angeles')
|
|
schema('UTC')
|
|
|
|
|
|
def test_date():
|
|
"""Test date validation."""
|
|
schema = vol.Schema(cv.date)
|
|
|
|
for value in ['Not a date', '23:42', '2016-11-23T18:59:08']:
|
|
with pytest.raises(vol.Invalid):
|
|
schema(value)
|
|
|
|
schema(datetime.now().date())
|
|
schema('2016-11-23')
|
|
|
|
|
|
def test_time():
|
|
"""Test date validation."""
|
|
schema = vol.Schema(cv.time)
|
|
|
|
for value in ['Not a time', '2016-11-23', '2016-11-23T18:59:08']:
|
|
with pytest.raises(vol.Invalid):
|
|
schema(value)
|
|
|
|
schema(datetime.now().time())
|
|
schema('23:42:00')
|
|
schema('23:42')
|
|
|
|
|
|
def test_datetime():
|
|
"""Test date time validation."""
|
|
schema = vol.Schema(cv.datetime)
|
|
for value in [date.today(), 'Wrong DateTime', '2016-11-23']:
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
schema(datetime.now())
|
|
schema('2016-11-23T18:59:08')
|
|
|
|
|
|
@pytest.fixture
|
|
def schema():
|
|
"""Create a schema used for testing deprecation."""
|
|
return vol.Schema({
|
|
'venus': cv.boolean,
|
|
'mars': cv.boolean,
|
|
'jupiter': cv.boolean
|
|
})
|
|
|
|
|
|
@pytest.fixture
|
|
def version(monkeypatch):
|
|
"""Patch the version used for testing to 0.5.0."""
|
|
monkeypatch.setattr(homeassistant.const, '__version__', '0.5.0')
|
|
|
|
|
|
def test_deprecated_with_no_optionals(caplog, schema):
|
|
"""
|
|
Test deprecation behaves correctly when optional params are None.
|
|
|
|
Expected behavior:
|
|
- Outputs the appropriate deprecation warning if key is detected
|
|
- Processes schema without changing any values
|
|
- No warning or difference in output if key is not provided
|
|
"""
|
|
deprecated_schema = vol.All(
|
|
cv.deprecated('mars'),
|
|
schema
|
|
)
|
|
|
|
test_data = {'mars': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 1
|
|
assert caplog.records[0].name == __name__
|
|
assert ("The 'mars' option (with value 'True') is deprecated, "
|
|
"please remove it from your configuration") in caplog.text
|
|
assert test_data == output
|
|
|
|
caplog.clear()
|
|
assert len(caplog.records) == 0
|
|
|
|
test_data = {'venus': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 0
|
|
assert test_data == output
|
|
|
|
|
|
def test_deprecated_with_replacement_key(caplog, schema):
|
|
"""
|
|
Test deprecation behaves correctly when only a replacement key is provided.
|
|
|
|
Expected behavior:
|
|
- Outputs the appropriate deprecation warning if key is detected
|
|
- Processes schema moving the value from key to replacement_key
|
|
- Processes schema changing nothing if only replacement_key provided
|
|
- No warning if only replacement_key provided
|
|
- No warning or difference in output if neither key nor
|
|
replacement_key are provided
|
|
"""
|
|
deprecated_schema = vol.All(
|
|
cv.deprecated('mars', replacement_key='jupiter'),
|
|
schema
|
|
)
|
|
|
|
test_data = {'mars': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 1
|
|
assert ("The 'mars' option (with value 'True') is deprecated, "
|
|
"please replace it with 'jupiter'") in caplog.text
|
|
assert {'jupiter': True} == output
|
|
|
|
caplog.clear()
|
|
assert len(caplog.records) == 0
|
|
|
|
test_data = {'jupiter': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 0
|
|
assert test_data == output
|
|
|
|
test_data = {'venus': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 0
|
|
assert test_data == output
|
|
|
|
|
|
def test_deprecated_with_invalidation_version(caplog, schema, version):
|
|
"""
|
|
Test deprecation behaves correctly with only an invalidation_version.
|
|
|
|
Expected behavior:
|
|
- Outputs the appropriate deprecation warning if key is detected
|
|
- Processes schema without changing any values
|
|
- No warning or difference in output if key is not provided
|
|
- Once the invalidation_version is crossed, raises vol.Invalid if key
|
|
is detected
|
|
"""
|
|
deprecated_schema = vol.All(
|
|
cv.deprecated('mars', invalidation_version='1.0.0'),
|
|
schema
|
|
)
|
|
|
|
message = ("The 'mars' option (with value 'True') is deprecated, "
|
|
"please remove it from your configuration. "
|
|
"This option will become invalid in version 1.0.0")
|
|
|
|
test_data = {'mars': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 1
|
|
assert message in caplog.text
|
|
assert test_data == output
|
|
|
|
caplog.clear()
|
|
assert len(caplog.records) == 0
|
|
|
|
test_data = {'venus': False}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 0
|
|
assert test_data == output
|
|
|
|
invalidated_schema = vol.All(
|
|
cv.deprecated('mars', invalidation_version='0.1.0'),
|
|
schema
|
|
)
|
|
test_data = {'mars': True}
|
|
with pytest.raises(vol.MultipleInvalid) as exc_info:
|
|
invalidated_schema(test_data)
|
|
assert ("The 'mars' option (with value 'True') is deprecated, "
|
|
"please remove it from your configuration. This option will "
|
|
"become invalid in version 0.1.0") == str(exc_info.value)
|
|
|
|
|
|
def test_deprecated_with_replacement_key_and_invalidation_version(
|
|
caplog, schema, version
|
|
):
|
|
"""
|
|
Test deprecation behaves with a replacement key & invalidation_version.
|
|
|
|
Expected behavior:
|
|
- Outputs the appropriate deprecation warning if key is detected
|
|
- Processes schema moving the value from key to replacement_key
|
|
- Processes schema changing nothing if only replacement_key provided
|
|
- No warning if only replacement_key provided
|
|
- No warning or difference in output if neither key nor
|
|
replacement_key are provided
|
|
- Once the invalidation_version is crossed, raises vol.Invalid if key
|
|
is detected
|
|
"""
|
|
deprecated_schema = vol.All(
|
|
cv.deprecated(
|
|
'mars', replacement_key='jupiter', invalidation_version='1.0.0'
|
|
),
|
|
schema
|
|
)
|
|
|
|
warning = ("The 'mars' option (with value 'True') is deprecated, "
|
|
"please replace it with 'jupiter'. This option will become "
|
|
"invalid in version 1.0.0")
|
|
|
|
test_data = {'mars': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 1
|
|
assert warning in caplog.text
|
|
assert {'jupiter': True} == output
|
|
|
|
caplog.clear()
|
|
assert len(caplog.records) == 0
|
|
|
|
test_data = {'jupiter': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 0
|
|
assert test_data == output
|
|
|
|
test_data = {'venus': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 0
|
|
assert test_data == output
|
|
|
|
invalidated_schema = vol.All(
|
|
cv.deprecated(
|
|
'mars', replacement_key='jupiter', invalidation_version='0.1.0'
|
|
),
|
|
schema
|
|
)
|
|
test_data = {'mars': True}
|
|
with pytest.raises(vol.MultipleInvalid) as exc_info:
|
|
invalidated_schema(test_data)
|
|
assert ("The 'mars' option (with value 'True') is deprecated, "
|
|
"please replace it with 'jupiter'. This option will become "
|
|
"invalid in version 0.1.0") == str(exc_info.value)
|
|
|
|
|
|
def test_deprecated_with_default(caplog, schema):
|
|
"""
|
|
Test deprecation behaves correctly with a default value.
|
|
|
|
This is likely a scenario that would never occur.
|
|
|
|
Expected behavior:
|
|
- Behaves identically as when the default value was not present
|
|
"""
|
|
deprecated_schema = vol.All(
|
|
cv.deprecated('mars', default=False),
|
|
schema
|
|
)
|
|
|
|
test_data = {'mars': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 1
|
|
assert caplog.records[0].name == __name__
|
|
assert ("The 'mars' option (with value 'True') is deprecated, "
|
|
"please remove it from your configuration") in caplog.text
|
|
assert test_data == output
|
|
|
|
caplog.clear()
|
|
assert len(caplog.records) == 0
|
|
|
|
test_data = {'venus': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 0
|
|
assert test_data == output
|
|
|
|
|
|
def test_deprecated_with_replacement_key_and_default(caplog, schema):
|
|
"""
|
|
Test deprecation with a replacement key and default.
|
|
|
|
Expected behavior:
|
|
- Outputs the appropriate deprecation warning if key is detected
|
|
- Processes schema moving the value from key to replacement_key
|
|
- Processes schema changing nothing if only replacement_key provided
|
|
- No warning if only replacement_key provided
|
|
- No warning if neither key nor replacement_key are provided
|
|
- Adds replacement_key with default value in this case
|
|
"""
|
|
deprecated_schema = vol.All(
|
|
cv.deprecated('mars', replacement_key='jupiter', default=False),
|
|
schema
|
|
)
|
|
|
|
test_data = {'mars': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 1
|
|
assert ("The 'mars' option (with value 'True') is deprecated, "
|
|
"please replace it with 'jupiter'") in caplog.text
|
|
assert {'jupiter': True} == output
|
|
|
|
caplog.clear()
|
|
assert len(caplog.records) == 0
|
|
|
|
test_data = {'jupiter': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 0
|
|
assert test_data == output
|
|
|
|
test_data = {'venus': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 0
|
|
assert {'venus': True, 'jupiter': False} == output
|
|
|
|
deprecated_schema_with_default = vol.All(
|
|
vol.Schema({
|
|
'venus': cv.boolean,
|
|
vol.Optional('mars', default=False): cv.boolean,
|
|
vol.Optional('jupiter', default=False): cv.boolean
|
|
}),
|
|
cv.deprecated('mars', replacement_key='jupiter', default=False)
|
|
)
|
|
|
|
test_data = {'mars': True}
|
|
output = deprecated_schema_with_default(test_data.copy())
|
|
assert len(caplog.records) == 1
|
|
assert ("The 'mars' option (with value 'True') is deprecated, "
|
|
"please replace it with 'jupiter'") in caplog.text
|
|
assert {'jupiter': True} == output
|
|
|
|
|
|
def test_deprecated_with_replacement_key_invalidation_version_default(
|
|
caplog, schema, version
|
|
):
|
|
"""
|
|
Test deprecation with a replacement key, invalidation_version & default.
|
|
|
|
Expected behavior:
|
|
- Outputs the appropriate deprecation warning if key is detected
|
|
- Processes schema moving the value from key to replacement_key
|
|
- Processes schema changing nothing if only replacement_key provided
|
|
- No warning if only replacement_key provided
|
|
- No warning if neither key nor replacement_key are provided
|
|
- Adds replacement_key with default value in this case
|
|
- Once the invalidation_version is crossed, raises vol.Invalid if key
|
|
is detected
|
|
"""
|
|
deprecated_schema = vol.All(
|
|
cv.deprecated(
|
|
'mars', replacement_key='jupiter', invalidation_version='1.0.0',
|
|
default=False
|
|
),
|
|
schema
|
|
)
|
|
|
|
test_data = {'mars': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 1
|
|
assert ("The 'mars' option (with value 'True') is deprecated, "
|
|
"please replace it with 'jupiter'. This option will become "
|
|
"invalid in version 1.0.0") in caplog.text
|
|
assert {'jupiter': True} == output
|
|
|
|
caplog.clear()
|
|
assert len(caplog.records) == 0
|
|
|
|
test_data = {'jupiter': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 0
|
|
assert test_data == output
|
|
|
|
test_data = {'venus': True}
|
|
output = deprecated_schema(test_data.copy())
|
|
assert len(caplog.records) == 0
|
|
assert {'venus': True, 'jupiter': False} == output
|
|
|
|
invalidated_schema = vol.All(
|
|
cv.deprecated(
|
|
'mars', replacement_key='jupiter', invalidation_version='0.1.0'
|
|
),
|
|
schema
|
|
)
|
|
test_data = {'mars': True}
|
|
with pytest.raises(vol.MultipleInvalid) as exc_info:
|
|
invalidated_schema(test_data)
|
|
assert ("The 'mars' option (with value 'True') is deprecated, "
|
|
"please replace it with 'jupiter'. This option will become "
|
|
"invalid in version 0.1.0") == str(exc_info.value)
|
|
|
|
|
|
def test_key_dependency():
|
|
"""Test key_dependency validator."""
|
|
schema = vol.Schema(cv.key_dependency('beer', 'soda'))
|
|
|
|
options = (
|
|
{'beer': None}
|
|
)
|
|
for value in options:
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
options = (
|
|
{'beer': None, 'soda': None},
|
|
{'soda': None}, {}
|
|
)
|
|
for value in options:
|
|
schema(value)
|
|
|
|
|
|
def test_has_at_most_one_key():
|
|
"""Test has_at_most_one_key validator."""
|
|
schema = vol.Schema(cv.has_at_most_one_key('beer', 'soda'))
|
|
|
|
for value in (None, [], {'beer': None, 'soda': None}):
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
for value in ({}, {'beer': None}, {'soda': None}):
|
|
schema(value)
|
|
|
|
|
|
def test_has_at_least_one_key():
|
|
"""Test has_at_least_one_key validator."""
|
|
schema = vol.Schema(cv.has_at_least_one_key('beer', 'soda'))
|
|
|
|
for value in (None, [], {}, {'wine': None}):
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
schema(value)
|
|
|
|
for value in ({'beer': None}, {'soda': None}):
|
|
schema(value)
|
|
|
|
|
|
def test_enum():
|
|
"""Test enum validator."""
|
|
class TestEnum(enum.Enum):
|
|
"""Test enum."""
|
|
|
|
value1 = "Value 1"
|
|
value2 = "Value 2"
|
|
|
|
schema = vol.Schema(cv.enum(TestEnum))
|
|
|
|
with pytest.raises(vol.Invalid):
|
|
schema('value3')
|
|
|
|
|
|
def test_socket_timeout(): # pylint: disable=invalid-name
|
|
"""Test socket timeout validator."""
|
|
schema = vol.Schema(cv.socket_timeout)
|
|
|
|
with pytest.raises(vol.Invalid):
|
|
schema(0.0)
|
|
|
|
with pytest.raises(vol.Invalid):
|
|
schema(-1)
|
|
|
|
assert _GLOBAL_DEFAULT_TIMEOUT == schema(None)
|
|
|
|
assert schema(1) == 1.0
|
|
|
|
|
|
def test_matches_regex():
|
|
"""Test matches_regex validator."""
|
|
schema = vol.Schema(cv.matches_regex('.*uiae.*'))
|
|
|
|
with pytest.raises(vol.Invalid):
|
|
schema(1.0)
|
|
|
|
with pytest.raises(vol.Invalid):
|
|
schema(" nrtd ")
|
|
|
|
test_str = "This is a test including uiae."
|
|
assert (schema(test_str) == test_str)
|
|
|
|
|
|
def test_is_regex():
|
|
"""Test the is_regex validator."""
|
|
schema = vol.Schema(cv.is_regex)
|
|
|
|
with pytest.raises(vol.Invalid):
|
|
schema("(")
|
|
|
|
with pytest.raises(vol.Invalid):
|
|
schema({"a dict": "is not a regex"})
|
|
|
|
valid_re = ".*"
|
|
schema(valid_re)
|
|
|
|
|
|
def test_comp_entity_ids():
|
|
"""Test config validation for component entity IDs."""
|
|
schema = vol.Schema(cv.comp_entity_ids)
|
|
|
|
for valid in ('ALL', 'all', 'AlL', 'light.kitchen', ['light.kitchen'],
|
|
['light.kitchen', 'light.ceiling'], []):
|
|
schema(valid)
|
|
|
|
for invalid in (['light.kitchen', 'not-entity-id'], '*', ''):
|
|
with pytest.raises(vol.Invalid):
|
|
schema(invalid)
|
|
|
|
|
|
def test_schema_with_slug_keys_allows_old_slugs(caplog):
|
|
"""Test schema with slug keys allowing old slugs."""
|
|
schema = cv.schema_with_slug_keys(str)
|
|
|
|
with patch.dict(cv.INVALID_SLUGS_FOUND, clear=True):
|
|
for value in ('_world', 'wow__yeah'):
|
|
caplog.clear()
|
|
# Will raise if not allowing old slugs
|
|
schema({value: 'yo'})
|
|
assert "Found invalid slug {}".format(value) in caplog.text
|
|
|
|
assert len(cv.INVALID_SLUGS_FOUND) == 2
|
|
|
|
|
|
def test_entity_id_allow_old_validation(caplog):
|
|
"""Test schema allowing old entity_ids."""
|
|
schema = vol.Schema(cv.entity_id)
|
|
|
|
with patch.dict(cv.INVALID_ENTITY_IDS_FOUND, clear=True):
|
|
for value in ('hello.__world', 'great.wow__yeah'):
|
|
caplog.clear()
|
|
# Will raise if not allowing old entity ID
|
|
schema(value)
|
|
assert "Found invalid entity_id {}".format(value) in caplog.text
|
|
|
|
assert len(cv.INVALID_ENTITY_IDS_FOUND) == 2
|