From e87ebbef01fbe1bf869f6719286088d59f0ed33c Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 14 Nov 2023 21:50:54 +0100 Subject: [PATCH] Improve errors for component configuration with multiple errors (#103969) * Improve errors for component configuration with multiple errors * Suffix with link to documentation --- homeassistant/config.py | 73 ++++++++++++++++----------- tests/snapshots/test_config.ambr | 84 +++++++++++++++++++++++++------- 2 files changed, 110 insertions(+), 47 deletions(-) diff --git a/homeassistant/config.py b/homeassistant/config.py index 569c059e9c2..d39267f6f7d 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -578,26 +578,57 @@ def find_annotation( return find_annotation_rec(config, list(path), None) -def stringify_invalid(ex: vol.Invalid) -> str: +def stringify_invalid( + ex: vol.Invalid, + domain: str, + config: dict, + link: str | None, + max_sub_error_length: int, +) -> str: """Stringify voluptuous.Invalid. This is an alternative to the custom __str__ implemented in - voluptuous.error.Invalid. The main modification is to format - the path delimited by -> instead of @data[]. + voluptuous.error.Invalid. The modifications are: + - Format the path delimited by -> instead of @data[] + - Prefix with domain, file and line of the error + - Suffix with a link to the documentation + - Give a more user friendly output for unknown options """ + message_prefix = f"Invalid config for [{domain}]" + if domain != CONF_CORE and link: + message_suffix = f". Please check the docs at {link}" + else: + message_suffix = "" + if annotation := find_annotation(config, ex.path): + message_prefix += f" at {annotation[0]}, line {annotation[1]}" path = "->".join(str(m) for m in ex.path) + if ex.error_message == "extra keys not allowed": + return ( + f"{message_prefix}: '{ex.path[-1]}' is an invalid option for [{domain}], " + f"check: {path}{message_suffix}" + ) # This function is an alternative to the stringification done by # vol.Invalid.__str__, so we need to call Exception.__str__ here # instead of str(ex) output = Exception.__str__(ex) if error_type := ex.error_type: output += " for " + error_type - return f"{output} '{path}'" + offending_item_summary = repr(_get_by_path(config, ex.path)) + if len(offending_item_summary) > max_sub_error_length: + offending_item_summary = ( + f"{offending_item_summary[: max_sub_error_length - 3]}..." + ) + return ( + f"{message_prefix}: {output} '{path}', got {offending_item_summary}" + f"{message_suffix}." + ) def humanize_error( - data: Any, validation_error: vol.Invalid, + domain: str, + config: dict, + link: str | None, max_sub_error_length: int = MAX_VALIDATION_ERROR_ITEM_LENGTH, ) -> str: """Provide a more helpful + complete validation error message. @@ -608,16 +639,13 @@ def humanize_error( if isinstance(validation_error, vol.MultipleInvalid): return "\n".join( sorted( - humanize_error(data, sub_error, max_sub_error_length) + humanize_error(sub_error, domain, config, link, max_sub_error_length) for sub_error in validation_error.errors ) ) - offending_item_summary = repr(_get_by_path(data, validation_error.path)) - if len(offending_item_summary) > max_sub_error_length: - offending_item_summary = ( - f"{offending_item_summary[: max_sub_error_length - 3]}..." - ) - return f"{stringify_invalid(validation_error)}, got {offending_item_summary}" + return stringify_invalid( + validation_error, domain, config, link, max_sub_error_length + ) @callback @@ -629,28 +657,15 @@ def _format_config_error( This method must be run in the event loop. """ is_friendly = False - message = f"Invalid config for [{domain}]" if isinstance(ex, vol.Invalid): - if annotation := find_annotation(config, ex.path): - message += f" at {annotation[0]}, line {annotation[1]}: " - else: - message += ": " - - if "extra keys not allowed" in ex.error_message: - path = "->".join(str(m) for m in ex.path) - message += ( - f"'{ex.path[-1]}' is an invalid option for [{domain}], check: {path}" - ) - else: - message += f"{humanize_error(config, ex)}." + message = humanize_error(ex, domain, config, link) is_friendly = True else: - message += ": " - message += str(ex) or repr(ex) + message = f"Invalid config for [{domain}]: {str(ex) or repr(ex)}" - if domain != CONF_CORE and link: - message += f" Please check the docs at {link}" + if domain != CONF_CORE and link: + message += f" Please check the docs at {link}." return message, is_friendly diff --git a/tests/snapshots/test_config.ambr b/tests/snapshots/test_config.ambr index 87d2af1e755..fcd3927a891 100644 --- a/tests/snapshots/test_config.ambr +++ b/tests/snapshots/test_config.ambr @@ -4,11 +4,19 @@ "Invalid config for [iot_domain] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 6: required key not provided 'platform', got None.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 9: expected str for dictionary value 'option1', got 123.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 12: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", - "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 19: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", + ''' + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 18: required key not provided 'option1', got None. + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 19: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 20: expected str for dictionary value 'option2', got 123. + ''', "Invalid config for [adr_0007_2] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 27: required key not provided 'adr_0007_2->host', got None.", "Invalid config for [adr_0007_3] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 32: expected int for dictionary value 'adr_0007_3->port', got 'foo'.", "Invalid config for [adr_0007_4] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 37: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option", - "Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 44: 'no_such_option' is an invalid option for [adr_0007_5], check: adr_0007_5->no_such_option", + ''' + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 43: required key not provided 'adr_0007_5->host', got None. + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 44: 'no_such_option' is an invalid option for [adr_0007_5], check: adr_0007_5->no_such_option + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 45: expected int for dictionary value 'adr_0007_5->port', got 'foo'. + ''', ]) # --- # name: test_component_config_validation_error[basic_include] @@ -16,11 +24,19 @@ "Invalid config for [iot_domain] at /fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 5: required key not provided 'platform', got None.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 8: expected str for dictionary value 'option1', got 123.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 11: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", - "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 18: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", + ''' + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 17: required key not provided 'option1', got None. + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 18: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 19: expected str for dictionary value 'option2', got 123. + ''', "Invalid config for [adr_0007_2] at /fixtures/core/config/component_validation/basic_include/configuration.yaml, line 3: required key not provided 'adr_0007_2->host', got None.", "Invalid config for [adr_0007_3] at /fixtures/core/config/component_validation/basic_include/integrations/adr_0007_3.yaml, line 3: expected int for dictionary value 'adr_0007_3->port', got 'foo'.", "Invalid config for [adr_0007_4] at /fixtures/core/config/component_validation/basic_include/integrations/adr_0007_4.yaml, line 3: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option", - "Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic_include/integrations/adr_0007_5.yaml, line 5: 'no_such_option' is an invalid option for [adr_0007_5], check: adr_0007_5->no_such_option", + ''' + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic_include/configuration.yaml, line 6: required key not provided 'adr_0007_5->host', got None. + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic_include/integrations/adr_0007_5.yaml, line 5: 'no_such_option' is an invalid option for [adr_0007_5], check: adr_0007_5->no_such_option + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic_include/integrations/adr_0007_5.yaml, line 6: expected int for dictionary value 'adr_0007_5->port', got 'foo'. + ''', ]) # --- # name: test_component_config_validation_error[include_dir_list] @@ -28,7 +44,11 @@ "Invalid config for [iot_domain] at /fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_2.yaml, line 2: required key not provided 'platform', got None.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_3.yaml, line 3: expected str for dictionary value 'option1', got 123.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_4.yaml, line 3: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", - "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_5.yaml, line 6: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", + ''' + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_5.yaml, line 5: required key not provided 'option1', got None. + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_5.yaml, line 6: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_5.yaml, line 7: expected str for dictionary value 'option2', got 123. + ''', ]) # --- # name: test_component_config_validation_error[include_dir_merge_list] @@ -36,7 +56,11 @@ "Invalid config for [iot_domain] at /fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_1.yaml, line 5: required key not provided 'platform', got None.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_2.yaml, line 3: expected str for dictionary value 'option1', got 123.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_2.yaml, line 6: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", - "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_2.yaml, line 13: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", + ''' + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_2.yaml, line 12: required key not provided 'option1', got None. + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_2.yaml, line 13: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_2.yaml, line 14: expected str for dictionary value 'option2', got 123. + ''', ]) # --- # name: test_component_config_validation_error[packages] @@ -44,11 +68,19 @@ "Invalid config for [iot_domain] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 11: required key not provided 'platform', got None.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 16: expected str for dictionary value 'option1', got 123.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 21: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", - "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 30: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", + ''' + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 29: required key not provided 'option1', got None. + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 30: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 31: expected str for dictionary value 'option2', got 123. + ''', "Invalid config for [adr_0007_2] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 38: required key not provided 'adr_0007_2->host', got None.", "Invalid config for [adr_0007_3] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 43: expected int for dictionary value 'adr_0007_3->port', got 'foo'.", "Invalid config for [adr_0007_4] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 48: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option", - "Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 55: 'no_such_option' is an invalid option for [adr_0007_5], check: adr_0007_5->no_such_option", + ''' + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 54: required key not provided 'adr_0007_5->host', got None. + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 55: 'no_such_option' is an invalid option for [adr_0007_5], check: adr_0007_5->no_such_option + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/packages/configuration.yaml, line 56: expected int for dictionary value 'adr_0007_5->port', got 'foo'. + ''', ]) # --- # name: test_component_config_validation_error[packages_include_dir_named] @@ -56,23 +88,39 @@ "Invalid config for [iot_domain] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 6: required key not provided 'platform', got None.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 9: expected str for dictionary value 'option1', got 123.", "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 12: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", - "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 19: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option", + ''' + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 18: required key not provided 'option1', got None. + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 19: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 20: expected str for dictionary value 'option2', got 123. + ''', "Invalid config for [adr_0007_2] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_2.yaml, line 2: required key not provided 'adr_0007_2->host', got None.", "Invalid config for [adr_0007_3] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_3.yaml, line 4: expected int for dictionary value 'adr_0007_3->port', got 'foo'.", "Invalid config for [adr_0007_4] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_4.yaml, line 4: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option", - "Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_5.yaml, line 6: 'no_such_option' is an invalid option for [adr_0007_5], check: adr_0007_5->no_such_option", + ''' + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_5.yaml, line 5: required key not provided 'adr_0007_5->host', got None. + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_5.yaml, line 6: 'no_such_option' is an invalid option for [adr_0007_5], check: adr_0007_5->no_such_option + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_5.yaml, line 7: expected int for dictionary value 'adr_0007_5->port', got 'foo'. + ''', ]) # --- # name: test_component_config_validation_error_with_docs[basic] list([ - "Invalid config for [iot_domain] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 6: required key not provided 'platform', got None. Please check the docs at https://www.home-assistant.io/integrations/iot_domain", - "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 9: expected str for dictionary value 'option1', got 123. Please check the docs at https://www.home-assistant.io/integrations/non_adr_0007", - "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 12: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option Please check the docs at https://www.home-assistant.io/integrations/non_adr_0007", - "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 19: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option Please check the docs at https://www.home-assistant.io/integrations/non_adr_0007", - "Invalid config for [adr_0007_2] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 27: required key not provided 'adr_0007_2->host', got None. Please check the docs at https://www.home-assistant.io/integrations/adr_0007_2", - "Invalid config for [adr_0007_3] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 32: expected int for dictionary value 'adr_0007_3->port', got 'foo'. Please check the docs at https://www.home-assistant.io/integrations/adr_0007_3", - "Invalid config for [adr_0007_4] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 37: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option Please check the docs at https://www.home-assistant.io/integrations/adr_0007_4", - "Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 44: 'no_such_option' is an invalid option for [adr_0007_5], check: adr_0007_5->no_such_option Please check the docs at https://www.home-assistant.io/integrations/adr_0007_5", + "Invalid config for [iot_domain] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 6: required key not provided 'platform', got None. Please check the docs at https://www.home-assistant.io/integrations/iot_domain.", + "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 9: expected str for dictionary value 'option1', got 123. Please check the docs at https://www.home-assistant.io/integrations/non_adr_0007.", + "Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 12: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option. Please check the docs at https://www.home-assistant.io/integrations/non_adr_0007", + ''' + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 18: required key not provided 'option1', got None. Please check the docs at https://www.home-assistant.io/integrations/non_adr_0007. + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 19: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option. Please check the docs at https://www.home-assistant.io/integrations/non_adr_0007 + Invalid config for [iot_domain.non_adr_0007] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 20: expected str for dictionary value 'option2', got 123. Please check the docs at https://www.home-assistant.io/integrations/non_adr_0007. + ''', + "Invalid config for [adr_0007_2] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 27: required key not provided 'adr_0007_2->host', got None. Please check the docs at https://www.home-assistant.io/integrations/adr_0007_2.", + "Invalid config for [adr_0007_3] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 32: expected int for dictionary value 'adr_0007_3->port', got 'foo'. Please check the docs at https://www.home-assistant.io/integrations/adr_0007_3.", + "Invalid config for [adr_0007_4] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 37: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option. Please check the docs at https://www.home-assistant.io/integrations/adr_0007_4", + ''' + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 43: required key not provided 'adr_0007_5->host', got None. Please check the docs at https://www.home-assistant.io/integrations/adr_0007_5. + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 44: 'no_such_option' is an invalid option for [adr_0007_5], check: adr_0007_5->no_such_option. Please check the docs at https://www.home-assistant.io/integrations/adr_0007_5 + Invalid config for [adr_0007_5] at /fixtures/core/config/component_validation/basic/configuration.yaml, line 45: expected int for dictionary value 'adr_0007_5->port', got 'foo'. Please check the docs at https://www.home-assistant.io/integrations/adr_0007_5. + ''', ]) # --- # name: test_package_merge_error[packages]