Allow parameterizing YAML config in tests (#87981)

* Add fixture to parameterize yaml config

* Apply to more tests

* Re-add @fixture label

* Add fixtures to patch yaml content and targets

* Typo

* Improve docstr

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

* Update references to mock_yaml_configuration

* Apply new fixtures

* Apply to check_config tests

* Follow up comments

* Rename fixtures, update docstr

* Split paths

* Patch load_yaml_config_file instead

* sort

* Fix tests

* improve docst

* Rename fixtures

* sorting

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

* Improve docstr

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

* Improve docstr

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

* Improve docstr

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

* Improve docstr

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Improve docstr

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Improve docstr

Co-authored-by: Erik Montnemery <erik@montnemery.com>

---------

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
Jan Bouwhuis 2023-02-20 16:57:12 +01:00 committed by GitHub
parent 1759f58fc1
commit 4f6a25b470
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 498 additions and 383 deletions

View File

@ -109,6 +109,9 @@ _TEST_FIXTURES: dict[str, list[str] | str] = {
"hass_admin_user": "MockUser",
"hass_client": "ClientSessionGenerator",
"hass_client_no_auth": "ClientSessionGenerator",
"hass_config": "ConfigType | None",
"hass_config_yaml": "str | None",
"hass_config_yaml_files": "dict[str, str] | None",
"hass_owner_user": "MockUser",
"hass_read_only_access_token": "str",
"hass_read_only_user": "MockUser",
@ -126,6 +129,8 @@ _TEST_FIXTURES: dict[str, list[str] | str] = {
"mock_bluetooth_adapters": "None",
"mock_device_tracker_conf": "list[Device]",
"mock_get_source_ip": "None",
"mock_hass_config": "None",
"mock_hass_config_yaml": "None",
"mock_zeroconf": "None",
"mqtt_client_mock": "MqttMockPahoClient",
"mqtt_mock": "MqttMockHAClient",

View File

@ -1,29 +1,39 @@
"""The tests for the Event automation."""
from unittest.mock import AsyncMock, patch
from unittest.mock import patch
import pytest
import homeassistant.components.automation as automation
from homeassistant.core import CoreState
from homeassistant.core import CoreState, HomeAssistant
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import async_setup_component
from tests.common import async_mock_service
async def test_if_fires_on_hass_start(hass):
@pytest.mark.parametrize(
"hass_config",
[
{
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "homeassistant", "event": "start"},
"action": {
"service": "test.automation",
"data_template": {"id": "{{ trigger.id}}"},
},
}
}
],
)
async def test_if_fires_on_hass_start(
hass: HomeAssistant, mock_hass_config: None, hass_config: ConfigType | None
) -> None:
"""Test the firing when Home Assistant starts."""
calls = async_mock_service(hass, "test", "automation")
hass.state = CoreState.not_running
config = {
automation.DOMAIN: {
"alias": "hello",
"trigger": {"platform": "homeassistant", "event": "start"},
"action": {
"service": "test.automation",
"data_template": {"id": "{{ trigger.id}}"},
},
}
}
assert await async_setup_component(hass, automation.DOMAIN, config)
assert await async_setup_component(hass, automation.DOMAIN, hass_config)
assert automation.is_on(hass, "automation.hello")
assert len(calls) == 0
@ -32,13 +42,9 @@ async def test_if_fires_on_hass_start(hass):
assert automation.is_on(hass, "automation.hello")
assert len(calls) == 1
with patch(
"homeassistant.config.async_hass_config_yaml",
AsyncMock(return_value=config),
):
await hass.services.async_call(
automation.DOMAIN, automation.SERVICE_RELOAD, blocking=True
)
await hass.services.async_call(
automation.DOMAIN, automation.SERVICE_RELOAD, blocking=True
)
assert automation.is_on(hass, "automation.hello")
assert len(calls) == 1

View File

@ -1,6 +1,6 @@
"""Tests for the diagnostics data provided by the KNX integration."""
from unittest.mock import patch
import pytest
from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT
from homeassistant.components.knx.const import (
@ -29,37 +29,40 @@ from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@pytest.mark.parametrize("hass_config", [{}])
async def test_diagnostics(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
mock_config_entry: MockConfigEntry,
knx: KNXTestKit,
mock_hass_config: None,
) -> None:
"""Test diagnostics."""
await knx.setup_integration({})
with patch("homeassistant.config.async_hass_config_yaml", return_value={}):
# Overwrite the version for this test since we don't want to change this with every library bump
knx.xknx.version = "1.0.0"
assert await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry
) == {
"config_entry_data": {
"connection_type": "automatic",
"individual_address": "0.0.240",
"multicast_group": "224.0.23.12",
"multicast_port": 3671,
"rate_limit": 0,
"state_updater": True,
},
"configuration_error": None,
"configuration_yaml": None,
"xknx": {"current_address": "0.0.0", "version": "1.0.0"},
}
# Overwrite the version for this test since we don't want to change this with every library bump
knx.xknx.version = "1.0.0"
assert await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry
) == {
"config_entry_data": {
"connection_type": "automatic",
"individual_address": "0.0.240",
"multicast_group": "224.0.23.12",
"multicast_port": 3671,
"rate_limit": 0,
"state_updater": True,
},
"configuration_error": None,
"configuration_yaml": None,
"xknx": {"current_address": "0.0.0", "version": "1.0.0"},
}
@pytest.mark.parametrize("hass_config", [{"knx": {"wrong_key": {}}}])
async def test_diagnostic_config_error(
hass: HomeAssistant,
mock_hass_config: None,
hass_client: ClientSessionGenerator,
mock_config_entry: MockConfigEntry,
knx: KNXTestKit,
@ -67,32 +70,30 @@ async def test_diagnostic_config_error(
"""Test diagnostics."""
await knx.setup_integration({})
with patch(
"homeassistant.config.async_hass_config_yaml",
return_value={"knx": {"wrong_key": {}}},
):
# Overwrite the version for this test since we don't want to change this with every library bump
knx.xknx.version = "1.0.0"
assert await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry
) == {
"config_entry_data": {
"connection_type": "automatic",
"individual_address": "0.0.240",
"multicast_group": "224.0.23.12",
"multicast_port": 3671,
"rate_limit": 0,
"state_updater": True,
},
"configuration_error": "extra keys not allowed @ data['knx']['wrong_key']",
"configuration_yaml": {"wrong_key": {}},
"xknx": {"current_address": "0.0.0", "version": "1.0.0"},
}
# Overwrite the version for this test since we don't want to change this with every library bump
knx.xknx.version = "1.0.0"
assert await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry
) == {
"config_entry_data": {
"connection_type": "automatic",
"individual_address": "0.0.240",
"multicast_group": "224.0.23.12",
"multicast_port": 3671,
"rate_limit": 0,
"state_updater": True,
},
"configuration_error": "extra keys not allowed @ data['knx']['wrong_key']",
"configuration_yaml": {"wrong_key": {}},
"xknx": {"current_address": "0.0.0", "version": "1.0.0"},
}
@pytest.mark.parametrize("hass_config", [{}])
async def test_diagnostic_redact(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
mock_hass_config: None,
) -> None:
"""Test diagnostics redacting data."""
mock_config_entry: MockConfigEntry = MockConfigEntry(
@ -114,25 +115,24 @@ async def test_diagnostic_redact(
knx: KNXTestKit = KNXTestKit(hass, mock_config_entry)
await knx.setup_integration({})
with patch("homeassistant.config.async_hass_config_yaml", return_value={}):
# Overwrite the version for this test since we don't want to change this with every library bump
knx.xknx.version = "1.0.0"
assert await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry
) == {
"config_entry_data": {
"connection_type": "automatic",
"individual_address": "0.0.240",
"multicast_group": "224.0.23.12",
"multicast_port": 3671,
"rate_limit": 0,
"state_updater": True,
"knxkeys_password": "**REDACTED**",
"user_password": "**REDACTED**",
"device_authentication": "**REDACTED**",
"backbone_key": "**REDACTED**",
},
"configuration_error": None,
"configuration_yaml": None,
"xknx": {"current_address": "0.0.0", "version": "1.0.0"},
}
# Overwrite the version for this test since we don't want to change this with every library bump
knx.xknx.version = "1.0.0"
assert await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry
) == {
"config_entry_data": {
"connection_type": "automatic",
"individual_address": "0.0.240",
"multicast_group": "224.0.23.12",
"multicast_port": 3671,
"rate_limit": 0,
"state_updater": True,
"knxkeys_password": "**REDACTED**",
"user_password": "**REDACTED**",
"device_authentication": "**REDACTED**",
"backbone_key": "**REDACTED**",
},
"configuration_error": None,
"configuration_yaml": None,
"xknx": {"current_address": "0.0.0", "version": "1.0.0"},
}

View File

@ -44,6 +44,7 @@ from homeassistant.components.websocket_api.auth import (
TYPE_AUTH_REQUIRED,
)
from homeassistant.components.websocket_api.http import URL
from homeassistant.config import YAML_CONFIG_FILE
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import HASSIO_USER_NAME
from homeassistant.core import CoreState, HomeAssistant
@ -90,6 +91,7 @@ from .common import ( # noqa: E402, isort:skip
get_test_home_assistant,
init_recorder_component,
mock_storage,
patch_yaml_files,
)
from .test_util.aiohttp import ( # noqa: E402, isort:skip
AiohttpClientMocker,
@ -917,6 +919,66 @@ async def _mqtt_mock_entry(
yield _setup_mqtt_entry
@pytest.fixture
def hass_config() -> ConfigType | None:
"""Fixture to parametrize the content of main configuration using mock_hass_config.
To set a configuration, tests can be marked with:
@pytest.mark.parametrize("hass_config", [{integration: {...}}])
Add the `mock_hass_config: None` fixture to the test.
"""
return None
@pytest.fixture
def mock_hass_config(
hass: HomeAssistant, hass_config: ConfigType | None
) -> Generator[None, None, None]:
"""Fixture to mock the content of main configuration.
Patches homeassistant.config.load_yaml_config_file with `hass_config` parameterized as content.
"""
with patch("homeassistant.config.load_yaml_config_file", return_value=hass_config):
yield
@pytest.fixture
def hass_config_yaml() -> str | None:
"""Fixture to parametrize the content of configuration.yaml file.
To set yaml content, tests can be marked with:
@pytest.mark.parametrize("hass_config_yaml", ["..."])
Add the `mock_hass_config_yaml: None` fixture to the test.
"""
return None
@pytest.fixture
def hass_config_yaml_files(hass_config_yaml: str | None) -> dict[str, str] | None:
"""Fixture to parametrize multiple yaml configuration files.
To set the YAML files to patch, tests can be marked with:
@pytest.mark.parametrize(
"hass_config_yaml_files", [{"configuration.yaml": "..."}]
)
Add the `mock_hass_config_yaml: None` fixture to the test.
"""
return None if hass_config_yaml is None else {YAML_CONFIG_FILE: hass_config_yaml}
@pytest.fixture
def mock_hass_config_yaml(
hass: HomeAssistant, hass_config_yaml_files: dict[str, str] | None
) -> Generator[None, None, None]:
"""Fixture to mock the content of the yaml configuration files.
Patches yaml configuration files using the `hass_config_yaml`
and `hass_config_yaml_files` fixtures.
"""
with patch_yaml_files(hass_config_yaml_files):
yield
@pytest.fixture
async def mqtt_mock_entry_no_yaml_config(
hass: HomeAssistant,

View File

@ -6,7 +6,7 @@ import pytest
from homeassistant.config import YAML_CONFIG_FILE
import homeassistant.scripts.check_config as check_config
from tests.common import get_test_config_dir, patch_yaml_files
from tests.common import get_test_config_dir
BASE_CONFIG = (
"homeassistant:\n"
@ -43,114 +43,118 @@ def normalize_yaml_files(check_dict):
return [key.replace(root, "...") for key in sorted(check_dict["yaml_files"].keys())]
def test_bad_core_config(mock_is_file, event_loop) -> None:
@pytest.mark.parametrize("hass_config_yaml", [BAD_CORE_CONFIG])
def test_bad_core_config(mock_is_file, event_loop, mock_hass_config_yaml: None) -> None:
"""Test a bad core config setup."""
files = {YAML_CONFIG_FILE: BAD_CORE_CONFIG}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir())
assert res["except"].keys() == {"homeassistant"}
assert res["except"]["homeassistant"][1] == {"unit_system": "bad"}
res = check_config.check(get_test_config_dir())
assert res["except"].keys() == {"homeassistant"}
assert res["except"]["homeassistant"][1] == {"unit_system": "bad"}
def test_config_platform_valid(mock_is_file, event_loop) -> None:
@pytest.mark.parametrize("hass_config_yaml", [BASE_CONFIG + "light:\n platform: demo"])
def test_config_platform_valid(
mock_is_file, event_loop, mock_hass_config_yaml: None
) -> None:
"""Test a valid platform setup."""
files = {YAML_CONFIG_FILE: BASE_CONFIG + "light:\n platform: demo"}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir())
assert res["components"].keys() == {"homeassistant", "light"}
assert res["components"]["light"] == [{"platform": "demo"}]
assert res["except"] == {}
assert res["secret_cache"] == {}
assert res["secrets"] == {}
assert len(res["yaml_files"]) == 1
res = check_config.check(get_test_config_dir())
assert res["components"].keys() == {"homeassistant", "light"}
assert res["components"]["light"] == [{"platform": "demo"}]
assert res["except"] == {}
assert res["secret_cache"] == {}
assert res["secrets"] == {}
assert len(res["yaml_files"]) == 1
def test_component_platform_not_found(mock_is_file, event_loop) -> None:
@pytest.mark.parametrize(
("hass_config_yaml", "platforms", "error"),
[
(
BASE_CONFIG + "beer:",
{"homeassistant"},
"Integration error: beer - Integration 'beer' not found.",
),
(
BASE_CONFIG + "light:\n platform: beer",
{"homeassistant", "light"},
"Platform error light.beer - Integration 'beer' not found.",
),
],
)
def test_component_platform_not_found(
mock_is_file, event_loop, mock_hass_config_yaml: None, platforms, error
) -> None:
"""Test errors if component or platform not found."""
# Make sure they don't exist
files = {YAML_CONFIG_FILE: BASE_CONFIG + "beer:"}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir())
assert res["components"].keys() == {"homeassistant"}
assert res["except"] == {
check_config.ERROR_STR: [
"Integration error: beer - Integration 'beer' not found."
]
res = check_config.check(get_test_config_dir())
assert res["components"].keys() == platforms
assert res["except"] == {check_config.ERROR_STR: [error]}
assert res["secret_cache"] == {}
assert res["secrets"] == {}
assert len(res["yaml_files"]) == 1
@pytest.mark.parametrize(
"hass_config_yaml_files",
[
{
get_test_config_dir(YAML_CONFIG_FILE): BASE_CONFIG
+ "http:\n cors_allowed_origins: !secret http_pw",
get_test_config_dir(
"secrets.yaml"
): "logger: debug\nhttp_pw: http://google.com",
}
assert res["secret_cache"] == {}
assert res["secrets"] == {}
assert len(res["yaml_files"]) == 1
files = {YAML_CONFIG_FILE: BASE_CONFIG + "light:\n platform: beer"}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir())
assert res["components"].keys() == {"homeassistant", "light"}
assert res["components"]["light"] == []
assert res["except"] == {
check_config.ERROR_STR: [
"Platform error light.beer - Integration 'beer' not found."
]
}
assert res["secret_cache"] == {}
assert res["secrets"] == {}
assert len(res["yaml_files"]) == 1
def test_secrets(mock_is_file, event_loop) -> None:
],
)
def test_secrets(mock_is_file, event_loop, mock_hass_config_yaml: None) -> None:
"""Test secrets config checking method."""
secrets_path = get_test_config_dir("secrets.yaml")
res = check_config.check(get_test_config_dir(), True)
files = {
get_test_config_dir(YAML_CONFIG_FILE): BASE_CONFIG
+ "http:\n cors_allowed_origins: !secret http_pw",
secrets_path: "logger: debug\nhttp_pw: http://google.com",
assert res["except"] == {}
assert res["components"].keys() == {"homeassistant", "http"}
assert res["components"]["http"] == {
"cors_allowed_origins": ["http://google.com"],
"ip_ban_enabled": True,
"login_attempts_threshold": -1,
"server_port": 8123,
"ssl_profile": "modern",
}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir(), True)
assert res["except"] == {}
assert res["components"].keys() == {"homeassistant", "http"}
assert res["components"]["http"] == {
"cors_allowed_origins": ["http://google.com"],
"ip_ban_enabled": True,
"login_attempts_threshold": -1,
"server_port": 8123,
"ssl_profile": "modern",
}
assert res["secret_cache"] == {secrets_path: {"http_pw": "http://google.com"}}
assert res["secrets"] == {"http_pw": "http://google.com"}
assert normalize_yaml_files(res) == [
".../configuration.yaml",
".../secrets.yaml",
]
assert res["secret_cache"] == {
get_test_config_dir("secrets.yaml"): {"http_pw": "http://google.com"}
}
assert res["secrets"] == {"http_pw": "http://google.com"}
assert normalize_yaml_files(res) == [
".../configuration.yaml",
".../secrets.yaml",
]
def test_package_invalid(mock_is_file, event_loop) -> None:
@pytest.mark.parametrize(
"hass_config_yaml", [BASE_CONFIG + ' packages:\n p1:\n group: ["a"]']
)
def test_package_invalid(mock_is_file, event_loop, mock_hass_config_yaml: None) -> None:
"""Test an invalid package."""
files = {YAML_CONFIG_FILE: BASE_CONFIG + ' packages:\n p1:\n group: ["a"]'}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir())
res = check_config.check(get_test_config_dir())
assert res["except"].keys() == {"homeassistant.packages.p1.group"}
assert res["except"]["homeassistant.packages.p1.group"][1] == {"group": ["a"]}
assert len(res["except"]) == 1
assert res["components"].keys() == {"homeassistant"}
assert len(res["components"]) == 1
assert res["secret_cache"] == {}
assert res["secrets"] == {}
assert len(res["yaml_files"]) == 1
assert res["except"].keys() == {"homeassistant.packages.p1.group"}
assert res["except"]["homeassistant.packages.p1.group"][1] == {"group": ["a"]}
assert len(res["except"]) == 1
assert res["components"].keys() == {"homeassistant"}
assert len(res["components"]) == 1
assert res["secret_cache"] == {}
assert res["secrets"] == {}
assert len(res["yaml_files"]) == 1
def test_bootstrap_error(event_loop) -> None:
@pytest.mark.parametrize(
"hass_config_yaml", [BASE_CONFIG + "automation: !include no.yaml"]
)
def test_bootstrap_error(event_loop, mock_hass_config_yaml: None) -> None:
"""Test a valid platform setup."""
files = {YAML_CONFIG_FILE: BASE_CONFIG + "automation: !include no.yaml"}
with patch_yaml_files(files):
res = check_config.check(get_test_config_dir(YAML_CONFIG_FILE))
err = res["except"].pop(check_config.ERROR_STR)
assert len(err) == 1
assert res["except"] == {}
assert res["components"] == {} # No components, load failed
assert res["secret_cache"] == {}
assert res["secrets"] == {}
assert res["yaml_files"] == {}
res = check_config.check(get_test_config_dir(YAML_CONFIG_FILE))
err = res["except"].pop(check_config.ERROR_STR)
assert len(err) == 1
assert res["except"] == {}
assert res["components"] == {} # No components, load failed
assert res["secret_cache"] == {}
assert res["secrets"] == {}
assert res["yaml_files"] == {}

View File

@ -454,7 +454,9 @@ def mock_ensure_config_exists():
yield ensure_config_exists
@pytest.mark.parametrize("hass_config", [{"browser": {}, "frontend": {}}])
async def test_setup_hass(
mock_hass_config: None,
mock_enable_logging,
mock_is_virtual_env,
mock_mount_local_lib_path,
@ -462,17 +464,14 @@ async def test_setup_hass(
mock_process_ha_config_upgrade,
caplog,
event_loop,
):
) -> None:
"""Test it works."""
verbose = Mock()
log_rotate_days = Mock()
log_file = Mock()
log_no_color = Mock()
with patch(
"homeassistant.config.async_hass_config_yaml",
return_value={"browser": {}, "frontend": {}},
), patch.object(bootstrap, "LOG_SLOW_STARTUP_INTERVAL", 5000):
with patch.object(bootstrap, "LOG_SLOW_STARTUP_INTERVAL", 5000):
hass = await bootstrap.async_setup_hass(
runner.RuntimeConfig(
config_dir=get_test_config_dir(),
@ -505,7 +504,9 @@ async def test_setup_hass(
assert hass == async_get_hass()
@pytest.mark.parametrize("hass_config", [{"browser": {}, "frontend": {}}])
async def test_setup_hass_takes_longer_than_log_slow_startup(
mock_hass_config: None,
mock_enable_logging,
mock_is_virtual_env,
mock_mount_local_lib_path,
@ -513,7 +514,7 @@ async def test_setup_hass_takes_longer_than_log_slow_startup(
mock_process_ha_config_upgrade,
caplog,
event_loop,
):
) -> None:
"""Test it works."""
verbose = Mock()
log_rotate_days = Mock()
@ -524,10 +525,7 @@ async def test_setup_hass_takes_longer_than_log_slow_startup(
await asyncio.sleep(0.6)
return True
with patch(
"homeassistant.config.async_hass_config_yaml",
return_value={"browser": {}, "frontend": {}},
), patch.object(bootstrap, "LOG_SLOW_STARTUP_INTERVAL", 0.3), patch.object(
with patch.object(bootstrap, "LOG_SLOW_STARTUP_INTERVAL", 0.3), patch.object(
bootstrap, "SLOW_STARTUP_CHECK_INTERVAL", 0.05
), patch(
"homeassistant.components.frontend.async_setup",
@ -555,7 +553,7 @@ async def test_setup_hass_invalid_yaml(
mock_ensure_config_exists,
mock_process_ha_config_upgrade,
event_loop,
):
) -> None:
"""Test it works."""
with patch(
"homeassistant.config.async_hass_config_yaml", side_effect=HomeAssistantError
@ -636,70 +634,71 @@ async def test_setup_hass_safe_mode(
assert len(browser_setup.mock_calls) == 0
@pytest.mark.parametrize("hass_config", [{"homeassistant": {"non-existing": 1}}])
async def test_setup_hass_invalid_core_config(
mock_hass_config: None,
mock_enable_logging,
mock_is_virtual_env,
mock_mount_local_lib_path,
mock_ensure_config_exists,
mock_process_ha_config_upgrade,
event_loop,
):
) -> None:
"""Test it works."""
with patch(
"homeassistant.config.async_hass_config_yaml",
return_value={"homeassistant": {"non-existing": 1}},
):
hass = await bootstrap.async_setup_hass(
runner.RuntimeConfig(
config_dir=get_test_config_dir(),
verbose=False,
log_rotate_days=10,
log_file="",
log_no_color=False,
skip_pip=True,
safe_mode=False,
),
)
hass = await bootstrap.async_setup_hass(
runner.RuntimeConfig(
config_dir=get_test_config_dir(),
verbose=False,
log_rotate_days=10,
log_file="",
log_no_color=False,
skip_pip=True,
safe_mode=False,
),
)
assert "safe_mode" in hass.config.components
async def test_setup_safe_mode_if_no_frontend(
mock_enable_logging,
mock_is_virtual_env,
mock_mount_local_lib_path,
mock_ensure_config_exists,
mock_process_ha_config_upgrade,
event_loop,
):
"""Test we setup safe mode if frontend didn't load."""
verbose = Mock()
log_rotate_days = Mock()
log_file = Mock()
log_no_color = Mock()
with patch(
"homeassistant.config.async_hass_config_yaml",
return_value={
@pytest.mark.parametrize(
"hass_config",
[
{
"homeassistant": {
"internal_url": "http://192.168.1.100:8123",
"external_url": "https://abcdef.ui.nabu.casa",
},
"map": {},
"person": {"invalid": True},
},
):
hass = await bootstrap.async_setup_hass(
runner.RuntimeConfig(
config_dir=get_test_config_dir(),
verbose=verbose,
log_rotate_days=log_rotate_days,
log_file=log_file,
log_no_color=log_no_color,
skip_pip=True,
safe_mode=False,
),
)
}
],
)
async def test_setup_safe_mode_if_no_frontend(
mock_hass_config: None,
mock_enable_logging,
mock_is_virtual_env,
mock_mount_local_lib_path,
mock_ensure_config_exists,
mock_process_ha_config_upgrade,
event_loop,
) -> None:
"""Test we setup safe mode if frontend didn't load."""
verbose = Mock()
log_rotate_days = Mock()
log_file = Mock()
log_no_color = Mock()
hass = await bootstrap.async_setup_hass(
runner.RuntimeConfig(
config_dir=get_test_config_dir(),
verbose=verbose,
log_rotate_days=log_rotate_days,
log_file=log_file,
log_no_color=log_no_color,
skip_pip=True,
safe_mode=False,
),
)
assert "safe_mode" in hass.config.components
assert hass.config.config_dir == get_test_config_dir()

View File

@ -40,7 +40,7 @@ from homeassistant.util.unit_system import (
)
from homeassistant.util.yaml import SECRET_YAML
from .common import MockUser, get_test_config_dir, patch_yaml_files
from .common import MockUser, get_test_config_dir
CONFIG_DIR = get_test_config_dir()
YAML_PATH = os.path.join(CONFIG_DIR, config_util.YAML_CONFIG_FILE)
@ -736,20 +736,25 @@ async def test_check_ha_config_file_wrong(mock_check, hass):
assert await config_util.async_check_ha_config_file(hass) == "bad"
@patch("homeassistant.config.os.path.isfile", mock.Mock(return_value=True))
async def test_async_hass_config_yaml_merge(merge_log_err, hass):
@pytest.mark.parametrize(
"hass_config",
[
{
config_util.CONF_CORE: {
config_util.CONF_PACKAGES: {
"pack_dict": {"input_boolean": {"ib1": None}}
}
},
"input_boolean": {"ib2": None},
"light": {"platform": "test"},
}
],
)
async def test_async_hass_config_yaml_merge(
merge_log_err, hass: HomeAssistant, mock_hass_config: None
) -> None:
"""Test merge during async config reload."""
config = {
config_util.CONF_CORE: {
config_util.CONF_PACKAGES: {"pack_dict": {"input_boolean": {"ib1": None}}}
},
"input_boolean": {"ib2": None},
"light": {"platform": "test"},
}
files = {config_util.YAML_CONFIG_FILE: yaml.dump(config)}
with patch_yaml_files(files, True):
conf = await config_util.async_hass_config_yaml(hass)
conf = await config_util.async_hass_config_yaml(hass)
assert merge_log_err.call_count == 0
assert conf[config_util.CONF_CORE].get(config_util.CONF_PACKAGES) is not None

View File

@ -3,6 +3,7 @@ import importlib
import io
import os
import pathlib
from typing import Any
import unittest
from unittest.mock import patch
@ -67,17 +68,17 @@ def test_simple_dict(try_both_loaders):
assert doc["key"] == "value"
def test_unhashable_key() -> None:
@pytest.mark.parametrize("hass_config_yaml", ["message:\n {{ states.state }}"])
def test_unhashable_key(mock_hass_config_yaml: None) -> None:
"""Test an unhashable key."""
files = {YAML_CONFIG_FILE: "message:\n {{ states.state }}"}
with pytest.raises(HomeAssistantError), patch_yaml_files(files):
with pytest.raises(HomeAssistantError):
load_yaml_config_file(YAML_CONFIG_FILE)
def test_no_key(try_both_loaders):
@pytest.mark.parametrize("hass_config_yaml", ["a: a\nnokeyhere"])
def test_no_key(try_both_loaders, mock_hass_config_yaml: None) -> None:
"""Test item without a key."""
files = {YAML_CONFIG_FILE: "a: a\nnokeyhere"}
with pytest.raises(HomeAssistantError), patch_yaml_files(files):
with pytest.raises(HomeAssistantError):
yaml.load_yaml(YAML_CONFIG_FILE)
@ -106,35 +107,50 @@ def test_invalid_environment_variable(try_both_loaders):
yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
def test_include_yaml(try_both_loaders):
@pytest.mark.parametrize(
("hass_config_yaml_files", "value"),
[({"test.yaml": "value"}, "value"), ({"test.yaml": None}, {})],
)
def test_include_yaml(
try_both_loaders, mock_hass_config_yaml: None, value: Any
) -> None:
"""Test include yaml."""
with patch_yaml_files({"test.yaml": "value"}):
conf = "key: !include test.yaml"
with io.StringIO(conf) as file:
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert doc["key"] == "value"
with patch_yaml_files({"test.yaml": None}):
conf = "key: !include test.yaml"
with io.StringIO(conf) as file:
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert doc["key"] == {}
conf = "key: !include test.yaml"
with io.StringIO(conf) as file:
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert doc["key"] == value
@patch("homeassistant.util.yaml.loader.os.walk")
def test_include_dir_list(mock_walk, try_both_loaders):
@pytest.mark.parametrize(
"hass_config_yaml_files", [{"/test/one.yaml": "one", "/test/two.yaml": "two"}]
)
def test_include_dir_list(
mock_walk, try_both_loaders, mock_hass_config_yaml: None
) -> None:
"""Test include dir list yaml."""
mock_walk.return_value = [["/test", [], ["two.yaml", "one.yaml"]]]
with patch_yaml_files({"/test/one.yaml": "one", "/test/two.yaml": "two"}):
conf = "key: !include_dir_list /test"
with io.StringIO(conf) as file:
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert doc["key"] == sorted(["one", "two"])
conf = "key: !include_dir_list /test"
with io.StringIO(conf) as file:
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert doc["key"] == sorted(["one", "two"])
@patch("homeassistant.util.yaml.loader.os.walk")
def test_include_dir_list_recursive(mock_walk, try_both_loaders):
@pytest.mark.parametrize(
"hass_config_yaml_files",
[
{
"/test/zero.yaml": "zero",
"/test/tmp2/one.yaml": "one",
"/test/tmp2/two.yaml": "two",
}
],
)
def test_include_dir_list_recursive(
mock_walk, try_both_loaders, mock_hass_config_yaml: None
) -> None:
"""Test include dir recursive list yaml."""
mock_walk.return_value = [
["/test", ["tmp2", ".ignore", "ignore"], ["zero.yaml"]],
@ -142,41 +158,49 @@ def test_include_dir_list_recursive(mock_walk, try_both_loaders):
["/test/ignore", [], [".ignore.yaml"]],
]
with patch_yaml_files(
{
"/test/zero.yaml": "zero",
"/test/tmp2/one.yaml": "one",
"/test/tmp2/two.yaml": "two",
}
):
conf = "key: !include_dir_list /test"
with io.StringIO(conf) as file:
assert (
".ignore" in mock_walk.return_value[0][1]
), "Expecting .ignore in here"
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert "tmp2" in mock_walk.return_value[0][1]
assert ".ignore" not in mock_walk.return_value[0][1]
assert sorted(doc["key"]) == sorted(["zero", "one", "two"])
conf = "key: !include_dir_list /test"
with io.StringIO(conf) as file:
assert ".ignore" in mock_walk.return_value[0][1], "Expecting .ignore in here"
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert "tmp2" in mock_walk.return_value[0][1]
assert ".ignore" not in mock_walk.return_value[0][1]
assert sorted(doc["key"]) == sorted(["zero", "one", "two"])
@patch("homeassistant.util.yaml.loader.os.walk")
def test_include_dir_named(mock_walk, try_both_loaders):
@pytest.mark.parametrize(
"hass_config_yaml_files",
[{"/test/first.yaml": "one", "/test/second.yaml": "two"}],
)
def test_include_dir_named(
mock_walk, try_both_loaders, mock_hass_config_yaml: None
) -> None:
"""Test include dir named yaml."""
mock_walk.return_value = [
["/test", [], ["first.yaml", "second.yaml", "secrets.yaml"]]
]
with patch_yaml_files({"/test/first.yaml": "one", "/test/second.yaml": "two"}):
conf = "key: !include_dir_named /test"
correct = {"first": "one", "second": "two"}
with io.StringIO(conf) as file:
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert doc["key"] == correct
conf = "key: !include_dir_named /test"
correct = {"first": "one", "second": "two"}
with io.StringIO(conf) as file:
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert doc["key"] == correct
@patch("homeassistant.util.yaml.loader.os.walk")
def test_include_dir_named_recursive(mock_walk, try_both_loaders):
@pytest.mark.parametrize(
"hass_config_yaml_files",
[
{
"/test/first.yaml": "one",
"/test/tmp2/second.yaml": "two",
"/test/tmp2/third.yaml": "three",
}
],
)
def test_include_dir_named_recursive(
mock_walk, try_both_loaders, mock_hass_config_yaml: None
) -> None:
"""Test include dir named yaml."""
mock_walk.return_value = [
["/test", ["tmp2", ".ignore", "ignore"], ["first.yaml"]],
@ -184,85 +208,99 @@ def test_include_dir_named_recursive(mock_walk, try_both_loaders):
["/test/ignore", [], [".ignore.yaml"]],
]
with patch_yaml_files(
{
"/test/first.yaml": "one",
"/test/tmp2/second.yaml": "two",
"/test/tmp2/third.yaml": "three",
}
):
conf = "key: !include_dir_named /test"
correct = {"first": "one", "second": "two", "third": "three"}
with io.StringIO(conf) as file:
assert (
".ignore" in mock_walk.return_value[0][1]
), "Expecting .ignore in here"
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert "tmp2" in mock_walk.return_value[0][1]
assert ".ignore" not in mock_walk.return_value[0][1]
assert doc["key"] == correct
conf = "key: !include_dir_named /test"
correct = {"first": "one", "second": "two", "third": "three"}
with io.StringIO(conf) as file:
assert ".ignore" in mock_walk.return_value[0][1], "Expecting .ignore in here"
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert "tmp2" in mock_walk.return_value[0][1]
assert ".ignore" not in mock_walk.return_value[0][1]
assert doc["key"] == correct
@patch("homeassistant.util.yaml.loader.os.walk")
def test_include_dir_merge_list(mock_walk, try_both_loaders):
@pytest.mark.parametrize(
"hass_config_yaml_files",
[{"/test/first.yaml": "- one", "/test/second.yaml": "- two\n- three"}],
)
def test_include_dir_merge_list(
mock_walk, try_both_loaders, mock_hass_config_yaml: None
) -> None:
"""Test include dir merge list yaml."""
mock_walk.return_value = [["/test", [], ["first.yaml", "second.yaml"]]]
with patch_yaml_files(
{"/test/first.yaml": "- one", "/test/second.yaml": "- two\n- three"}
):
conf = "key: !include_dir_merge_list /test"
with io.StringIO(conf) as file:
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert sorted(doc["key"]) == sorted(["one", "two", "three"])
conf = "key: !include_dir_merge_list /test"
with io.StringIO(conf) as file:
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert sorted(doc["key"]) == sorted(["one", "two", "three"])
@patch("homeassistant.util.yaml.loader.os.walk")
def test_include_dir_merge_list_recursive(mock_walk, try_both_loaders):
"""Test include dir merge list yaml."""
mock_walk.return_value = [
["/test", ["tmp2", ".ignore", "ignore"], ["first.yaml"]],
["/test/tmp2", [], ["second.yaml", "third.yaml"]],
["/test/ignore", [], [".ignore.yaml"]],
]
with patch_yaml_files(
@pytest.mark.parametrize(
"hass_config_yaml_files",
[
{
"/test/first.yaml": "- one",
"/test/tmp2/second.yaml": "- two",
"/test/tmp2/third.yaml": "- three\n- four",
}
):
conf = "key: !include_dir_merge_list /test"
with io.StringIO(conf) as file:
assert (
".ignore" in mock_walk.return_value[0][1]
), "Expecting .ignore in here"
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert "tmp2" in mock_walk.return_value[0][1]
assert ".ignore" not in mock_walk.return_value[0][1]
assert sorted(doc["key"]) == sorted(["one", "two", "three", "four"])
],
)
def test_include_dir_merge_list_recursive(
mock_walk, try_both_loaders, mock_hass_config_yaml: None
) -> None:
"""Test include dir merge list yaml."""
mock_walk.return_value = [
["/test", ["tmp2", ".ignore", "ignore"], ["first.yaml"]],
["/test/tmp2", [], ["second.yaml", "third.yaml"]],
["/test/ignore", [], [".ignore.yaml"]],
]
conf = "key: !include_dir_merge_list /test"
with io.StringIO(conf) as file:
assert ".ignore" in mock_walk.return_value[0][1], "Expecting .ignore in here"
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert "tmp2" in mock_walk.return_value[0][1]
assert ".ignore" not in mock_walk.return_value[0][1]
assert sorted(doc["key"]) == sorted(["one", "two", "three", "four"])
@patch("homeassistant.util.yaml.loader.os.walk")
def test_include_dir_merge_named(mock_walk, try_both_loaders):
@pytest.mark.parametrize(
"hass_config_yaml_files",
[
{
"/test/first.yaml": "key1: one",
"/test/second.yaml": "key2: two\nkey3: three",
}
],
)
def test_include_dir_merge_named(
mock_walk, try_both_loaders, mock_hass_config_yaml: None
) -> None:
"""Test include dir merge named yaml."""
mock_walk.return_value = [["/test", [], ["first.yaml", "second.yaml"]]]
files = {
"/test/first.yaml": "key1: one",
"/test/second.yaml": "key2: two\nkey3: three",
}
with patch_yaml_files(files):
conf = "key: !include_dir_merge_named /test"
with io.StringIO(conf) as file:
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert doc["key"] == {"key1": "one", "key2": "two", "key3": "three"}
conf = "key: !include_dir_merge_named /test"
with io.StringIO(conf) as file:
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert doc["key"] == {"key1": "one", "key2": "two", "key3": "three"}
@patch("homeassistant.util.yaml.loader.os.walk")
def test_include_dir_merge_named_recursive(mock_walk, try_both_loaders):
@pytest.mark.parametrize(
"hass_config_yaml_files",
[
{
"/test/first.yaml": "key1: one",
"/test/tmp2/second.yaml": "key2: two",
"/test/tmp2/third.yaml": "key3: three\nkey4: four",
}
],
)
def test_include_dir_merge_named_recursive(
mock_walk, try_both_loaders, mock_hass_config_yaml: None
) -> None:
"""Test include dir merge named yaml."""
mock_walk.return_value = [
["/test", ["tmp2", ".ignore", "ignore"], ["first.yaml"]],
@ -270,27 +308,18 @@ def test_include_dir_merge_named_recursive(mock_walk, try_both_loaders):
["/test/ignore", [], [".ignore.yaml"]],
]
with patch_yaml_files(
{
"/test/first.yaml": "key1: one",
"/test/tmp2/second.yaml": "key2: two",
"/test/tmp2/third.yaml": "key3: three\nkey4: four",
conf = "key: !include_dir_merge_named /test"
with io.StringIO(conf) as file:
assert ".ignore" in mock_walk.return_value[0][1], "Expecting .ignore in here"
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert "tmp2" in mock_walk.return_value[0][1]
assert ".ignore" not in mock_walk.return_value[0][1]
assert doc["key"] == {
"key1": "one",
"key2": "two",
"key3": "three",
"key4": "four",
}
):
conf = "key: !include_dir_merge_named /test"
with io.StringIO(conf) as file:
assert (
".ignore" in mock_walk.return_value[0][1]
), "Expecting .ignore in here"
doc = yaml_loader.yaml.load(file, Loader=yaml_loader.SafeLineLoader)
assert "tmp2" in mock_walk.return_value[0][1]
assert ".ignore" not in mock_walk.return_value[0][1]
assert doc["key"] == {
"key1": "one",
"key2": "two",
"key3": "three",
"key4": "four",
}
@patch("homeassistant.util.yaml.loader.open", create=True)
@ -452,26 +481,31 @@ class TestSecrets(unittest.TestCase):
)
def test_representing_yaml_loaded_data(try_both_dumpers):
@pytest.mark.parametrize("hass_config_yaml", ['key: [1, "2", 3]'])
def test_representing_yaml_loaded_data(
try_both_dumpers, mock_hass_config_yaml: None
) -> None:
"""Test we can represent YAML loaded data."""
files = {YAML_CONFIG_FILE: 'key: [1, "2", 3]'}
with patch_yaml_files(files):
data = load_yaml_config_file(YAML_CONFIG_FILE)
data = load_yaml_config_file(YAML_CONFIG_FILE)
assert yaml.dump(data) == "key:\n- 1\n- '2'\n- 3\n"
def test_duplicate_key(caplog, try_both_loaders):
@pytest.mark.parametrize("hass_config_yaml", ["key: thing1\nkey: thing2"])
def test_duplicate_key(caplog, try_both_loaders, mock_hass_config_yaml: None) -> None:
"""Test duplicate dict keys."""
files = {YAML_CONFIG_FILE: "key: thing1\nkey: thing2"}
with patch_yaml_files(files):
load_yaml_config_file(YAML_CONFIG_FILE)
load_yaml_config_file(YAML_CONFIG_FILE)
assert "contains duplicate key" in caplog.text
def test_no_recursive_secrets(caplog, try_both_loaders):
@pytest.mark.parametrize(
"hass_config_yaml_files",
[{YAML_CONFIG_FILE: "key: !secret a", yaml.SECRET_YAML: "a: 1\nb: !secret a"}],
)
def test_no_recursive_secrets(
caplog, try_both_loaders, mock_hass_config_yaml: None
) -> None:
"""Test that loading of secrets from the secrets file fails correctly."""
files = {YAML_CONFIG_FILE: "key: !secret a", yaml.SECRET_YAML: "a: 1\nb: !secret a"}
with patch_yaml_files(files), pytest.raises(HomeAssistantError) as e:
with pytest.raises(HomeAssistantError) as e:
load_yaml_config_file(YAML_CONFIG_FILE)
assert e.value.args == ("Secrets not supported in this YAML file",)