2018-09-14 09:57:31 +00:00
|
|
|
"""Test config flow."""
|
2023-02-09 15:47:28 +00:00
|
|
|
from collections.abc import Generator
|
2023-02-09 08:48:22 +00:00
|
|
|
from pathlib import Path
|
2022-10-24 07:58:23 +00:00
|
|
|
from random import getrandbits
|
|
|
|
from ssl import SSLError
|
2023-02-09 15:47:28 +00:00
|
|
|
from typing import Any
|
|
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
2022-10-24 07:58:23 +00:00
|
|
|
from uuid import uuid4
|
2021-01-01 21:31:56 +00:00
|
|
|
|
2018-09-14 09:57:31 +00:00
|
|
|
import pytest
|
2020-06-23 00:49:01 +00:00
|
|
|
import voluptuous as vol
|
2018-09-14 09:57:31 +00:00
|
|
|
|
2023-03-28 07:37:07 +00:00
|
|
|
from homeassistant import config_entries, data_entry_flow
|
2020-06-23 00:49:01 +00:00
|
|
|
from homeassistant.components import mqtt
|
2021-12-03 13:05:56 +00:00
|
|
|
from homeassistant.components.hassio import HassioServiceInfo
|
2021-08-26 09:14:42 +00:00
|
|
|
from homeassistant.core import HomeAssistant
|
2018-09-14 09:57:31 +00:00
|
|
|
|
2020-04-30 23:31:00 +00:00
|
|
|
from tests.common import MockConfigEntry
|
2023-02-09 08:48:22 +00:00
|
|
|
from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient
|
2018-09-14 09:57:31 +00:00
|
|
|
|
2022-10-24 07:58:23 +00:00
|
|
|
MOCK_CLIENT_CERT = b"## mock client certificate file ##"
|
|
|
|
MOCK_CLIENT_KEY = b"## mock key file ##"
|
|
|
|
|
2018-09-14 09:57:31 +00:00
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
2023-02-09 15:47:28 +00:00
|
|
|
def mock_finish_setup() -> Generator[MagicMock, None, None]:
|
2018-09-14 09:57:31 +00:00
|
|
|
"""Mock out the finish setup method."""
|
2019-07-31 19:25:30 +00:00
|
|
|
with patch(
|
2020-04-30 23:31:00 +00:00
|
|
|
"homeassistant.components.mqtt.MQTT.async_connect", return_value=True
|
2019-07-31 19:25:30 +00:00
|
|
|
) as mock_finish:
|
2018-09-14 09:57:31 +00:00
|
|
|
yield mock_finish
|
|
|
|
|
|
|
|
|
2022-10-24 07:58:23 +00:00
|
|
|
@pytest.fixture
|
2023-02-09 15:47:28 +00:00
|
|
|
def mock_client_cert_check_fail() -> Generator[MagicMock, None, None]:
|
2022-10-24 07:58:23 +00:00
|
|
|
"""Mock the client certificate check."""
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.mqtt.config_flow.load_pem_x509_certificate",
|
|
|
|
side_effect=ValueError,
|
|
|
|
) as mock_cert_check:
|
|
|
|
yield mock_cert_check
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2023-02-09 15:47:28 +00:00
|
|
|
def mock_client_key_check_fail() -> Generator[MagicMock, None, None]:
|
2022-10-24 07:58:23 +00:00
|
|
|
"""Mock the client key file check."""
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.mqtt.config_flow.load_pem_private_key",
|
|
|
|
side_effect=ValueError,
|
|
|
|
) as mock_key_check:
|
|
|
|
yield mock_key_check
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2023-02-09 15:47:28 +00:00
|
|
|
def mock_ssl_context() -> Generator[dict[str, MagicMock], None, None]:
|
2022-10-24 07:58:23 +00:00
|
|
|
"""Mock the SSL context used to load the cert chain and to load verify locations."""
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.mqtt.config_flow.SSLContext"
|
|
|
|
) as mock_context, patch(
|
|
|
|
"homeassistant.components.mqtt.config_flow.load_pem_private_key"
|
|
|
|
) as mock_key_check, patch(
|
|
|
|
"homeassistant.components.mqtt.config_flow.load_pem_x509_certificate"
|
|
|
|
) as mock_cert_check:
|
|
|
|
yield {
|
|
|
|
"context": mock_context,
|
|
|
|
"load_pem_x509_certificate": mock_cert_check,
|
|
|
|
"load_pem_private_key": mock_key_check,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-09-27 06:05:28 +00:00
|
|
|
@pytest.fixture
|
2023-02-09 15:47:28 +00:00
|
|
|
def mock_reload_after_entry_update() -> Generator[MagicMock, None, None]:
|
2022-09-27 06:05:28 +00:00
|
|
|
"""Mock out the reload after updating the entry."""
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.mqtt._async_config_entry_updated"
|
|
|
|
) as mock_reload:
|
|
|
|
yield mock_reload
|
|
|
|
|
|
|
|
|
2018-09-14 09:57:31 +00:00
|
|
|
@pytest.fixture
|
2023-02-09 15:47:28 +00:00
|
|
|
def mock_try_connection() -> Generator[MagicMock, None, None]:
|
2018-09-14 09:57:31 +00:00
|
|
|
"""Mock the try connection method."""
|
2019-07-31 19:25:30 +00:00
|
|
|
with patch("homeassistant.components.mqtt.config_flow.try_connection") as mock_try:
|
2018-09-14 09:57:31 +00:00
|
|
|
yield mock_try
|
|
|
|
|
|
|
|
|
2022-02-23 11:29:32 +00:00
|
|
|
@pytest.fixture
|
2023-02-09 15:47:28 +00:00
|
|
|
def mock_try_connection_success() -> Generator[MqttMockPahoClient, None, None]:
|
2022-02-23 11:29:32 +00:00
|
|
|
"""Mock the try connection method with success."""
|
|
|
|
|
2022-03-30 03:26:11 +00:00
|
|
|
_mid = 1
|
|
|
|
|
|
|
|
def get_mid():
|
|
|
|
nonlocal _mid
|
|
|
|
_mid += 1
|
|
|
|
return _mid
|
|
|
|
|
2022-02-23 11:29:32 +00:00
|
|
|
def loop_start():
|
|
|
|
"""Simulate connect on loop start."""
|
|
|
|
mock_client().on_connect(mock_client, None, None, 0)
|
|
|
|
|
2022-03-30 03:26:11 +00:00
|
|
|
def _subscribe(topic, qos=0):
|
|
|
|
mid = get_mid()
|
|
|
|
mock_client().on_subscribe(mock_client, 0, mid)
|
|
|
|
return (0, mid)
|
|
|
|
|
|
|
|
def _unsubscribe(topic):
|
|
|
|
mid = get_mid()
|
|
|
|
mock_client().on_unsubscribe(mock_client, 0, mid)
|
|
|
|
return (0, mid)
|
|
|
|
|
2022-02-23 11:29:32 +00:00
|
|
|
with patch("paho.mqtt.client.Client") as mock_client:
|
|
|
|
mock_client().loop_start = loop_start
|
2022-03-30 03:26:11 +00:00
|
|
|
mock_client().subscribe = _subscribe
|
|
|
|
mock_client().unsubscribe = _unsubscribe
|
|
|
|
|
2022-02-23 11:29:32 +00:00
|
|
|
yield mock_client()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2023-02-09 15:47:28 +00:00
|
|
|
def mock_try_connection_time_out() -> Generator[MagicMock, None, None]:
|
2022-02-23 11:29:32 +00:00
|
|
|
"""Mock the try connection method with a time out."""
|
|
|
|
|
|
|
|
# Patch prevent waiting 5 sec for a timeout
|
|
|
|
with patch("paho.mqtt.client.Client") as mock_client, patch(
|
|
|
|
"homeassistant.components.mqtt.config_flow.MQTT_TIMEOUT", 0
|
|
|
|
):
|
|
|
|
mock_client().loop_start = lambda *args: 1
|
|
|
|
yield mock_client()
|
|
|
|
|
|
|
|
|
2022-10-24 07:58:23 +00:00
|
|
|
@pytest.fixture
|
2023-02-09 15:47:28 +00:00
|
|
|
def mock_process_uploaded_file(tmp_path: Path) -> Generator[MagicMock, None, None]:
|
2022-10-24 07:58:23 +00:00
|
|
|
"""Mock upload certificate files."""
|
|
|
|
file_id_ca = str(uuid4())
|
|
|
|
file_id_cert = str(uuid4())
|
|
|
|
file_id_key = str(uuid4())
|
|
|
|
|
2023-02-08 17:08:03 +00:00
|
|
|
def _mock_process_uploaded_file(hass: HomeAssistant, file_id) -> None:
|
2022-10-24 07:58:23 +00:00
|
|
|
if file_id == file_id_ca:
|
|
|
|
with open(tmp_path / "ca.crt", "wb") as cafile:
|
|
|
|
cafile.write(b"## mock CA certificate file ##")
|
|
|
|
return tmp_path / "ca.crt"
|
|
|
|
elif file_id == file_id_cert:
|
|
|
|
with open(tmp_path / "client.crt", "wb") as certfile:
|
|
|
|
certfile.write(b"## mock client certificate file ##")
|
|
|
|
return tmp_path / "client.crt"
|
|
|
|
elif file_id == file_id_key:
|
|
|
|
with open(tmp_path / "client.key", "wb") as keyfile:
|
|
|
|
keyfile.write(b"## mock key file ##")
|
|
|
|
return tmp_path / "client.key"
|
|
|
|
else:
|
2023-01-27 10:10:29 +00:00
|
|
|
pytest.fail(f"Unexpected file_id: {file_id}")
|
2022-10-24 07:58:23 +00:00
|
|
|
|
|
|
|
with patch(
|
|
|
|
"homeassistant.components.mqtt.config_flow.process_uploaded_file",
|
|
|
|
side_effect=_mock_process_uploaded_file,
|
|
|
|
) as mock_upload, patch(
|
|
|
|
# Patch temp dir name to avoid tests fail running in parallel
|
|
|
|
"homeassistant.components.mqtt.util.TEMP_DIR_NAME",
|
|
|
|
"home-assistant-mqtt" + f"-{getrandbits(10):03x}",
|
|
|
|
):
|
|
|
|
mock_upload.file_id = {
|
|
|
|
mqtt.CONF_CERTIFICATE: file_id_ca,
|
|
|
|
mqtt.CONF_CLIENT_CERT: file_id_cert,
|
|
|
|
mqtt.CONF_CLIENT_KEY: file_id_key,
|
|
|
|
}
|
|
|
|
yield mock_upload
|
|
|
|
|
|
|
|
|
2021-10-05 09:14:53 +00:00
|
|
|
async def test_user_connection_works(
|
2023-02-09 08:48:22 +00:00
|
|
|
hass: HomeAssistant,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection: MagicMock,
|
|
|
|
mock_finish_setup: MagicMock,
|
2023-02-09 08:48:22 +00:00
|
|
|
mqtt_client_mock: MqttMockPahoClient,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2018-09-14 09:57:31 +00:00
|
|
|
"""Test we can finish a config flow."""
|
|
|
|
mock_try_connection.return_value = True
|
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
2021-04-25 09:27:40 +00:00
|
|
|
"mqtt", context={"source": config_entries.SOURCE_USER}
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
assert result["type"] == "form"
|
2018-09-14 09:57:31 +00:00
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
2023-04-04 16:12:18 +00:00
|
|
|
result["flow_id"], {"broker": "127.0.0.1"}
|
2018-09-14 09:57:31 +00:00
|
|
|
)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert result["type"] == "create_entry"
|
|
|
|
assert result["result"].data == {
|
|
|
|
"broker": "127.0.0.1",
|
|
|
|
"port": 1883,
|
2020-10-07 12:51:06 +00:00
|
|
|
"discovery": True,
|
2018-09-25 10:22:14 +00:00
|
|
|
}
|
2018-09-14 09:57:31 +00:00
|
|
|
# Check we tried the connection
|
|
|
|
assert len(mock_try_connection.mock_calls) == 1
|
|
|
|
# Check config entry got setup
|
|
|
|
assert len(mock_finish_setup.mock_calls) == 1
|
|
|
|
|
|
|
|
|
2022-11-17 20:12:27 +00:00
|
|
|
async def test_user_v5_connection_works(
|
2023-02-09 08:48:22 +00:00
|
|
|
hass: HomeAssistant,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection: MagicMock,
|
|
|
|
mock_finish_setup: MagicMock,
|
2023-02-09 08:48:22 +00:00
|
|
|
mqtt_client_mock: MqttMockPahoClient,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2022-11-17 20:12:27 +00:00
|
|
|
"""Test we can finish a config flow."""
|
|
|
|
mock_try_connection.return_value = True
|
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
2023-04-25 20:23:17 +00:00
|
|
|
"mqtt",
|
|
|
|
context={"source": config_entries.SOURCE_USER, "show_advanced_options": True},
|
2022-11-17 20:12:27 +00:00
|
|
|
)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
|
|
result["flow_id"], {"broker": "127.0.0.1", "advanced_options": True}
|
|
|
|
)
|
|
|
|
|
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_PROTOCOL: "5",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert result["type"] == "create_entry"
|
|
|
|
assert result["result"].data == {
|
|
|
|
"broker": "another-broker",
|
|
|
|
"discovery": True,
|
|
|
|
"port": 2345,
|
|
|
|
"protocol": "5",
|
|
|
|
}
|
|
|
|
# Check we tried the connection
|
|
|
|
assert len(mock_try_connection.mock_calls) == 1
|
|
|
|
# Check config entry got setup
|
|
|
|
assert len(mock_finish_setup.mock_calls) == 1
|
|
|
|
|
|
|
|
|
2022-02-23 11:29:32 +00:00
|
|
|
async def test_user_connection_fails(
|
2023-02-09 15:47:28 +00:00
|
|
|
hass: HomeAssistant,
|
|
|
|
mock_try_connection_time_out: MagicMock,
|
|
|
|
mock_finish_setup: MagicMock,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2020-01-31 16:33:00 +00:00
|
|
|
"""Test if connection cannot be made."""
|
2018-09-14 09:57:31 +00:00
|
|
|
result = await hass.config_entries.flow.async_init(
|
2021-04-25 09:27:40 +00:00
|
|
|
"mqtt", context={"source": config_entries.SOURCE_USER}
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
assert result["type"] == "form"
|
2018-09-14 09:57:31 +00:00
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
2019-07-31 19:25:30 +00:00
|
|
|
result["flow_id"], {"broker": "127.0.0.1"}
|
2018-09-14 09:57:31 +00:00
|
|
|
)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["errors"]["base"] == "cannot_connect"
|
2018-09-14 09:57:31 +00:00
|
|
|
|
|
|
|
# Check we tried the connection
|
2022-02-23 11:29:32 +00:00
|
|
|
assert len(mock_try_connection_time_out.mock_calls)
|
2018-09-14 09:57:31 +00:00
|
|
|
# Check config entry did not setup
|
|
|
|
assert len(mock_finish_setup.mock_calls) == 0
|
|
|
|
|
|
|
|
|
2023-03-28 07:37:07 +00:00
|
|
|
@pytest.mark.parametrize("hass_config", [{"mqtt": {"sensor": {"state_topic": "test"}}}])
|
2021-10-05 09:14:53 +00:00
|
|
|
async def test_manual_config_set(
|
2023-02-08 17:08:03 +00:00
|
|
|
hass: HomeAssistant,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection: MqttMockPahoClient,
|
|
|
|
mock_finish_setup: MagicMock,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2022-02-10 20:09:57 +00:00
|
|
|
"""Test manual config does not create an entry, and entry can be setup late."""
|
|
|
|
assert len(mock_finish_setup.mock_calls) == 0
|
2018-09-14 09:57:31 +00:00
|
|
|
|
|
|
|
mock_try_connection.return_value = True
|
|
|
|
|
2022-02-10 20:09:57 +00:00
|
|
|
# Start config flow
|
2018-09-14 09:57:31 +00:00
|
|
|
result = await hass.config_entries.flow.async_init(
|
2021-04-25 09:27:40 +00:00
|
|
|
"mqtt", context={"source": config_entries.SOURCE_USER}
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2022-02-10 20:09:57 +00:00
|
|
|
assert result["type"] == "form"
|
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
2023-04-04 16:12:18 +00:00
|
|
|
result["flow_id"], {"broker": "127.0.0.1", "port": "1883"}
|
2022-02-10 20:09:57 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
assert result["type"] == "create_entry"
|
|
|
|
assert result["result"].data == {
|
|
|
|
"broker": "127.0.0.1",
|
|
|
|
"port": 1883,
|
|
|
|
"discovery": True,
|
|
|
|
}
|
|
|
|
# Check we tried the connection, with precedence for config entry settings
|
2022-09-28 12:13:44 +00:00
|
|
|
mock_try_connection.assert_called_once_with(
|
|
|
|
{
|
2022-10-07 08:12:19 +00:00
|
|
|
"broker": "127.0.0.1",
|
|
|
|
"port": 1883,
|
2022-10-24 07:58:23 +00:00
|
|
|
"discovery": True,
|
2022-09-28 12:13:44 +00:00
|
|
|
},
|
|
|
|
)
|
2022-02-10 20:09:57 +00:00
|
|
|
# Check config entry got setup
|
|
|
|
assert len(mock_finish_setup.mock_calls) == 1
|
2022-09-27 06:05:28 +00:00
|
|
|
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
|
|
|
|
assert config_entry.title == "127.0.0.1"
|
2018-10-01 12:11:21 +00:00
|
|
|
|
|
|
|
|
2023-02-08 17:08:03 +00:00
|
|
|
async def test_user_single_instance(hass: HomeAssistant) -> None:
|
2018-10-01 12:11:21 +00:00
|
|
|
"""Test we only allow a single config flow."""
|
2019-07-31 19:25:30 +00:00
|
|
|
MockConfigEntry(domain="mqtt").add_to_hass(hass)
|
2018-10-01 12:11:21 +00:00
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
2021-04-25 09:27:40 +00:00
|
|
|
"mqtt", context={"source": config_entries.SOURCE_USER}
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
assert result["type"] == "abort"
|
|
|
|
assert result["reason"] == "single_instance_allowed"
|
2018-10-01 12:11:21 +00:00
|
|
|
|
|
|
|
|
2023-02-08 17:08:03 +00:00
|
|
|
async def test_hassio_already_configured(hass: HomeAssistant) -> None:
|
2018-10-01 12:11:21 +00:00
|
|
|
"""Test we only allow a single config flow."""
|
2019-07-31 19:25:30 +00:00
|
|
|
MockConfigEntry(domain="mqtt").add_to_hass(hass)
|
2018-10-01 12:11:21 +00:00
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
2021-12-02 20:13:41 +00:00
|
|
|
"mqtt", context={"source": config_entries.SOURCE_HASSIO}
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
assert result["type"] == "abort"
|
2021-08-26 09:14:42 +00:00
|
|
|
assert result["reason"] == "already_configured"
|
|
|
|
|
|
|
|
|
|
|
|
async def test_hassio_ignored(hass: HomeAssistant) -> None:
|
|
|
|
"""Test we supervisor discovered instance can be ignored."""
|
|
|
|
MockConfigEntry(
|
|
|
|
domain=mqtt.DOMAIN, source=config_entries.SOURCE_IGNORE
|
|
|
|
).add_to_hass(hass)
|
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
mqtt.DOMAIN,
|
2021-12-03 13:05:56 +00:00
|
|
|
data=HassioServiceInfo(
|
2022-02-23 11:29:32 +00:00
|
|
|
config={
|
|
|
|
"addon": "Mosquitto",
|
|
|
|
"host": "mock-mosquitto",
|
|
|
|
"port": "1883",
|
|
|
|
"protocol": "3.1.1",
|
2022-10-11 14:56:45 +00:00
|
|
|
},
|
|
|
|
name="Mosquitto",
|
|
|
|
slug="mosquitto",
|
2023-04-25 07:48:47 +00:00
|
|
|
uuid="1234",
|
2021-12-03 13:05:56 +00:00
|
|
|
),
|
2021-08-26 09:14:42 +00:00
|
|
|
context={"source": config_entries.SOURCE_HASSIO},
|
|
|
|
)
|
|
|
|
assert result
|
2022-07-07 16:57:36 +00:00
|
|
|
assert result.get("type") == data_entry_flow.FlowResultType.ABORT
|
2021-08-26 09:14:42 +00:00
|
|
|
assert result.get("reason") == "already_configured"
|
2018-10-01 12:11:21 +00:00
|
|
|
|
|
|
|
|
2023-02-08 17:08:03 +00:00
|
|
|
async def test_hassio_confirm(
|
2023-02-09 15:47:28 +00:00
|
|
|
hass: HomeAssistant,
|
|
|
|
mock_try_connection_success: MqttMockPahoClient,
|
|
|
|
mock_finish_setup: MagicMock,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2018-10-01 12:11:21 +00:00
|
|
|
"""Test we can finish a config flow."""
|
|
|
|
mock_try_connection.return_value = True
|
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
2019-07-31 19:25:30 +00:00
|
|
|
"mqtt",
|
2021-12-03 13:05:56 +00:00
|
|
|
data=HassioServiceInfo(
|
|
|
|
config={
|
|
|
|
"addon": "Mock Addon",
|
|
|
|
"host": "mock-broker",
|
|
|
|
"port": 1883,
|
|
|
|
"username": "mock-user",
|
|
|
|
"password": "mock-pass",
|
2022-05-30 12:25:36 +00:00
|
|
|
"protocol": "3.1.1", # Set by the addon's discovery, ignored by HA
|
|
|
|
"ssl": False, # Set by the addon's discovery, ignored by HA
|
2022-10-11 14:56:45 +00:00
|
|
|
},
|
|
|
|
name="Mock Addon",
|
|
|
|
slug="mosquitto",
|
2023-04-25 07:48:47 +00:00
|
|
|
uuid="1234",
|
2021-12-03 13:05:56 +00:00
|
|
|
),
|
2021-04-25 09:27:40 +00:00
|
|
|
context={"source": config_entries.SOURCE_HASSIO},
|
2018-10-01 12:11:21 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "hassio_confirm"
|
|
|
|
assert result["description_placeholders"] == {"addon": "Mock Addon"}
|
2018-10-01 12:11:21 +00:00
|
|
|
|
2022-02-23 11:29:32 +00:00
|
|
|
mock_try_connection_success.reset_mock()
|
2018-10-01 12:11:21 +00:00
|
|
|
result = await hass.config_entries.flow.async_configure(
|
2019-07-31 19:25:30 +00:00
|
|
|
result["flow_id"], {"discovery": True}
|
2018-10-01 12:11:21 +00:00
|
|
|
)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert result["type"] == "create_entry"
|
|
|
|
assert result["result"].data == {
|
|
|
|
"broker": "mock-broker",
|
|
|
|
"port": 1883,
|
|
|
|
"username": "mock-user",
|
|
|
|
"password": "mock-pass",
|
|
|
|
"discovery": True,
|
2018-10-01 12:11:21 +00:00
|
|
|
}
|
|
|
|
# Check we tried the connection
|
2022-02-23 11:29:32 +00:00
|
|
|
assert len(mock_try_connection_success.mock_calls)
|
2018-10-01 12:11:21 +00:00
|
|
|
# Check config entry got setup
|
|
|
|
assert len(mock_finish_setup.mock_calls) == 1
|
2020-06-23 00:49:01 +00:00
|
|
|
|
|
|
|
|
2022-10-07 08:12:19 +00:00
|
|
|
async def test_hassio_cannot_connect(
|
2023-02-09 15:47:28 +00:00
|
|
|
hass: HomeAssistant,
|
|
|
|
mock_try_connection_time_out: MagicMock,
|
|
|
|
mock_finish_setup: MagicMock,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2022-10-07 08:12:19 +00:00
|
|
|
"""Test a config flow is aborted when a connection was not successful."""
|
|
|
|
mock_try_connection.return_value = True
|
|
|
|
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
|
|
"mqtt",
|
|
|
|
data=HassioServiceInfo(
|
|
|
|
config={
|
|
|
|
"addon": "Mock Addon",
|
|
|
|
"host": "mock-broker",
|
|
|
|
"port": 1883,
|
|
|
|
"username": "mock-user",
|
|
|
|
"password": "mock-pass",
|
|
|
|
"protocol": "3.1.1", # Set by the addon's discovery, ignored by HA
|
|
|
|
"ssl": False, # Set by the addon's discovery, ignored by HA
|
2022-10-11 14:56:45 +00:00
|
|
|
},
|
|
|
|
name="Mock Addon",
|
|
|
|
slug="mosquitto",
|
2023-04-25 07:48:47 +00:00
|
|
|
uuid="1234",
|
2022-10-07 08:12:19 +00:00
|
|
|
),
|
|
|
|
context={"source": config_entries.SOURCE_HASSIO},
|
|
|
|
)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "hassio_confirm"
|
|
|
|
assert result["description_placeholders"] == {"addon": "Mock Addon"}
|
|
|
|
|
|
|
|
mock_try_connection_time_out.reset_mock()
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
|
|
result["flow_id"], {"discovery": True}
|
|
|
|
)
|
|
|
|
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["errors"]["base"] == "cannot_connect"
|
|
|
|
# Check we tried the connection
|
|
|
|
assert len(mock_try_connection_time_out.mock_calls)
|
|
|
|
# Check config entry got setup
|
|
|
|
assert len(mock_finish_setup.mock_calls) == 0
|
|
|
|
|
|
|
|
|
2022-09-27 06:05:28 +00:00
|
|
|
async def test_option_flow(
|
2023-02-08 17:08:03 +00:00
|
|
|
hass: HomeAssistant,
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection: MagicMock,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2020-06-23 00:49:01 +00:00
|
|
|
"""Test config flow options."""
|
2022-12-29 17:29:28 +00:00
|
|
|
with patch(
|
|
|
|
"homeassistant.config.async_hass_config_yaml", AsyncMock(return_value={})
|
|
|
|
) as yaml_mock:
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock = await mqtt_mock_entry()
|
2022-12-29 17:29:28 +00:00
|
|
|
mock_try_connection.return_value = True
|
|
|
|
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
}
|
2020-06-23 00:49:01 +00:00
|
|
|
|
2022-12-29 17:29:28 +00:00
|
|
|
mqtt_mock.async_connect.reset_mock()
|
2020-06-23 00:49:01 +00:00
|
|
|
|
2022-12-29 17:29:28 +00:00
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
|
|
assert result["step_id"] == "broker"
|
2020-06-23 00:49:01 +00:00
|
|
|
|
2022-12-29 17:29:28 +00:00
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "pass",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
|
|
assert result["step_id"] == "options"
|
|
|
|
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert mqtt_mock.async_connect.call_count == 0
|
|
|
|
|
|
|
|
yaml_mock.reset_mock()
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
"discovery_prefix": "homeassistant",
|
|
|
|
"birth_enable": True,
|
|
|
|
"birth_topic": "ha_state/online",
|
|
|
|
"birth_payload": "online",
|
|
|
|
"birth_qos": 1,
|
|
|
|
"birth_retain": True,
|
|
|
|
"will_enable": True,
|
|
|
|
"will_topic": "ha_state/offline",
|
|
|
|
"will_payload": "offline",
|
|
|
|
"will_qos": 2,
|
|
|
|
"will_retain": True,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
|
|
|
assert result["data"] == {}
|
|
|
|
assert config_entry.data == {
|
2020-06-23 00:49:01 +00:00
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "pass",
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
2022-12-29 17:29:28 +00:00
|
|
|
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
|
|
|
|
mqtt.CONF_BIRTH_MESSAGE: {
|
|
|
|
mqtt.ATTR_TOPIC: "ha_state/online",
|
|
|
|
mqtt.ATTR_PAYLOAD: "online",
|
|
|
|
mqtt.ATTR_QOS: 1,
|
|
|
|
mqtt.ATTR_RETAIN: True,
|
|
|
|
},
|
|
|
|
mqtt.CONF_WILL_MESSAGE: {
|
|
|
|
mqtt.ATTR_TOPIC: "ha_state/offline",
|
|
|
|
mqtt.ATTR_PAYLOAD: "offline",
|
|
|
|
mqtt.ATTR_QOS: 2,
|
|
|
|
mqtt.ATTR_RETAIN: True,
|
|
|
|
},
|
|
|
|
}
|
2020-06-23 00:49:01 +00:00
|
|
|
|
2022-12-29 17:29:28 +00:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert config_entry.title == "another-broker"
|
2022-09-27 06:05:28 +00:00
|
|
|
# assert that the entry was reloaded with the new config
|
2022-12-29 17:29:28 +00:00
|
|
|
assert yaml_mock.await_count
|
2020-06-23 00:49:01 +00:00
|
|
|
|
|
|
|
|
2022-10-24 07:58:23 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"test_error",
|
|
|
|
[
|
|
|
|
"bad_certificate",
|
|
|
|
"bad_client_cert",
|
|
|
|
"bad_client_key",
|
|
|
|
"bad_client_cert_key",
|
|
|
|
"invalid_inclusion",
|
|
|
|
None,
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_bad_certificate(
|
2023-02-08 17:08:03 +00:00
|
|
|
hass: HomeAssistant,
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection_success: MqttMockPahoClient,
|
|
|
|
mock_ssl_context: dict[str, MagicMock],
|
|
|
|
mock_process_uploaded_file: MagicMock,
|
|
|
|
test_error: str | None,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2022-10-24 07:58:23 +00:00
|
|
|
"""Test bad certificate tests."""
|
|
|
|
# Mock certificate files
|
|
|
|
file_id = mock_process_uploaded_file.file_id
|
|
|
|
test_input = {
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_CERTIFICATE: file_id[mqtt.CONF_CERTIFICATE],
|
|
|
|
mqtt.CONF_CLIENT_CERT: file_id[mqtt.CONF_CLIENT_CERT],
|
|
|
|
mqtt.CONF_CLIENT_KEY: file_id[mqtt.CONF_CLIENT_KEY],
|
|
|
|
"set_ca_cert": True,
|
|
|
|
"set_client_cert": True,
|
|
|
|
}
|
|
|
|
set_client_cert = True
|
|
|
|
set_ca_cert = "custom"
|
|
|
|
tls_insecure = False
|
|
|
|
if test_error == "bad_certificate":
|
|
|
|
# CA chain is not loading
|
|
|
|
mock_ssl_context["context"]().load_verify_locations.side_effect = SSLError
|
|
|
|
elif test_error == "bad_client_cert":
|
|
|
|
# Client certificate is invalid
|
|
|
|
mock_ssl_context["load_pem_x509_certificate"].side_effect = ValueError
|
|
|
|
elif test_error == "bad_client_key":
|
|
|
|
# Client key file is invalid
|
|
|
|
mock_ssl_context["load_pem_private_key"].side_effect = ValueError
|
|
|
|
elif test_error == "bad_client_cert_key":
|
|
|
|
# Client key file file and certificate do not pair
|
|
|
|
mock_ssl_context["context"]().load_cert_chain.side_effect = SSLError
|
|
|
|
elif test_error == "invalid_inclusion":
|
|
|
|
# Client key file without client cert, client cert without key file
|
|
|
|
test_input.pop(mqtt.CONF_CLIENT_KEY)
|
|
|
|
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock = await mqtt_mock_entry()
|
2022-10-24 07:58:23 +00:00
|
|
|
mock_try_connection.return_value = True
|
|
|
|
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
|
|
|
|
# Add at least one advanced option to get the full form
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
mqtt.CONF_CLIENT_ID: "custom1234",
|
|
|
|
mqtt.CONF_KEEPALIVE: 60,
|
|
|
|
mqtt.CONF_TLS_INSECURE: False,
|
|
|
|
mqtt.CONF_PROTOCOL: "3.1.1",
|
|
|
|
}
|
|
|
|
|
|
|
|
mqtt_mock.async_connect.reset_mock()
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_KEEPALIVE: 60,
|
|
|
|
"set_client_cert": set_client_cert,
|
|
|
|
"set_ca_cert": set_ca_cert,
|
|
|
|
mqtt.CONF_TLS_INSECURE: tls_insecure,
|
|
|
|
mqtt.CONF_PROTOCOL: "3.1.1",
|
|
|
|
mqtt.CONF_CLIENT_ID: "custom1234",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
test_input["set_client_cert"] = set_client_cert
|
|
|
|
test_input["set_ca_cert"] = set_ca_cert
|
|
|
|
test_input["tls_insecure"] = tls_insecure
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input=test_input,
|
|
|
|
)
|
|
|
|
if test_error is not None:
|
|
|
|
assert result["errors"]["base"] == test_error
|
|
|
|
return
|
|
|
|
assert result["errors"] == {}
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 13:09:50 +00:00
|
|
|
("input_value", "error"),
|
2022-10-24 07:58:23 +00:00
|
|
|
[
|
|
|
|
("", True),
|
|
|
|
("-10", True),
|
|
|
|
("10", True),
|
|
|
|
("15", False),
|
|
|
|
("26", False),
|
|
|
|
("100", False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
async def test_keepalive_validation(
|
2023-02-08 17:08:03 +00:00
|
|
|
hass: HomeAssistant,
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection: MagicMock,
|
|
|
|
mock_reload_after_entry_update: MagicMock,
|
|
|
|
input_value: str,
|
|
|
|
error: bool,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2022-10-24 07:58:23 +00:00
|
|
|
"""Test validation of the keep alive option."""
|
|
|
|
|
|
|
|
test_input = {
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_KEEPALIVE: input_value,
|
|
|
|
}
|
|
|
|
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock = await mqtt_mock_entry()
|
2022-10-24 07:58:23 +00:00
|
|
|
mock_try_connection.return_value = True
|
|
|
|
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
|
|
|
|
# Add at least one advanced option to get the full form
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
mqtt.CONF_CLIENT_ID: "custom1234",
|
|
|
|
}
|
|
|
|
|
|
|
|
mqtt_mock.async_connect.reset_mock()
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
|
|
|
|
if error:
|
|
|
|
with pytest.raises(vol.MultipleInvalid):
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input=test_input,
|
|
|
|
)
|
|
|
|
return
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input=test_input,
|
|
|
|
)
|
|
|
|
assert not result["errors"]
|
|
|
|
|
|
|
|
|
2022-06-02 12:24:46 +00:00
|
|
|
async def test_disable_birth_will(
|
2023-02-08 17:08:03 +00:00
|
|
|
hass: HomeAssistant,
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection: MagicMock,
|
|
|
|
mock_reload_after_entry_update: MagicMock,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2020-07-24 10:29:19 +00:00
|
|
|
"""Test disabling birth and will."""
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock = await mqtt_mock_entry()
|
2020-07-24 10:29:19 +00:00
|
|
|
mock_try_connection.return_value = True
|
|
|
|
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
}
|
|
|
|
|
|
|
|
mqtt_mock.async_connect.reset_mock()
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
2022-07-07 16:57:36 +00:00
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
2020-07-24 10:29:19 +00:00
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "pass",
|
|
|
|
},
|
|
|
|
)
|
2022-07-07 16:57:36 +00:00
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
2020-07-24 10:29:19 +00:00
|
|
|
assert result["step_id"] == "options"
|
|
|
|
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert mqtt_mock.async_connect.call_count == 0
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
2022-10-24 07:58:23 +00:00
|
|
|
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
|
2020-07-24 10:29:19 +00:00
|
|
|
"birth_enable": False,
|
|
|
|
"birth_topic": "ha_state/online",
|
|
|
|
"birth_payload": "online",
|
|
|
|
"birth_qos": 1,
|
|
|
|
"birth_retain": True,
|
|
|
|
"will_enable": False,
|
|
|
|
"will_topic": "ha_state/offline",
|
|
|
|
"will_payload": "offline",
|
|
|
|
"will_qos": 2,
|
|
|
|
"will_retain": True,
|
|
|
|
},
|
|
|
|
)
|
2022-07-07 16:57:36 +00:00
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
2022-06-30 19:11:45 +00:00
|
|
|
assert result["data"] == {}
|
2020-07-24 10:29:19 +00:00
|
|
|
assert config_entry.data == {
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "pass",
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
2022-10-24 07:58:23 +00:00
|
|
|
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
|
2020-07-24 10:29:19 +00:00
|
|
|
mqtt.CONF_BIRTH_MESSAGE: {},
|
|
|
|
mqtt.CONF_WILL_MESSAGE: {},
|
|
|
|
}
|
|
|
|
|
|
|
|
await hass.async_block_till_done()
|
2022-09-27 06:05:28 +00:00
|
|
|
# assert that the entry was reloaded with the new config
|
|
|
|
assert mock_reload_after_entry_update.call_count == 1
|
2020-07-24 10:29:19 +00:00
|
|
|
|
|
|
|
|
2022-10-24 07:58:23 +00:00
|
|
|
async def test_invalid_discovery_prefix(
|
2023-02-08 17:08:03 +00:00
|
|
|
hass: HomeAssistant,
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection: MagicMock,
|
|
|
|
mock_reload_after_entry_update: MagicMock,
|
2023-02-09 14:22:30 +00:00
|
|
|
) -> None:
|
2022-10-24 07:58:23 +00:00
|
|
|
"""Test setting an invalid discovery prefix."""
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock = await mqtt_mock_entry()
|
2022-10-24 07:58:23 +00:00
|
|
|
mock_try_connection.return_value = True
|
|
|
|
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
|
|
|
|
}
|
|
|
|
|
|
|
|
mqtt_mock.async_connect.reset_mock()
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
|
|
assert result["step_id"] == "options"
|
|
|
|
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert mqtt_mock.async_connect.call_count == 0
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant#invalid",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
|
|
assert result["step_id"] == "options"
|
|
|
|
assert result["errors"]["base"] == "bad_discovery_prefix"
|
|
|
|
assert config_entry.data == {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant",
|
|
|
|
}
|
|
|
|
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
# assert that the entry was not reloaded with the new config
|
|
|
|
assert mock_reload_after_entry_update.call_count == 0
|
|
|
|
|
|
|
|
|
2023-02-09 15:47:28 +00:00
|
|
|
def get_default(schema: vol.Schema, key: str) -> Any:
|
2020-06-23 00:49:01 +00:00
|
|
|
"""Get default value for key in voluptuous schema."""
|
2023-02-15 11:39:12 +00:00
|
|
|
for schema_key in schema:
|
2023-02-09 15:47:28 +00:00
|
|
|
if schema_key == key:
|
|
|
|
if schema_key.default == vol.UNDEFINED:
|
2020-06-23 00:49:01 +00:00
|
|
|
return None
|
2023-02-09 15:47:28 +00:00
|
|
|
return schema_key.default()
|
2020-06-23 00:49:01 +00:00
|
|
|
|
|
|
|
|
2023-02-09 15:47:28 +00:00
|
|
|
def get_suggested(schema: vol.Schema, key: str) -> Any:
|
2020-06-23 00:49:01 +00:00
|
|
|
"""Get suggested value for key in voluptuous schema."""
|
2023-02-15 11:39:12 +00:00
|
|
|
for schema_key in schema:
|
2023-02-09 15:47:28 +00:00
|
|
|
if schema_key == key:
|
|
|
|
if (
|
|
|
|
schema_key.description is None
|
|
|
|
or "suggested_value" not in schema_key.description
|
|
|
|
):
|
2020-06-23 00:49:01 +00:00
|
|
|
return None
|
2023-02-09 15:47:28 +00:00
|
|
|
return schema_key.description["suggested_value"]
|
2020-06-23 00:49:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_option_flow_default_suggested_values(
|
2023-02-08 17:08:03 +00:00
|
|
|
hass: HomeAssistant,
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection_success: MqttMockPahoClient,
|
|
|
|
mock_reload_after_entry_update: MagicMock,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2020-06-23 00:49:01 +00:00
|
|
|
"""Test config flow options has default/suggested values."""
|
2023-04-12 07:43:03 +00:00
|
|
|
await mqtt_mock_entry()
|
2020-06-23 00:49:01 +00:00
|
|
|
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "pass",
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
mqtt.CONF_BIRTH_MESSAGE: {
|
|
|
|
mqtt.ATTR_TOPIC: "ha_state/online",
|
|
|
|
mqtt.ATTR_PAYLOAD: "online",
|
|
|
|
mqtt.ATTR_QOS: 1,
|
|
|
|
mqtt.ATTR_RETAIN: True,
|
|
|
|
},
|
|
|
|
mqtt.CONF_WILL_MESSAGE: {
|
|
|
|
mqtt.ATTR_TOPIC: "ha_state/offline",
|
|
|
|
mqtt.ATTR_PAYLOAD: "offline",
|
|
|
|
mqtt.ATTR_QOS: 2,
|
|
|
|
mqtt.ATTR_RETAIN: False,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
# Test default/suggested values from config
|
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
2022-07-07 16:57:36 +00:00
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
2020-06-23 00:49:01 +00:00
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
defaults = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
}
|
|
|
|
suggested = {
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "pass",
|
|
|
|
}
|
2023-02-09 15:47:28 +00:00
|
|
|
for key, value in defaults.items():
|
|
|
|
assert get_default(result["data_schema"].schema, key) == value
|
|
|
|
for key, value in suggested.items():
|
|
|
|
assert get_suggested(result["data_schema"].schema, key) == value
|
2020-06-23 00:49:01 +00:00
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_USERNAME: "us3r",
|
|
|
|
mqtt.CONF_PASSWORD: "p4ss",
|
|
|
|
},
|
|
|
|
)
|
2022-07-07 16:57:36 +00:00
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
2020-06-23 00:49:01 +00:00
|
|
|
assert result["step_id"] == "options"
|
|
|
|
defaults = {
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
"birth_qos": 1,
|
|
|
|
"birth_retain": True,
|
|
|
|
"will_qos": 2,
|
|
|
|
"will_retain": False,
|
|
|
|
}
|
|
|
|
suggested = {
|
|
|
|
"birth_topic": "ha_state/online",
|
|
|
|
"birth_payload": "online",
|
|
|
|
"will_topic": "ha_state/offline",
|
|
|
|
"will_payload": "offline",
|
|
|
|
}
|
2023-02-09 15:47:28 +00:00
|
|
|
for key, value in defaults.items():
|
|
|
|
assert get_default(result["data_schema"].schema, key) == value
|
|
|
|
for key, value in suggested.items():
|
|
|
|
assert get_suggested(result["data_schema"].schema, key) == value
|
2020-06-23 00:49:01 +00:00
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_DISCOVERY: False,
|
|
|
|
"birth_topic": "ha_state/onl1ne",
|
|
|
|
"birth_payload": "onl1ne",
|
|
|
|
"birth_qos": 2,
|
|
|
|
"birth_retain": False,
|
|
|
|
"will_topic": "ha_state/offl1ne",
|
|
|
|
"will_payload": "offl1ne",
|
|
|
|
"will_qos": 1,
|
|
|
|
"will_retain": True,
|
|
|
|
},
|
|
|
|
)
|
2022-07-07 16:57:36 +00:00
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
2020-06-23 00:49:01 +00:00
|
|
|
|
|
|
|
# Test updated default/suggested values from config
|
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
2022-07-07 16:57:36 +00:00
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
2020-06-23 00:49:01 +00:00
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
defaults = {
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
}
|
|
|
|
suggested = {
|
|
|
|
mqtt.CONF_USERNAME: "us3r",
|
|
|
|
mqtt.CONF_PASSWORD: "p4ss",
|
|
|
|
}
|
2023-02-09 15:47:28 +00:00
|
|
|
for key, value in defaults.items():
|
|
|
|
assert get_default(result["data_schema"].schema, key) == value
|
|
|
|
for key, value in suggested.items():
|
|
|
|
assert get_suggested(result["data_schema"].schema, key) == value
|
2020-06-23 00:49:01 +00:00
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={mqtt.CONF_BROKER: "another-broker", mqtt.CONF_PORT: 2345},
|
|
|
|
)
|
2022-07-07 16:57:36 +00:00
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
2020-06-23 00:49:01 +00:00
|
|
|
assert result["step_id"] == "options"
|
|
|
|
defaults = {
|
|
|
|
mqtt.CONF_DISCOVERY: False,
|
|
|
|
"birth_qos": 2,
|
|
|
|
"birth_retain": False,
|
|
|
|
"will_qos": 1,
|
|
|
|
"will_retain": True,
|
|
|
|
}
|
|
|
|
suggested = {
|
|
|
|
"birth_topic": "ha_state/onl1ne",
|
|
|
|
"birth_payload": "onl1ne",
|
|
|
|
"will_topic": "ha_state/offl1ne",
|
|
|
|
"will_payload": "offl1ne",
|
|
|
|
}
|
2023-02-09 15:47:28 +00:00
|
|
|
for key, value in defaults.items():
|
|
|
|
assert get_default(result["data_schema"].schema, key) == value
|
|
|
|
for key, value in suggested.items():
|
|
|
|
assert get_suggested(result["data_schema"].schema, key) == value
|
2020-06-23 00:49:01 +00:00
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
"birth_topic": "ha_state/onl1ne",
|
|
|
|
"birth_payload": "onl1ne",
|
|
|
|
"birth_qos": 2,
|
|
|
|
"birth_retain": False,
|
|
|
|
"will_topic": "ha_state/offl1ne",
|
|
|
|
"will_payload": "offl1ne",
|
|
|
|
"will_qos": 1,
|
|
|
|
"will_retain": True,
|
|
|
|
},
|
|
|
|
)
|
2022-07-07 16:57:36 +00:00
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
2020-06-23 00:49:01 +00:00
|
|
|
|
2021-10-05 09:14:53 +00:00
|
|
|
# Make sure all MQTT related jobs are done before ending the test
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2020-06-23 00:49:01 +00:00
|
|
|
|
2022-10-24 07:58:23 +00:00
|
|
|
@pytest.mark.parametrize(
|
2023-02-15 13:09:50 +00:00
|
|
|
("advanced_options", "step_id"), [(False, "options"), (True, "broker")]
|
2022-10-24 07:58:23 +00:00
|
|
|
)
|
|
|
|
async def test_skipping_advanced_options(
|
2023-02-08 17:08:03 +00:00
|
|
|
hass: HomeAssistant,
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection: MagicMock,
|
|
|
|
mock_reload_after_entry_update: MagicMock,
|
|
|
|
advanced_options: bool,
|
|
|
|
step_id: str,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2022-10-24 07:58:23 +00:00
|
|
|
"""Test advanced options option."""
|
|
|
|
|
|
|
|
test_input = {
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
"advanced_options": advanced_options,
|
|
|
|
}
|
|
|
|
|
2023-04-12 07:43:03 +00:00
|
|
|
mqtt_mock = await mqtt_mock_entry()
|
2022-10-24 07:58:23 +00:00
|
|
|
mock_try_connection.return_value = True
|
|
|
|
config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
|
|
|
|
# Initiate with a basic setup
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
}
|
|
|
|
|
|
|
|
mqtt_mock.async_connect.reset_mock()
|
|
|
|
|
2023-04-25 20:23:17 +00:00
|
|
|
result = await hass.config_entries.options.async_init(
|
|
|
|
config_entry.entry_id, context={"show_advanced_options": True}
|
|
|
|
)
|
2022-10-24 07:58:23 +00:00
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input=test_input,
|
|
|
|
)
|
|
|
|
assert result["step_id"] == step_id
|
|
|
|
|
|
|
|
|
2023-02-08 17:08:03 +00:00
|
|
|
async def test_options_user_connection_fails(
|
2023-02-09 15:47:28 +00:00
|
|
|
hass: HomeAssistant, mock_try_connection_time_out: MagicMock
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2020-06-23 00:49:01 +00:00
|
|
|
"""Test if connection cannot be made."""
|
|
|
|
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
|
|
|
config_entry.add_to_hass(hass)
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
}
|
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
|
2022-02-23 11:29:32 +00:00
|
|
|
mock_try_connection_time_out.reset_mock()
|
2020-06-23 00:49:01 +00:00
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={mqtt.CONF_BROKER: "bad-broker", mqtt.CONF_PORT: 2345},
|
|
|
|
)
|
|
|
|
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["errors"]["base"] == "cannot_connect"
|
|
|
|
|
|
|
|
# Check we tried the connection
|
2022-02-23 11:29:32 +00:00
|
|
|
assert len(mock_try_connection_time_out.mock_calls)
|
2020-06-23 00:49:01 +00:00
|
|
|
# Check config entry did not update
|
|
|
|
assert config_entry.data == {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-02-08 17:08:03 +00:00
|
|
|
async def test_options_bad_birth_message_fails(
|
2023-02-09 15:47:28 +00:00
|
|
|
hass: HomeAssistant, mock_try_connection: MqttMockPahoClient
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2020-06-23 00:49:01 +00:00
|
|
|
"""Test bad birth message."""
|
|
|
|
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
|
|
|
config_entry.add_to_hass(hass)
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
}
|
|
|
|
|
|
|
|
mock_try_connection.return_value = True
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={mqtt.CONF_BROKER: "another-broker", mqtt.CONF_PORT: 2345},
|
|
|
|
)
|
|
|
|
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "options"
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
2020-08-27 11:56:20 +00:00
|
|
|
result["flow_id"],
|
|
|
|
user_input={"birth_topic": "ha_state/online/#"},
|
2020-06-23 00:49:01 +00:00
|
|
|
)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["errors"]["base"] == "bad_birth"
|
|
|
|
|
|
|
|
# Check config entry did not update
|
|
|
|
assert config_entry.data == {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-02-08 17:08:03 +00:00
|
|
|
async def test_options_bad_will_message_fails(
|
2023-02-09 15:47:28 +00:00
|
|
|
hass: HomeAssistant, mock_try_connection: MagicMock
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2020-06-23 00:49:01 +00:00
|
|
|
"""Test bad will message."""
|
|
|
|
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
|
|
|
config_entry.add_to_hass(hass)
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
}
|
|
|
|
|
|
|
|
mock_try_connection.return_value = True
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={mqtt.CONF_BROKER: "another-broker", mqtt.CONF_PORT: 2345},
|
|
|
|
)
|
|
|
|
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "options"
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
2020-08-27 11:56:20 +00:00
|
|
|
result["flow_id"],
|
|
|
|
user_input={"will_topic": "ha_state/offline/#"},
|
2020-06-23 00:49:01 +00:00
|
|
|
)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["errors"]["base"] == "bad_will"
|
|
|
|
|
|
|
|
# Check config entry did not update
|
|
|
|
assert config_entry.data == {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
}
|
2022-03-03 20:40:15 +00:00
|
|
|
|
|
|
|
|
2023-03-28 07:37:07 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"hass_config", [{"mqtt": {"sensor": [{"state_topic": "some-topic"}]}}]
|
|
|
|
)
|
2022-03-03 20:40:15 +00:00
|
|
|
async def test_try_connection_with_advanced_parameters(
|
2023-02-08 17:08:03 +00:00
|
|
|
hass: HomeAssistant,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection_success: MqttMockPahoClient,
|
2023-02-09 08:48:22 +00:00
|
|
|
tmp_path: Path,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_ssl_context: dict[str, MagicMock],
|
|
|
|
mock_process_uploaded_file: MagicMock,
|
2023-02-09 14:22:30 +00:00
|
|
|
) -> None:
|
2022-03-03 20:40:15 +00:00
|
|
|
"""Test config flow with advanced parameters from config."""
|
2022-10-24 07:58:23 +00:00
|
|
|
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
|
|
|
config_entry.add_to_hass(hass)
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "pass",
|
2022-11-23 14:03:31 +00:00
|
|
|
mqtt.CONF_TRANSPORT: "websockets",
|
2023-03-28 07:37:07 +00:00
|
|
|
mqtt.CONF_CERTIFICATE: "auto",
|
|
|
|
mqtt.CONF_TLS_INSECURE: True,
|
|
|
|
mqtt.CONF_CLIENT_CERT: MOCK_CLIENT_CERT.decode(encoding="utf-8)"),
|
|
|
|
mqtt.CONF_CLIENT_KEY: MOCK_CLIENT_KEY.decode(encoding="utf-8"),
|
2022-11-23 14:03:31 +00:00
|
|
|
mqtt.CONF_WS_PATH: "/path/",
|
|
|
|
mqtt.CONF_WS_HEADERS: {"h1": "v1", "h2": "v2"},
|
2022-10-24 07:58:23 +00:00
|
|
|
mqtt.CONF_KEEPALIVE: 30,
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
mqtt.CONF_BIRTH_MESSAGE: {
|
|
|
|
mqtt.ATTR_TOPIC: "ha_state/online",
|
|
|
|
mqtt.ATTR_PAYLOAD: "online",
|
|
|
|
mqtt.ATTR_QOS: 1,
|
|
|
|
mqtt.ATTR_RETAIN: True,
|
|
|
|
},
|
|
|
|
mqtt.CONF_WILL_MESSAGE: {
|
|
|
|
mqtt.ATTR_TOPIC: "ha_state/offline",
|
|
|
|
mqtt.ATTR_PAYLOAD: "offline",
|
|
|
|
mqtt.ATTR_QOS: 2,
|
|
|
|
mqtt.ATTR_RETAIN: False,
|
|
|
|
},
|
|
|
|
}
|
2023-03-28 07:37:07 +00:00
|
|
|
# Test default/suggested values from config
|
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
defaults = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
"set_client_cert": True,
|
|
|
|
"set_ca_cert": "auto",
|
|
|
|
}
|
|
|
|
suggested = {
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "pass",
|
|
|
|
mqtt.CONF_TLS_INSECURE: True,
|
|
|
|
mqtt.CONF_PROTOCOL: "3.1.1",
|
|
|
|
mqtt.CONF_TRANSPORT: "websockets",
|
|
|
|
mqtt.CONF_WS_PATH: "/path/",
|
|
|
|
mqtt.CONF_WS_HEADERS: '{"h1":"v1","h2":"v2"}',
|
|
|
|
}
|
|
|
|
for k, v in defaults.items():
|
|
|
|
assert get_default(result["data_schema"].schema, k) == v
|
|
|
|
for k, v in suggested.items():
|
|
|
|
assert get_suggested(result["data_schema"].schema, k) == v
|
2022-10-24 07:58:23 +00:00
|
|
|
|
2023-03-28 07:37:07 +00:00
|
|
|
# test we can change username and password
|
|
|
|
# as it was configured as auto in configuration.yaml is is migrated now
|
|
|
|
mock_try_connection_success.reset_mock()
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "another-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_USERNAME: "us3r",
|
|
|
|
mqtt.CONF_PASSWORD: "p4ss",
|
2022-10-24 07:58:23 +00:00
|
|
|
"set_ca_cert": "auto",
|
2023-03-28 07:37:07 +00:00
|
|
|
"set_client_cert": True,
|
2022-10-24 07:58:23 +00:00
|
|
|
mqtt.CONF_TLS_INSECURE: True,
|
2022-11-23 14:03:31 +00:00
|
|
|
mqtt.CONF_TRANSPORT: "websockets",
|
2023-03-28 07:37:07 +00:00
|
|
|
mqtt.CONF_WS_PATH: "/new/path",
|
|
|
|
mqtt.CONF_WS_HEADERS: '{"h3": "v3"}',
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
|
|
|
assert result["errors"] == {}
|
|
|
|
assert result["step_id"] == "options"
|
|
|
|
await hass.async_block_till_done()
|
2022-11-23 14:03:31 +00:00
|
|
|
|
2023-03-28 07:37:07 +00:00
|
|
|
# check if the username and password was set from config flow and not from configuration.yaml
|
|
|
|
assert mock_try_connection_success.username_pw_set.mock_calls[0][1] == (
|
|
|
|
"us3r",
|
|
|
|
"p4ss",
|
|
|
|
)
|
|
|
|
# check if tls_insecure_set is called
|
|
|
|
assert mock_try_connection_success.tls_insecure_set.mock_calls[0][1] == (True,)
|
|
|
|
|
|
|
|
# check if the ca certificate settings were not set during connection test
|
|
|
|
assert mock_try_connection_success.tls_set.mock_calls[0].kwargs[
|
|
|
|
"certfile"
|
|
|
|
] == mqtt.util.get_file_path(mqtt.CONF_CLIENT_CERT)
|
|
|
|
assert mock_try_connection_success.tls_set.mock_calls[0].kwargs[
|
|
|
|
"keyfile"
|
|
|
|
] == mqtt.util.get_file_path(mqtt.CONF_CLIENT_KEY)
|
|
|
|
|
|
|
|
# check if websockets options are set
|
|
|
|
assert mock_try_connection_success.ws_set_options.mock_calls[0][1] == (
|
|
|
|
"/new/path",
|
|
|
|
{"h3": "v3"},
|
|
|
|
)
|
|
|
|
# Accept default option
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={},
|
|
|
|
)
|
|
|
|
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
|
|
|
await hass.async_block_till_done()
|
2022-10-24 07:58:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_setup_with_advanced_settings(
|
2023-02-08 17:08:03 +00:00
|
|
|
hass: HomeAssistant,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_try_connection: MagicMock,
|
|
|
|
mock_ssl_context: dict[str, MagicMock],
|
|
|
|
mock_process_uploaded_file: MagicMock,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2022-10-24 07:58:23 +00:00
|
|
|
"""Test config flow setup with advanced parameters."""
|
|
|
|
file_id = mock_process_uploaded_file.file_id
|
|
|
|
|
|
|
|
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
|
|
|
config_entry.add_to_hass(hass)
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
}
|
|
|
|
|
|
|
|
mock_try_connection.return_value = True
|
|
|
|
|
2023-04-25 20:23:17 +00:00
|
|
|
result = await hass.config_entries.options.async_init(
|
|
|
|
config_entry.entry_id, context={"show_advanced_options": True}
|
|
|
|
)
|
2022-10-24 07:58:23 +00:00
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
assert result["data_schema"].schema["advanced_options"]
|
|
|
|
|
|
|
|
# first iteration, basic settings
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "secret",
|
|
|
|
"advanced_options": True,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
assert "advanced_options" not in result["data_schema"].schema
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_CLIENT_ID]
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_KEEPALIVE]
|
|
|
|
assert result["data_schema"].schema["set_client_cert"]
|
|
|
|
assert result["data_schema"].schema["set_ca_cert"]
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_TLS_INSECURE]
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_PROTOCOL]
|
2022-11-23 14:03:31 +00:00
|
|
|
assert result["data_schema"].schema[mqtt.CONF_TRANSPORT]
|
2022-10-24 07:58:23 +00:00
|
|
|
assert mqtt.CONF_CLIENT_CERT not in result["data_schema"].schema
|
|
|
|
assert mqtt.CONF_CLIENT_KEY not in result["data_schema"].schema
|
|
|
|
|
|
|
|
# second iteration, advanced settings with request for client cert
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "secret",
|
|
|
|
mqtt.CONF_KEEPALIVE: 30,
|
|
|
|
"set_ca_cert": "auto",
|
|
|
|
"set_client_cert": True,
|
|
|
|
mqtt.CONF_TLS_INSECURE: True,
|
2022-11-23 14:03:31 +00:00
|
|
|
mqtt.CONF_PROTOCOL: "3.1.1",
|
|
|
|
mqtt.CONF_TRANSPORT: "websockets",
|
2022-10-24 07:58:23 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
assert "advanced_options" not in result["data_schema"].schema
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_CLIENT_ID]
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_KEEPALIVE]
|
|
|
|
assert result["data_schema"].schema["set_client_cert"]
|
|
|
|
assert result["data_schema"].schema["set_ca_cert"]
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_TLS_INSECURE]
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_PROTOCOL]
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_CLIENT_CERT]
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_CLIENT_KEY]
|
2022-11-23 14:03:31 +00:00
|
|
|
assert result["data_schema"].schema[mqtt.CONF_TRANSPORT]
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_WS_PATH]
|
|
|
|
assert result["data_schema"].schema[mqtt.CONF_WS_HEADERS]
|
2022-10-24 07:58:23 +00:00
|
|
|
|
2022-11-23 14:03:31 +00:00
|
|
|
# third iteration, advanced settings with client cert and key set and bad json payload
|
2022-10-24 07:58:23 +00:00
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "secret",
|
|
|
|
mqtt.CONF_KEEPALIVE: 30,
|
|
|
|
"set_ca_cert": "auto",
|
|
|
|
"set_client_cert": True,
|
|
|
|
mqtt.CONF_CLIENT_CERT: file_id[mqtt.CONF_CLIENT_CERT],
|
|
|
|
mqtt.CONF_CLIENT_KEY: file_id[mqtt.CONF_CLIENT_KEY],
|
|
|
|
mqtt.CONF_TLS_INSECURE: True,
|
2022-11-23 14:03:31 +00:00
|
|
|
mqtt.CONF_TRANSPORT: "websockets",
|
|
|
|
mqtt.CONF_WS_PATH: "/custom_path/",
|
|
|
|
mqtt.CONF_WS_HEADERS: '{"header_1": "content_header_1", "header_2": "content_header_2"',
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
assert result["errors"]["base"] == "bad_ws_headers"
|
|
|
|
|
|
|
|
# fourth iteration, advanced settings with client cert and key set
|
|
|
|
# and correct json payload for ws_headers
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "secret",
|
|
|
|
mqtt.CONF_KEEPALIVE: 30,
|
|
|
|
"set_ca_cert": "auto",
|
|
|
|
"set_client_cert": True,
|
|
|
|
mqtt.CONF_CLIENT_CERT: file_id[mqtt.CONF_CLIENT_CERT],
|
|
|
|
mqtt.CONF_CLIENT_KEY: file_id[mqtt.CONF_CLIENT_KEY],
|
|
|
|
mqtt.CONF_TLS_INSECURE: True,
|
|
|
|
mqtt.CONF_TRANSPORT: "websockets",
|
|
|
|
mqtt.CONF_WS_PATH: "/custom_path/",
|
|
|
|
mqtt.CONF_WS_HEADERS: '{"header_1": "content_header_1", "header_2": "content_header_2"}',
|
2022-10-24 07:58:23 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "options"
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert result["type"] == "create_entry"
|
2022-11-23 14:03:31 +00:00
|
|
|
|
|
|
|
# Check config entry result
|
|
|
|
assert config_entry.data == {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 2345,
|
|
|
|
mqtt.CONF_USERNAME: "user",
|
|
|
|
mqtt.CONF_PASSWORD: "secret",
|
|
|
|
mqtt.CONF_KEEPALIVE: 30,
|
|
|
|
mqtt.CONF_CLIENT_CERT: "## mock client certificate file ##",
|
|
|
|
mqtt.CONF_CLIENT_KEY: "## mock key file ##",
|
|
|
|
"tls_insecure": True,
|
|
|
|
mqtt.CONF_TRANSPORT: "websockets",
|
|
|
|
mqtt.CONF_WS_PATH: "/custom_path/",
|
|
|
|
mqtt.CONF_WS_HEADERS: {
|
|
|
|
"header_1": "content_header_1",
|
|
|
|
"header_2": "content_header_2",
|
|
|
|
},
|
|
|
|
mqtt.CONF_CERTIFICATE: "auto",
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test",
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async def test_change_websockets_transport_to_tcp(
|
2023-02-08 17:08:03 +00:00
|
|
|
hass: HomeAssistant,
|
|
|
|
mock_try_connection,
|
2023-02-09 15:47:28 +00:00
|
|
|
mock_ssl_context: dict[str, MagicMock],
|
|
|
|
mock_process_uploaded_file: MagicMock,
|
2023-02-08 17:08:03 +00:00
|
|
|
) -> None:
|
2022-11-23 14:03:31 +00:00
|
|
|
"""Test option flow setup with websockets transport settings."""
|
|
|
|
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
|
|
|
config_entry.add_to_hass(hass)
|
|
|
|
config_entry.data = {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
mqtt.CONF_TRANSPORT: "websockets",
|
|
|
|
mqtt.CONF_WS_HEADERS: {"header_1": "custom_header1"},
|
|
|
|
mqtt.CONF_WS_PATH: "/some_path",
|
|
|
|
}
|
|
|
|
|
|
|
|
mock_try_connection.return_value = True
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "broker"
|
|
|
|
assert result["data_schema"].schema["transport"]
|
|
|
|
assert result["data_schema"].schema["ws_path"]
|
|
|
|
assert result["data_schema"].schema["ws_headers"]
|
|
|
|
|
|
|
|
# Change transport to tcp
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
mqtt.CONF_TRANSPORT: "tcp",
|
|
|
|
mqtt.CONF_WS_HEADERS: '{"header_1": "custom_header1"}',
|
|
|
|
mqtt.CONF_WS_PATH: "/some_path",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert result["type"] == "form"
|
|
|
|
assert result["step_id"] == "options"
|
|
|
|
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
|
|
result["flow_id"],
|
|
|
|
user_input={
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert result["type"] == "create_entry"
|
|
|
|
|
|
|
|
# Check config entry result
|
|
|
|
assert config_entry.data == {
|
|
|
|
mqtt.CONF_BROKER: "test-broker",
|
|
|
|
mqtt.CONF_PORT: 1234,
|
|
|
|
mqtt.CONF_TRANSPORT: "tcp",
|
|
|
|
mqtt.CONF_DISCOVERY: True,
|
|
|
|
mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test",
|
|
|
|
}
|