From 19fa39d556e3c8964628c4e6803de88c7aea3cb5 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 25 Mar 2024 10:39:30 +0100 Subject: [PATCH] Generate ConfigValidationError message from English translations (#113844) * Fetch ConfigValidationError message from translation cache * Sync error logmessages with translation cache * More sync * Cleanup * Remove unrelated change * Follow up comments * Rebase and improve contructor * Improve name * Rename to MULTIPLE_INTEGRATION_CONFIG_ERRORS --- .../components/homeassistant/strings.json | 14 +- homeassistant/config.py | 81 +++----- homeassistant/exceptions.py | 9 +- tests/snapshots/test_config.ambr | 95 ++++++---- tests/test_config.py | 179 ++++++++++-------- 5 files changed, 200 insertions(+), 178 deletions(-) diff --git a/homeassistant/components/homeassistant/strings.json b/homeassistant/components/homeassistant/strings.json index 016f3e0580d..37604c0e18e 100644 --- a/homeassistant/components/homeassistant/strings.json +++ b/homeassistant/components/homeassistant/strings.json @@ -163,31 +163,31 @@ "message": "Error importing config platform {domain}: {error}" }, "config_validation_err": { - "message": "Invalid config for integration {domain} at {config_file}, line {line}: {error}. Check the logs for more information." + "message": "Invalid config for integration {domain} at {config_file}, line {line}: {error}." }, "config_validator_unknown_err": { - "message": "Unknown error calling {domain} config validator. Check the logs for more information." + "message": "Unknown error calling {domain} config validator - {error}." }, "config_schema_unknown_err": { - "message": "Unknown error calling {domain} CONFIG_SCHEMA. Check the logs for more information." + "message": "Unknown error calling {domain} CONFIG_SCHEMA - {error}." }, - "integration_config_error": { + "multiple_integration_config_errors": { "message": "Failed to process config for integration {domain} due to multiple ({errors}) errors. Check the logs for more information." }, "max_length_exceeded": { "message": "Value {value} for property {property_name} has a maximum length of {max_length} characters." }, "platform_component_load_err": { - "message": "Platform error: {domain} - {error}. Check the logs for more information." + "message": "Platform error: {domain} - {error}." }, "platform_component_load_exc": { - "message": "Platform error: {domain} - {error}. Check the logs for more information." + "message": "[%key:component::homeassistant::exceptions::platform_component_load_err::message%]" }, "platform_config_validation_err": { "message": "Invalid config for {domain} from integration {p_name} at file {config_file}, line {line}: {error}. Check the logs for more information." }, "platform_schema_validator_err": { - "message": "Unknown error when validating config for {domain} from integration {p_name}" + "message": "Unknown error when validating config for {domain} from integration {p_name} - {error}." }, "service_not_found": { "message": "Service {domain}.{service} not found." diff --git a/homeassistant/config.py b/homeassistant/config.py index 73c38a6c41a..d2390c6c4ff 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -63,6 +63,7 @@ from .exceptions import ConfigValidationError, HomeAssistantError from .generated.currencies import HISTORIC_CURRENCIES from .helpers import config_validation as cv, issue_registry as ir from .helpers.entity_values import EntityValues +from .helpers.translation import async_get_exception_message from .helpers.typing import ConfigType from .loader import ComponentProtocol, Integration, IntegrationNotFound from .requirements import RequirementsNotFound, async_get_integration_with_requirements @@ -130,13 +131,23 @@ class ConfigErrorTranslationKey(StrEnum): CONFIG_PLATFORM_IMPORT_ERR = "config_platform_import_err" CONFIG_VALIDATOR_UNKNOWN_ERR = "config_validator_unknown_err" CONFIG_SCHEMA_UNKNOWN_ERR = "config_schema_unknown_err" - PLATFORM_VALIDATOR_UNKNOWN_ERR = "platform_validator_unknown_err" PLATFORM_COMPONENT_LOAD_ERR = "platform_component_load_err" PLATFORM_COMPONENT_LOAD_EXC = "platform_component_load_exc" PLATFORM_SCHEMA_VALIDATOR_ERR = "platform_schema_validator_err" # translation key in case multiple errors occurred - INTEGRATION_CONFIG_ERROR = "integration_config_error" + MULTIPLE_INTEGRATION_CONFIG_ERRORS = "multiple_integration_config_errors" + + +_CONFIG_LOG_SHOW_STACK_TRACE: dict[ConfigErrorTranslationKey, bool] = { + ConfigErrorTranslationKey.COMPONENT_IMPORT_ERR: False, + ConfigErrorTranslationKey.CONFIG_PLATFORM_IMPORT_ERR: False, + ConfigErrorTranslationKey.CONFIG_VALIDATOR_UNKNOWN_ERR: True, + ConfigErrorTranslationKey.CONFIG_SCHEMA_UNKNOWN_ERR: True, + ConfigErrorTranslationKey.PLATFORM_COMPONENT_LOAD_ERR: False, + ConfigErrorTranslationKey.PLATFORM_COMPONENT_LOAD_EXC: True, + ConfigErrorTranslationKey.PLATFORM_SCHEMA_VALIDATOR_ERR: True, +} @dataclass @@ -1183,48 +1194,16 @@ def _get_log_message_and_stack_print_pref( platform_config = platform_exception.config link = platform_exception.integration_link - placeholders: dict[str, str] = {"domain": domain, "error": str(exception)} - - log_message_mapping: dict[ConfigErrorTranslationKey, tuple[str, bool]] = { - ConfigErrorTranslationKey.COMPONENT_IMPORT_ERR: ( - f"Unable to import {domain}: {exception}", - False, - ), - ConfigErrorTranslationKey.CONFIG_PLATFORM_IMPORT_ERR: ( - f"Error importing config platform {domain}: {exception}", - False, - ), - ConfigErrorTranslationKey.CONFIG_VALIDATOR_UNKNOWN_ERR: ( - f"Unknown error calling {domain} config validator", - True, - ), - ConfigErrorTranslationKey.CONFIG_SCHEMA_UNKNOWN_ERR: ( - f"Unknown error calling {domain} CONFIG_SCHEMA", - True, - ), - ConfigErrorTranslationKey.PLATFORM_VALIDATOR_UNKNOWN_ERR: ( - f"Unknown error validating {platform_path} platform config with {domain} " - "component platform schema", - True, - ), - ConfigErrorTranslationKey.PLATFORM_COMPONENT_LOAD_ERR: ( - f"Platform error: {domain} - {exception}", - False, - ), - ConfigErrorTranslationKey.PLATFORM_COMPONENT_LOAD_EXC: ( - f"Platform error: {domain} - {exception}", - True, - ), - ConfigErrorTranslationKey.PLATFORM_SCHEMA_VALIDATOR_ERR: ( - f"Unknown error validating config for {platform_path} platform " - f"for {domain} component with PLATFORM_SCHEMA", - True, - ), + placeholders: dict[str, str] = { + "domain": domain, + "error": str(exception), + "p_name": platform_path, } - log_message_show_stack_trace = log_message_mapping.get( + + show_stack_trace: bool | None = _CONFIG_LOG_SHOW_STACK_TRACE.get( platform_exception.translation_key ) - if log_message_show_stack_trace is None: + if show_stack_trace is None: # If no pre defined log_message is set, we generate an enriched error # message, so we can notify about it during setup show_stack_trace = False @@ -1247,9 +1226,14 @@ def _get_log_message_and_stack_print_pref( show_stack_trace = True return (log_message, show_stack_trace, placeholders) - assert isinstance(log_message_show_stack_trace, tuple) + # Generate the log message from the English translations + log_message = async_get_exception_message( + HA_DOMAIN, + platform_exception.translation_key, + translation_placeholders=placeholders, + ) - return (*log_message_show_stack_trace, placeholders) + return (log_message, show_stack_trace, placeholders) async def async_process_component_and_handle_errors( @@ -1348,21 +1332,16 @@ def async_handle_component_errors( if len(config_exception_info) == 1: translation_key = platform_exception.translation_key else: - translation_key = ConfigErrorTranslationKey.INTEGRATION_CONFIG_ERROR + translation_key = ConfigErrorTranslationKey.MULTIPLE_INTEGRATION_CONFIG_ERRORS errors = str(len(config_exception_info)) - log_message = ( - f"Failed to process component config for integration {domain} " - f"due to multiple errors ({errors}), check the logs for more information." - ) placeholders = { "domain": domain, "errors": errors, } raise ConfigValidationError( - str(log_message), + translation_key, [platform_exception.exception for platform_exception in config_exception_info], - translation_domain="homeassistant", - translation_key=translation_key, + translation_domain=HA_DOMAIN, translation_placeholders=placeholders, ) diff --git a/homeassistant/exceptions.py b/homeassistant/exceptions.py index e6ed111c13f..f681764b4af 100644 --- a/homeassistant/exceptions.py +++ b/homeassistant/exceptions.py @@ -87,20 +87,19 @@ class ConfigValidationError(HomeAssistantError, ExceptionGroup[Exception]): def __init__( self, - message: str, + message_translation_key: str, exceptions: list[Exception], translation_domain: str | None = None, - translation_key: str | None = None, translation_placeholders: dict[str, str] | None = None, ) -> None: """Initialize exception.""" super().__init__( - *(message, exceptions), + *(message_translation_key, exceptions), translation_domain=translation_domain, - translation_key=translation_key, + translation_key=message_translation_key, translation_placeholders=translation_placeholders, ) - self._message = message + self.generate_message = True class ServiceValidationError(HomeAssistantError): diff --git a/tests/snapshots/test_config.ambr b/tests/snapshots/test_config.ambr index 34fa5d61797..6fcbce7d8d6 100644 --- a/tests/snapshots/test_config.ambr +++ b/tests/snapshots/test_config.ambr @@ -1,4 +1,19 @@ # serializer version: 1 +# name: test_component_config_error_processing[exception_info_list0-bla-messages0-False-component_import_err] + 'Unable to import test_domain: bla' +# --- +# name: test_component_config_error_processing[exception_info_list1-bla-messages1-True-config_validation_err] + 'Invalid config for integration test_domain at configuration.yaml, line 140: bla' +# --- +# name: test_component_config_error_processing[exception_info_list2-bla @ data['path']-messages2-False-config_validation_err] + "Invalid config for integration test_domain at configuration.yaml, line 140: bla @ data['path']" +# --- +# name: test_component_config_error_processing[exception_info_list3-bla @ data['path']-messages3-False-platform_config_validation_err] + "Invalid config for test_domain from integration test_domain at file configuration.yaml, line 140: bla @ data['path']. Check the logs for more information" +# --- +# name: test_component_config_error_processing[exception_info_list4-bla-messages4-False-platform_component_load_err] + 'Platform error: test_domain - bla' +# --- # name: test_component_config_validation_error[basic] list([ dict({ @@ -63,7 +78,7 @@ }), dict({ 'has_exc_info': True, - 'message': 'Unknown error calling custom_validator_bad_2 config validator', + 'message': 'config_validator_unknown_err', }), ]) # --- @@ -131,7 +146,7 @@ }), dict({ 'has_exc_info': True, - 'message': 'Unknown error calling custom_validator_bad_2 config validator', + 'message': 'config_validator_unknown_err', }), ]) # --- @@ -247,7 +262,7 @@ }), dict({ 'has_exc_info': True, - 'message': 'Unknown error calling custom_validator_bad_2 config validator', + 'message': 'config_validator_unknown_err', }), ]) # --- @@ -291,7 +306,7 @@ }), dict({ 'has_exc_info': True, - 'message': 'Unknown error calling custom_validator_bad_2 config validator', + 'message': 'config_validator_unknown_err', }), dict({ 'has_exc_info': False, @@ -342,7 +357,27 @@ ''', "Invalid config for 'custom_validator_ok_2' at configuration.yaml, line 52: required key 'host' not provided, please check the docs at https://www.home-assistant.io/integrations/custom_validator_ok_2", "Invalid config for 'custom_validator_bad_1' at configuration.yaml, line 55: broken, please check the docs at https://www.home-assistant.io/integrations/custom_validator_bad_1", - 'Unknown error calling custom_validator_bad_2 config validator', + 'config_validator_unknown_err', + ]) +# --- +# name: test_individual_packages_schema_validation_errors[packages_dict] + list([ + "Setup of package 'should_be_a_dict' at configuration.yaml, line 3 failed: Invalid package definition 'should_be_a_dict': expected a dictionary. Package will not be initialized", + ]) +# --- +# name: test_individual_packages_schema_validation_errors[packages_include_dir_named_dict] + list([ + "Setup of package 'should_be_a_dict' at packages/expected_dict.yaml, line 1 failed: Invalid package definition 'should_be_a_dict': expected a dictionary. Package will not be initialized", + ]) +# --- +# name: test_individual_packages_schema_validation_errors[packages_include_dir_named_slug] + list([ + "Setup of package 'this is not a slug but it should be one' at packages/expected_slug.yaml, line 1 failed: Invalid package definition 'this is not a slug but it should be one': invalid slug this is not a slug but it should be one (try this_is_not_a_slug_but_it_should_be_one). Package will not be initialized", + ]) +# --- +# name: test_individual_packages_schema_validation_errors[packages_slug] + list([ + "Setup of package 'this is not a slug but it should be one' at configuration.yaml, line 3 failed: Invalid package definition 'this is not a slug but it should be one': invalid slug this is not a slug but it should be one (try this_is_not_a_slug_but_it_should_be_one). Package will not be initialized", ]) # --- # name: test_package_merge_error[packages] @@ -381,6 +416,21 @@ "Setup of package 'unknown_integration' at integrations/unknown_integration.yaml, line 1 failed: Integration test_domain caused error: ModuleNotFoundError: No module named 'not_installed_something'", ]) # --- +# name: test_packages_schema_validation_error[packages_is_a_list] + list([ + "Invalid package configuration 'packages' at configuration.yaml, line 2: expected a dictionary", + ]) +# --- +# name: test_packages_schema_validation_error[packages_is_a_value] + list([ + "Invalid package configuration 'packages' at configuration.yaml, line 2: expected a dictionary", + ]) +# --- +# name: test_packages_schema_validation_error[packages_is_null] + list([ + "Invalid package configuration 'packages' at configuration.yaml, line 3: expected a dictionary", + ]) +# --- # name: test_yaml_error[basic] ''' mapping values are not allowed here @@ -451,38 +501,3 @@ ''', ]) # --- -# name: test_individual_packages_schema_validation_errors[packages_dict] - list([ - "Setup of package 'should_be_a_dict' at configuration.yaml, line 3 failed: Invalid package definition 'should_be_a_dict': expected a dictionary. Package will not be initialized", - ]) -# --- -# name: test_individual_packages_schema_validation_errors[packages_slug] - list([ - "Setup of package 'this is not a slug but it should be one' at configuration.yaml, line 3 failed: Invalid package definition 'this is not a slug but it should be one': invalid slug this is not a slug but it should be one (try this_is_not_a_slug_but_it_should_be_one). Package will not be initialized", - ]) -# --- -# name: test_individual_packages_schema_validation_errors[packages_include_dir_named_dict] - list([ - "Setup of package 'should_be_a_dict' at packages/expected_dict.yaml, line 1 failed: Invalid package definition 'should_be_a_dict': expected a dictionary. Package will not be initialized", - ]) -# --- -# name: test_individual_packages_schema_validation_errors[packages_include_dir_named_slug] - list([ - "Setup of package 'this is not a slug but it should be one' at packages/expected_slug.yaml, line 1 failed: Invalid package definition 'this is not a slug but it should be one': invalid slug this is not a slug but it should be one (try this_is_not_a_slug_but_it_should_be_one). Package will not be initialized", - ]) -# --- -# name: test_packages_schema_validation_error[packages_is_a_list] - list([ - "Invalid package configuration 'packages' at configuration.yaml, line 2: expected a dictionary", - ]) -# --- -# name: test_packages_schema_validation_error[packages_is_a_value] - list([ - "Invalid package configuration 'packages' at configuration.yaml, line 2: expected a dictionary", - ]) -# --- -# name: test_packages_schema_validation_error[packages_is_null] - list([ - "Invalid package configuration 'packages' at configuration.yaml, line 3: expected a dictionary", - ]) -# --- diff --git a/tests/test_config.py b/tests/test_config.py index efb87a44bdb..c2e134b1cfd 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -44,6 +44,7 @@ import homeassistant.helpers.check_config as check_config from homeassistant.helpers.entity import Entity from homeassistant.helpers.typing import ConfigType from homeassistant.loader import Integration, async_get_integration +from homeassistant.setup import async_setup_component from homeassistant.util.unit_system import ( _CONF_UNIT_SYSTEM_US_CUSTOMARY, METRIC_SYSTEM, @@ -51,6 +52,7 @@ from homeassistant.util.unit_system import ( UnitSystem, ) from homeassistant.util.yaml import SECRET_YAML +from homeassistant.util.yaml.objects import NodeDictClass from .common import ( MockModule, @@ -373,6 +375,14 @@ async def mock_custom_validator_integrations_with_docs( ) +class ConfigTestClass(NodeDictClass): + """Test class for config with wrapper.""" + + __dict__ = {"__config_file__": "configuration.yaml", "__line__": 140} + __line__ = 140 + __config_file__ = "configuration.yaml" + + async def test_create_default_config(hass: HomeAssistant) -> None: """Test creation of default config.""" assert not os.path.isfile(YAML_PATH) @@ -1435,7 +1445,24 @@ async def test_component_config_exceptions( hass: HomeAssistant, caplog: pytest.LogCaptureFixture ) -> None: """Test unexpected exceptions validating component config.""" - # Config validator + + # Create test config with embedded info + test_config = ConfigTestClass({"test_domain": {}}) + test_platform_config = ConfigTestClass( + {"test_domain": {"platform": "test_platform"}} + ) + test_multi_platform_config = ConfigTestClass( + { + "test_domain": [ + {"platform": "test_platform1"}, + {"platform": "test_platform2"}, + ] + }, + ) + + # Make sure the exception translation cache is loaded + await async_setup_component(hass, "homeassistant", {}) + test_integration = Mock( domain="test_domain", async_get_component=AsyncMock(), @@ -1447,7 +1474,7 @@ async def test_component_config_exceptions( ) assert ( await config_util.async_process_component_and_handle_errors( - hass, {}, integration=test_integration + hass, test_config, integration=test_integration ) is None ) @@ -1456,12 +1483,13 @@ async def test_component_config_exceptions( caplog.clear() with pytest.raises(HomeAssistantError) as ex: await config_util.async_process_component_and_handle_errors( - hass, {}, integration=test_integration, raise_on_failure=True + hass, test_config, integration=test_integration, raise_on_failure=True ) assert "ValueError: broken" in caplog.text assert "Unknown error calling test_domain config validator" in caplog.text - assert str(ex.value) == "Unknown error calling test_domain config validator" - + assert ( + str(ex.value) == "Unknown error calling test_domain config validator - broken" + ) test_integration = Mock( domain="test_domain", async_get_platform=AsyncMock( @@ -1476,17 +1504,23 @@ async def test_component_config_exceptions( caplog.clear() assert ( await config_util.async_process_component_and_handle_errors( - hass, {}, integration=test_integration, raise_on_failure=False + hass, test_config, integration=test_integration, raise_on_failure=False ) is None ) - assert "Invalid config for 'test_domain': broken" in caplog.text + assert ( + "Invalid config for 'test_domain' at ../../configuration.yaml, " + "line 140: broken, please check the docs at" in caplog.text + ) with pytest.raises(HomeAssistantError) as ex: await config_util.async_process_component_and_handle_errors( - hass, {}, integration=test_integration, raise_on_failure=True + hass, test_config, integration=test_integration, raise_on_failure=True ) - assert "Invalid config for 'test_domain': broken" in str(ex.value) - + assert ( + str(ex.value) + == "Invalid config for integration test_domain at configuration.yaml, " + "line 140: broken" + ) # component.CONFIG_SCHEMA caplog.clear() test_integration = Mock( @@ -1499,23 +1533,23 @@ async def test_component_config_exceptions( assert ( await config_util.async_process_component_and_handle_errors( hass, - {}, + test_config, integration=test_integration, raise_on_failure=False, ) is None ) assert "Unknown error calling test_domain CONFIG_SCHEMA" in caplog.text + caplog.clear() with pytest.raises(HomeAssistantError) as ex: await config_util.async_process_component_and_handle_errors( hass, - {}, + test_config, integration=test_integration, raise_on_failure=True, ) assert "Unknown error calling test_domain CONFIG_SCHEMA" in caplog.text - assert str(ex.value) == "Unknown error calling test_domain CONFIG_SCHEMA" - + assert str(ex.value) == "Unknown error calling test_domain CONFIG_SCHEMA - broken" # component.PLATFORM_SCHEMA caplog.clear() test_integration = Mock( @@ -1530,30 +1564,30 @@ async def test_component_config_exceptions( ) assert await config_util.async_process_component_and_handle_errors( hass, - {"test_domain": {"platform": "test_platform"}}, + test_platform_config, integration=test_integration, raise_on_failure=False, ) == {"test_domain": []} assert "ValueError: broken" in caplog.text assert ( - "Unknown error validating config for test_platform platform " - "for test_domain component with PLATFORM_SCHEMA" + "Unknown error when validating config for test_domain " + "from integration test_platform - broken" ) in caplog.text caplog.clear() with pytest.raises(HomeAssistantError) as ex: await config_util.async_process_component_and_handle_errors( hass, - {"test_domain": {"platform": "test_platform"}}, + test_platform_config, integration=test_integration, raise_on_failure=True, ) assert ( - "Unknown error validating config for test_platform platform " - "for test_domain component with PLATFORM_SCHEMA" + "Unknown error when validating config for test_domain " + "from integration test_platform - broken" ) in caplog.text assert str(ex.value) == ( - "Unknown error validating config for test_platform platform " - "for test_domain component with PLATFORM_SCHEMA" + "Unknown error when validating config for test_domain " + "from integration test_platform - broken" ) # platform.PLATFORM_SCHEMA @@ -1575,78 +1609,65 @@ async def test_component_config_exceptions( ): assert await config_util.async_process_component_and_handle_errors( hass, - {"test_domain": {"platform": "test_platform"}}, + test_platform_config, integration=test_integration, raise_on_failure=False, ) == {"test_domain": []} assert "ValueError: broken" in caplog.text assert ( - "Unknown error validating config for test_platform platform for test_domain" - " component with PLATFORM_SCHEMA" + "Unknown error when validating config for test_domain " + "from integration test_platform - broken" ) in caplog.text caplog.clear() with pytest.raises(HomeAssistantError) as ex: assert await config_util.async_process_component_and_handle_errors( hass, - {"test_domain": {"platform": "test_platform"}}, + test_platform_config, integration=test_integration, raise_on_failure=True, ) assert ( - "Unknown error validating config for test_platform platform for test_domain" - " component with PLATFORM_SCHEMA" - ) in str(ex.value) + "Unknown error when validating config for test_domain " + "from integration test_platform - broken" in str(ex.value) + ) assert "ValueError: broken" in caplog.text assert ( - "Unknown error validating config for test_platform platform for test_domain" - " component with PLATFORM_SCHEMA" in caplog.text + "Unknown error when validating config for test_domain " + "from integration test_platform - broken" in caplog.text ) # Test multiple platform failures assert await config_util.async_process_component_and_handle_errors( hass, - { - "test_domain": [ - {"platform": "test_platform1"}, - {"platform": "test_platform2"}, - ] - }, + test_multi_platform_config, integration=test_integration, raise_on_failure=False, ) == {"test_domain": []} assert "ValueError: broken" in caplog.text assert ( - "Unknown error validating config for test_platform1 platform " - "for test_domain component with PLATFORM_SCHEMA" - ) in caplog.text - assert ( - "Unknown error validating config for test_platform2 platform " - "for test_domain component with PLATFORM_SCHEMA" + "Unknown error when validating config for test_domain " + "from integration test_platform - broken" ) in caplog.text caplog.clear() with pytest.raises(HomeAssistantError) as ex: assert await config_util.async_process_component_and_handle_errors( hass, - { - "test_domain": [ - {"platform": "test_platform1"}, - {"platform": "test_platform2"}, - ] - }, + test_multi_platform_config, integration=test_integration, raise_on_failure=True, ) assert ( - "Failed to process component config for integration test_domain" - " due to multiple errors (2), check the logs for more information." - ) in str(ex.value) + "Failed to process config for integration test_domain " + "due to multiple (2) errors. Check the logs for more information" + in str(ex.value) + ) assert "ValueError: broken" in caplog.text assert ( - "Unknown error validating config for test_platform1 platform " - "for test_domain component with PLATFORM_SCHEMA" + "Unknown error when validating config for test_domain " + "from integration test_platform1 - broken" ) in caplog.text assert ( - "Unknown error validating config for test_platform2 platform " - "for test_domain component with PLATFORM_SCHEMA" + "Unknown error when validating config for test_domain " + "from integration test_platform2 - broken" ) in caplog.text # async_get_platform("domain") raising on ImportError @@ -1668,7 +1689,7 @@ async def test_component_config_exceptions( ): assert await config_util.async_process_component_and_handle_errors( hass, - {"test_domain": {"platform": "test_platform"}}, + test_platform_config, integration=test_integration, raise_on_failure=False, ) == {"test_domain": []} @@ -1680,7 +1701,7 @@ async def test_component_config_exceptions( with pytest.raises(HomeAssistantError) as ex: assert await config_util.async_process_component_and_handle_errors( hass, - {"test_domain": {"platform": "test_platform"}}, + test_platform_config, integration=test_integration, raise_on_failure=True, ) @@ -1713,7 +1734,7 @@ async def test_component_config_exceptions( assert ( await config_util.async_process_component_and_handle_errors( hass, - {"test_domain": {}}, + test_config, integration=test_integration, raise_on_failure=False, ) @@ -1726,7 +1747,7 @@ async def test_component_config_exceptions( with pytest.raises(HomeAssistantError) as ex: await config_util.async_process_component_and_handle_errors( hass, - {"test_domain": {}}, + test_config, integration=test_integration, raise_on_failure=True, ) @@ -1751,7 +1772,7 @@ async def test_component_config_exceptions( assert ( await config_util.async_process_component_and_handle_errors( hass, - {"test_domain": {}}, + test_config, integration=test_integration, raise_on_failure=False, ) @@ -1761,7 +1782,7 @@ async def test_component_config_exceptions( with pytest.raises(HomeAssistantError) as ex: await config_util.async_process_component_and_handle_errors( hass, - {"test_domain": {}}, + test_config, integration=test_integration, raise_on_failure=True, ) @@ -1778,7 +1799,7 @@ async def test_component_config_exceptions( ImportError("bla"), "component_import_err", "test_domain", - {"test_domain": []}, + ConfigTestClass({"test_domain": []}), "https://example.com", ) ], @@ -1793,13 +1814,14 @@ async def test_component_config_exceptions( HomeAssistantError("bla"), "config_validation_err", "test_domain", - {"test_domain": []}, + ConfigTestClass({"test_domain": []}), "https://example.com", ) ], "bla", [ - "Invalid config for 'test_domain': bla, " + "Invalid config for 'test_domain' at " + "../../configuration.yaml, line 140: bla, " "please check the docs at https://example.com", "bla", ], @@ -1812,14 +1834,15 @@ async def test_component_config_exceptions( vol.Invalid("bla", ["path"]), "config_validation_err", "test_domain", - {"test_domain": []}, + ConfigTestClass({"test_domain": []}), "https://example.com", ) ], "bla @ data['path']", [ - "Invalid config for 'test_domain': bla 'path', got None, " - "please check the docs at https://example.com", + "Invalid config for 'test_domain' at " + "../../configuration.yaml, line 140: bla 'path', " + "got None, please check the docs at https://example.com", "bla", ], False, @@ -1831,14 +1854,15 @@ async def test_component_config_exceptions( vol.Invalid("bla", ["path"]), "platform_config_validation_err", "test_domain", - {"test_domain": []}, + ConfigTestClass({"test_domain": []}), "https://alt.example.com", ) ], "bla @ data['path']", [ - "Invalid config for 'test_domain': bla 'path', got None, " - "please check the docs at https://alt.example.com", + "Invalid config for 'test_domain' at " + "../../configuration.yaml, line 140: bla 'path', " + "got None, please check the docs at https://alt.example.com", "bla", ], False, @@ -1850,7 +1874,7 @@ async def test_component_config_exceptions( ImportError("bla"), "platform_component_load_err", "test_domain", - {"test_domain": []}, + ConfigTestClass({"test_domain": []}), "https://example.com", ) ], @@ -1864,13 +1888,18 @@ async def test_component_config_exceptions( async def test_component_config_error_processing( hass: HomeAssistant, caplog: pytest.LogCaptureFixture, - error: str, exception_info_list: list[config_util.ConfigExceptionInfo], + snapshot: SnapshotAssertion, + error: str, messages: list[str], show_stack_trace: bool, translation_key: str, ) -> None: """Test component config error processing.""" + + # Make sure the exception translation cache is loaded + await async_setup_component(hass, "homeassistant", {}) + test_integration = Mock( domain="test_domain", documentation="https://example.com", @@ -1890,7 +1919,7 @@ async def test_component_config_error_processing( records = [record for record in caplog.records if record.msg == messages[0]] assert len(records) == 1 assert (records[0].exc_info is not None) == show_stack_trace - assert str(ex.value) == messages[0] + assert str(ex.value) == snapshot assert ex.value.translation_key == translation_key assert ex.value.translation_domain == "homeassistant" assert ex.value.translation_placeholders["domain"] == "test_domain" @@ -1902,7 +1931,7 @@ async def test_component_config_error_processing( return_value=config_util.IntegrationConfigInfo(None, exception_info_list), ): await config_util.async_process_component_and_handle_errors( - hass, {}, test_integration + hass, ConfigTestClass({}), test_integration ) assert all(message in caplog.text for message in messages)