diff --git a/tests/components/mqtt/test_humidifier.py b/tests/components/mqtt/test_humidifier.py index 89afe0a3972..0517e8e6a9c 100644 --- a/tests/components/mqtt/test_humidifier.py +++ b/tests/components/mqtt/test_humidifier.py @@ -14,7 +14,6 @@ from homeassistant.components.humidifier import ( SERVICE_SET_HUMIDITY, SERVICE_SET_MODE, ) -from homeassistant.components.mqtt import CONFIG_SCHEMA from homeassistant.components.mqtt.humidifier import ( CONF_MODE_COMMAND_TOPIC, CONF_MODE_STATE_TOPIC, @@ -34,7 +33,6 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant -from homeassistant.setup import async_setup_component from .test_common import ( help_test_availability_when_connection_lost, @@ -130,30 +128,26 @@ async def async_set_humidity( await hass.services.async_call(DOMAIN, SERVICE_SET_HUMIDITY, data, blocking=True) +@pytest.mark.parametrize( + "hass_config", [{mqtt.DOMAIN: {humidifier.DOMAIN: {"name": "test"}}}] +) async def test_fail_setup_if_no_command_topic( - hass: HomeAssistant, caplog: pytest.LogCaptureFixture + hass: HomeAssistant, + mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, + caplog: pytest.LogCaptureFixture, ) -> None: """Test if command fails with command topic.""" - assert not await async_setup_component( - hass, - mqtt.DOMAIN, - {mqtt.DOMAIN: {humidifier.DOMAIN: {"name": "test"}}}, - ) + with pytest.raises(AssertionError): + await mqtt_mock_entry_no_yaml_config() assert ( "Invalid config for [mqtt]: required key not provided @ data['mqtt']['humidifier'][0]['command_topic']. Got None" in caplog.text ) -async def test_controlling_state_via_topic( - hass: HomeAssistant, - mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test the controlling state via topic.""" - assert await async_setup_component( - hass, - mqtt.DOMAIN, +@pytest.mark.parametrize( + "hass_config", + [ { mqtt.DOMAIN: { humidifier.DOMAIN: { @@ -178,10 +172,16 @@ async def test_controlling_state_via_topic( "payload_reset_mode": "rEset_mode", } } - }, - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + } + ], +) +async def test_controlling_state_via_topic( + hass: HomeAssistant, + mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test the controlling state via topic.""" + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -252,15 +252,9 @@ async def test_controlling_state_via_topic( assert state.state == STATE_UNKNOWN -async def test_controlling_state_via_topic_and_json_message( - hass: HomeAssistant, - mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test the controlling state via topic and JSON message.""" - assert await async_setup_component( - hass, - mqtt.DOMAIN, +@pytest.mark.parametrize( + "hass_config", + [ { mqtt.DOMAIN: { humidifier.DOMAIN: { @@ -281,10 +275,17 @@ async def test_controlling_state_via_topic_and_json_message( "mode_state_template": "{{ value_json.val }}", } } - }, - ) + } + ], +) +async def test_controlling_state_via_topic_and_json_message( + hass: HomeAssistant, + mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test the controlling state via topic and JSON message.""" await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -343,15 +344,9 @@ async def test_controlling_state_via_topic_and_json_message( assert state.state == STATE_UNKNOWN -async def test_controlling_state_via_topic_and_json_message_shared_topic( - hass: HomeAssistant, - mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test the controlling state via topic and JSON message using a shared topic.""" - assert await async_setup_component( - hass, - mqtt.DOMAIN, +@pytest.mark.parametrize( + "hass_config", + [ { mqtt.DOMAIN: { humidifier.DOMAIN: { @@ -372,10 +367,16 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic( "mode_state_template": "{{ value_json.mode }}", } } - }, - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + } + ], +) +async def test_controlling_state_via_topic_and_json_message_shared_topic( + hass: HomeAssistant, + mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test the controlling state via topic and JSON message using a shared topic.""" + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -422,15 +423,9 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic( caplog.clear() -async def test_sending_mqtt_commands_and_optimistic( - hass: HomeAssistant, - mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test optimistic mode without state topic.""" - assert await async_setup_component( - hass, - mqtt.DOMAIN, +@pytest.mark.parametrize( + "hass_config", + [ { mqtt.DOMAIN: { humidifier.DOMAIN: { @@ -447,10 +442,16 @@ async def test_sending_mqtt_commands_and_optimistic( ], } } - }, - ) - await hass.async_block_till_done() - mqtt_mock = await mqtt_mock_entry_with_yaml_config() + } + ], +) +async def test_sending_mqtt_commands_and_optimistic( + hass: HomeAssistant, + mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test optimistic mode without state topic.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -521,15 +522,9 @@ async def test_sending_mqtt_commands_and_optimistic( assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_sending_mqtt_command_templates_( - hass: HomeAssistant, - mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, -) -> None: - """Testing command templates with optimistic mode without state topic.""" - assert await async_setup_component( - hass, - mqtt.DOMAIN, +@pytest.mark.parametrize( + "hass_config", + [ { mqtt.DOMAIN: { humidifier.DOMAIN: { @@ -547,10 +542,16 @@ async def test_sending_mqtt_command_templates_( ], } } - }, - ) - await hass.async_block_till_done() - mqtt_mock = await mqtt_mock_entry_with_yaml_config() + } + ], +) +async def test_sending_mqtt_command_templates_( + hass: HomeAssistant, + mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, + caplog: pytest.LogCaptureFixture, +) -> None: + """Testing command templates with optimistic mode without state topic.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -621,15 +622,9 @@ async def test_sending_mqtt_command_templates_( assert state.attributes.get(ATTR_ASSUMED_STATE) -async def test_sending_mqtt_commands_and_explicit_optimistic( - hass: HomeAssistant, - mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test optimistic mode with state topic and turn on attributes.""" - assert await async_setup_component( - hass, - mqtt.DOMAIN, +@pytest.mark.parametrize( + "hass_config", + [ { mqtt.DOMAIN: { humidifier.DOMAIN: { @@ -648,10 +643,16 @@ async def test_sending_mqtt_commands_and_explicit_optimistic( "optimistic": True, } } - }, - ) - await hass.async_block_till_done() - mqtt_mock = await mqtt_mock_entry_with_yaml_config() + } + ], +) +async def test_sending_mqtt_commands_and_explicit_optimistic( + hass: HomeAssistant, + mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test optimistic mode with state topic and turn on attributes.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -774,15 +775,9 @@ async def test_encoding_subscribable_topics( ) -async def test_attributes( - hass: HomeAssistant, - mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test attributes.""" - assert await async_setup_component( - hass, - mqtt.DOMAIN, +@pytest.mark.parametrize( + "hass_config", + [ { mqtt.DOMAIN: { humidifier.DOMAIN: { @@ -796,10 +791,16 @@ async def test_attributes( ], } } - }, - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + } + ], +) +async def test_attributes( + hass: HomeAssistant, + mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test attributes.""" + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("humidifier.test") assert state.state == STATE_UNKNOWN @@ -826,105 +827,142 @@ async def test_attributes( @pytest.mark.parametrize( - ("config", "valid"), + ("hass_config", "valid"), [ ( { - "name": "test_valid_1", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test_valid_1", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + } + } }, True, ), ( { - "name": "test_valid_2", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", - "device_class": "humidifier", + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test_valid_2", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + "device_class": "humidifier", + } + } }, True, ), ( { - "name": "test_valid_3", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", - "device_class": "dehumidifier", + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test_valid_3", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + "device_class": "dehumidifier", + } + } }, True, ), ( { - "name": "test_invalid_device_class", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", - "device_class": "notsupporedSpeci@l", + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test_invalid_device_class", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + "device_class": "notsupporedSpeci@l", + } + } }, False, ), ( { - "name": "test_mode_command_without_modes", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", - "mode_command_topic": "mode-command-topic", + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test_mode_command_without_modes", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + "mode_command_topic": "mode-command-topic", + } + } }, False, ), ( { - "name": "test_invalid_humidity_min_max_1", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", - "min_humidity": 0, - "max_humidity": 101, + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test_invalid_humidity_min_max_1", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + "min_humidity": 0, + "max_humidity": 101, + } + } }, False, ), ( { - "name": "test_invalid_humidity_min_max_2", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", - "max_humidity": 20, - "min_humidity": 40, + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test_invalid_humidity_min_max_2", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + "max_humidity": 20, + "min_humidity": 40, + } + } }, False, ), ( { - "name": "test_invalid_mode_is_reset", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", - "mode_command_topic": "mode-command-topic", - "modes": ["eco", "None"], + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test_invalid_mode_is_reset", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + "mode_command_topic": "mode-command-topic", + "modes": ["eco", "None"], + } + } }, False, ), ], ) -async def test_validity_configurations(hass: HomeAssistant, config, valid) -> None: +async def test_validity_configurations( + hass: HomeAssistant, + mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, + valid: bool, +) -> None: """Test validity of configurations.""" - assert ( - await async_setup_component( - hass, - mqtt.DOMAIN, - {mqtt.DOMAIN: {humidifier.DOMAIN: config}}, - ) - is valid - ) + if valid: + await mqtt_mock_entry_no_yaml_config() + return + with pytest.raises(AssertionError): + await mqtt_mock_entry_no_yaml_config() @pytest.mark.parametrize( - ("name", "config", "success", "features"), + ("name", "hass_config", "success", "features"), [ ( "test1", { - "name": "test1", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test1", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + } + } }, True, 0, @@ -932,21 +970,29 @@ async def test_validity_configurations(hass: HomeAssistant, config, valid) -> No ( "test2", { - "name": "test2", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", - "mode_command_topic": "mode-command-topic", - "modes": ["eco", "auto"], + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test2", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + "mode_command_topic": "mode-command-topic", + "modes": ["eco", "auto"], + } + } }, True, - humidifier.SUPPORT_MODES, + humidifier.HumidifierEntityFeature.MODES, ), ( "test3", { - "name": "test3", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test3", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + } + } }, True, 0, @@ -954,20 +1000,28 @@ async def test_validity_configurations(hass: HomeAssistant, config, valid) -> No ( "test4", { - "name": "test4", - "command_topic": "command-topic", - "target_humidity_command_topic": "humidity-command-topic", - "mode_command_topic": "mode-command-topic", - "modes": ["eco", "auto"], + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test4", + "command_topic": "command-topic", + "target_humidity_command_topic": "humidity-command-topic", + "mode_command_topic": "mode-command-topic", + "modes": ["eco", "auto"], + } + } }, True, - humidifier.SUPPORT_MODES, + humidifier.HumidifierEntityFeature.MODES, ), ( "test5", { - "name": "test5", - "command_topic": "command-topic", + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test5", + "command_topic": "command-topic", + } + } }, False, None, @@ -975,8 +1029,12 @@ async def test_validity_configurations(hass: HomeAssistant, config, valid) -> No ( "test6", { - "name": "test6", - "target_humidity_command_topic": "humidity-command-topic", + mqtt.DOMAIN: { + humidifier.DOMAIN: { + "name": "test6", + "target_humidity_command_topic": "humidity-command-topic", + } + } }, False, None, @@ -985,27 +1043,20 @@ async def test_validity_configurations(hass: HomeAssistant, config, valid) -> No ) async def test_supported_features( hass: HomeAssistant, - mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator, - name, - config, - success, - features, + mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, + name: str, + success: bool, + features: humidifier.HumidifierEntityFeature | None, ) -> None: """Test supported features.""" - assert ( - await async_setup_component( - hass, - mqtt.DOMAIN, - {mqtt.DOMAIN: {humidifier.DOMAIN: config}}, - ) - is success - ) if success: - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get(f"humidifier.{name}") assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == features + return + with pytest.raises(AssertionError): + await mqtt_mock_entry_no_yaml_config() @pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG]) @@ -1388,17 +1439,6 @@ async def test_setup_manual_entity_from_yaml( assert hass.states.get(f"{platform}.test") -async def test_config_schema_validation(hass: HomeAssistant) -> None: - """Test invalid platform options in the config schema do not pass the config validation.""" - platform = humidifier.DOMAIN - config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][platform]) - config["name"] = "test" - CONFIG_SCHEMA({mqtt.DOMAIN: {platform: config}}) - CONFIG_SCHEMA({mqtt.DOMAIN: {platform: [config]}}) - with pytest.raises(MultipleInvalid): - CONFIG_SCHEMA({mqtt.DOMAIN: {platform: [{"bla": "bla"}]}}) - - async def test_unload_config_entry( hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, diff --git a/tests/components/mqtt/test_legacy_vacuum.py b/tests/components/mqtt/test_legacy_vacuum.py index 42077fee0a7..e4cf5bb8030 100644 --- a/tests/components/mqtt/test_legacy_vacuum.py +++ b/tests/components/mqtt/test_legacy_vacuum.py @@ -32,9 +32,10 @@ from homeassistant.components.vacuum import ( ) from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON, Platform from homeassistant.core import HomeAssistant -from homeassistant.setup import async_setup_component +from homeassistant.helpers.typing import ConfigType from .test_common import ( + help_custom_config, help_test_availability_when_connection_lost, help_test_availability_without_topic, help_test_custom_availability_payload, @@ -92,6 +93,28 @@ DEFAULT_CONFIG = { DEFAULT_CONFIG_2 = {mqtt.DOMAIN: {vacuum.DOMAIN: {"name": "test"}}} +DEFAULT_CONFIG_ALL_SERVICES = help_custom_config( + vacuum.DOMAIN, + DEFAULT_CONFIG, + ( + { + mqttvacuum.CONF_SUPPORTED_FEATURES: services_to_strings( + ALL_SERVICES, SERVICE_TO_STRING + ) + }, + ), +) + + +def filter_options(default_config: ConfigType, options: set[str]) -> ConfigType: + """Generate a config from a default config with omitted options.""" + options_base: ConfigType = default_config[mqtt.DOMAIN][vacuum.DOMAIN] + config = deepcopy(default_config) + config[mqtt.DOMAIN][vacuum.DOMAIN] = { + key: value for key, value in options_base.items() if key not in options + } + return config + @pytest.fixture(autouse=True) def vacuum_platform_only(): @@ -100,13 +123,12 @@ def vacuum_platform_only(): yield +@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG]) async def test_default_supported_features( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test that the correct supported features.""" - assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() entity = hass.states.get("vacuum.mqtttest") entity_features = entity.attributes.get(mqttvacuum.CONF_SUPPORTED_FEATURES, 0) assert sorted(services_to_strings(entity_features, SERVICE_TO_STRING)) == sorted( @@ -122,20 +144,15 @@ async def test_default_supported_features( ) +@pytest.mark.parametrize( + "hass_config", + [DEFAULT_CONFIG_ALL_SERVICES], +) async def test_all_commands( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test simple commands to the vacuum.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - ALL_SERVICES, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - mqtt_mock = await mqtt_mock_entry_with_yaml_config() + mqtt_mock = await mqtt_mock_entry_no_yaml_config() await common.async_turn_on(hass, "vacuum.mqtttest") mqtt_mock.async_publish.assert_called_once_with( @@ -206,21 +223,27 @@ async def test_all_commands( } +@pytest.mark.parametrize( + "hass_config", + [ + help_custom_config( + vacuum.DOMAIN, + DEFAULT_CONFIG, + ( + { + mqttvacuum.CONF_SUPPORTED_FEATURES: services_to_strings( + mqttvacuum.STRING_TO_SERVICE["status"], SERVICE_TO_STRING + ) + }, + ), + ) + ], +) async def test_commands_without_supported_features( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test commands which are not supported by the vacuum.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - services = mqttvacuum.STRING_TO_SERVICE["status"] - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - services, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - mqtt_mock = await mqtt_mock_entry_with_yaml_config() + mqtt_mock = await mqtt_mock_entry_no_yaml_config() await common.async_turn_on(hass, "vacuum.mqtttest") mqtt_mock.async_publish.assert_not_called() @@ -259,21 +282,27 @@ async def test_commands_without_supported_features( mqtt_mock.async_publish.reset_mock() +@pytest.mark.parametrize( + "hass_config", + [ + help_custom_config( + vacuum.DOMAIN, + DEFAULT_CONFIG, + ( + { + mqttvacuum.CONF_SUPPORTED_FEATURES: services_to_strings( + mqttvacuum.STRING_TO_SERVICE["turn_on"], SERVICE_TO_STRING + ) + }, + ), + ) + ], +) async def test_attributes_without_supported_features( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test attributes which are not supported by the vacuum.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - services = mqttvacuum.STRING_TO_SERVICE["turn_on"] - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - services, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() message = """{ "battery_level": 54, @@ -291,20 +320,15 @@ async def test_attributes_without_supported_features( assert state.attributes.get(ATTR_FAN_SPEED_LIST) is None +@pytest.mark.parametrize( + "hass_config", + [DEFAULT_CONFIG_ALL_SERVICES], +) async def test_status( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test status updates from the vacuum.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - ALL_SERVICES, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() message = """{ "battery_level": 54, @@ -336,20 +360,15 @@ async def test_status( assert state.attributes.get(ATTR_FAN_SPEED) == "min" +@pytest.mark.parametrize( + "hass_config", + [DEFAULT_CONFIG_ALL_SERVICES], +) async def test_status_battery( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test status updates from the vacuum.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - ALL_SERVICES, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() message = """{ "battery_level": 54 @@ -359,20 +378,16 @@ async def test_status_battery( assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-50" +@pytest.mark.parametrize( + "hass_config", + [DEFAULT_CONFIG_ALL_SERVICES], +) async def test_status_cleaning( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test status updates from the vacuum.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - ALL_SERVICES, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() message = """{ "cleaning": true @@ -382,20 +397,15 @@ async def test_status_cleaning( assert state.state == STATE_ON +@pytest.mark.parametrize( + "hass_config", + [DEFAULT_CONFIG_ALL_SERVICES], +) async def test_status_docked( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test status updates from the vacuum.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - ALL_SERVICES, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() message = """{ "docked": true @@ -405,20 +415,15 @@ async def test_status_docked( assert state.state == STATE_OFF +@pytest.mark.parametrize( + "hass_config", + [DEFAULT_CONFIG_ALL_SERVICES], +) async def test_status_charging( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test status updates from the vacuum.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - ALL_SERVICES, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() message = """{ "charging": true @@ -428,20 +433,12 @@ async def test_status_charging( assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-outline" +@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG_ALL_SERVICES]) async def test_status_fan_speed( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test status updates from the vacuum.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - ALL_SERVICES, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() message = """{ "fan_speed": "max" @@ -451,62 +448,52 @@ async def test_status_fan_speed( assert state.attributes.get(ATTR_FAN_SPEED) == "max" +@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG_ALL_SERVICES]) async def test_status_fan_speed_list( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test status updates from the vacuum.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - ALL_SERVICES, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("vacuum.mqtttest") assert state.attributes.get(ATTR_FAN_SPEED_LIST) == ["min", "medium", "high", "max"] +@pytest.mark.parametrize( + "hass_config", + [ + help_custom_config( + vacuum.DOMAIN, + DEFAULT_CONFIG, + ( + { + mqttvacuum.CONF_SUPPORTED_FEATURES: services_to_strings( + ALL_SERVICES - VacuumEntityFeature.FAN_SPEED, SERVICE_TO_STRING + ) + }, + ), + ) + ], +) async def test_status_no_fan_speed_list( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test status updates from the vacuum. If the vacuum doesn't support fan speed, fan speed list should be None. """ - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - services = ALL_SERVICES - VacuumEntityFeature.FAN_SPEED - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - services, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() state = hass.states.get("vacuum.mqtttest") assert state.attributes.get(ATTR_FAN_SPEED_LIST) is None +@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG_ALL_SERVICES]) async def test_status_error( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test status updates from the vacuum.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - ALL_SERVICES, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() message = """{ "error": "Error1" @@ -523,26 +510,26 @@ async def test_status_error( assert state.attributes.get(ATTR_STATUS) == "Stopped" +@pytest.mark.parametrize( + "hass_config", + [ + help_custom_config( + vacuum.DOMAIN, + DEFAULT_CONFIG, + ( + { + mqttvacuum.CONF_BATTERY_LEVEL_TOPIC: "retroroomba/battery_level", + mqttvacuum.CONF_BATTERY_LEVEL_TEMPLATE: "{{ value }}", + }, + ), + ) + ], +) async def test_battery_template( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test that you can use non-default templates for battery_level.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config.update( - { - mqttvacuum.CONF_SUPPORTED_FEATURES: services_to_strings( - ALL_SERVICES, SERVICE_TO_STRING - ), - mqttvacuum.CONF_BATTERY_LEVEL_TOPIC: "retroroomba/battery_level", - mqttvacuum.CONF_BATTERY_LEVEL_TEMPLATE: "{{ value }}", - } - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message(hass, "retroroomba/battery_level", "54") state = hass.states.get("vacuum.mqtttest") @@ -550,20 +537,12 @@ async def test_battery_template( assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-50" +@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG_ALL_SERVICES]) async def test_status_invalid_json( - hass: HomeAssistant, mqtt_mock_entry_with_yaml_config: MqttMockHAClientGenerator + hass: HomeAssistant, mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator ) -> None: """Test to make sure nothing breaks if the vacuum sends bad JSON.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( - ALL_SERVICES, SERVICE_TO_STRING - ) - - assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - await hass.async_block_till_done() - await mqtt_mock_entry_with_yaml_config() + await mqtt_mock_entry_no_yaml_config() async_fire_mqtt_message(hass, "vacuum/state", '{"asdfasas false}') state = hass.states.get("vacuum.mqtttest") @@ -571,63 +550,28 @@ async def test_status_invalid_json( assert state.attributes.get(ATTR_STATUS) == "Stopped" -async def test_missing_battery_template(hass: HomeAssistant) -> None: +@pytest.mark.parametrize( + "hass_config", + [ + filter_options(DEFAULT_CONFIG, {mqttvacuum.CONF_BATTERY_LEVEL_TEMPLATE}), + filter_options(DEFAULT_CONFIG, {mqttvacuum.CONF_CHARGING_TEMPLATE}), + filter_options(DEFAULT_CONFIG, {mqttvacuum.CONF_CLEANING_TEMPLATE}), + filter_options(DEFAULT_CONFIG, {mqttvacuum.CONF_DOCKED_TEMPLATE}), + filter_options(DEFAULT_CONFIG, {mqttvacuum.CONF_ERROR_TEMPLATE}), + filter_options(DEFAULT_CONFIG, {mqttvacuum.CONF_FAN_SPEED_TEMPLATE}), + ], +) +async def test_missing_templates( + hass: HomeAssistant, + mqtt_mock_entry_no_yaml_config: MqttMockHAClientGenerator, + caplog: pytest.LogCaptureFixture, +) -> None: """Test to make sure missing template is not allowed.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config.pop(mqttvacuum.CONF_BATTERY_LEVEL_TEMPLATE) - - assert not await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - - -async def test_missing_charging_template(hass: HomeAssistant) -> None: - """Test to make sure missing template is not allowed.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config.pop(mqttvacuum.CONF_CHARGING_TEMPLATE) - - assert not await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - - -async def test_missing_cleaning_template(hass: HomeAssistant) -> None: - """Test to make sure missing template is not allowed.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config.pop(mqttvacuum.CONF_CLEANING_TEMPLATE) - - assert not await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - - -async def test_missing_docked_template(hass: HomeAssistant) -> None: - """Test to make sure missing template is not allowed.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config.pop(mqttvacuum.CONF_DOCKED_TEMPLATE) - - assert not await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - - -async def test_missing_error_template(hass: HomeAssistant) -> None: - """Test to make sure missing template is not allowed.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config.pop(mqttvacuum.CONF_ERROR_TEMPLATE) - - assert not await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} - ) - - -async def test_missing_fan_speed_template(hass: HomeAssistant) -> None: - """Test to make sure missing template is not allowed.""" - config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN]) - config.pop(mqttvacuum.CONF_FAN_SPEED_TEMPLATE) - - assert not await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}} + with pytest.raises(AssertionError): + await mqtt_mock_entry_no_yaml_config() + assert ( + "Invalid config for [mqtt]: some but not all values in the same group of inclusion" + in caplog.text )