Add config flow tests for OwnTracks (#28644)
* Add config flow tests for OwnTracks * Fix pylint * Woops, uncomment test * Woops again, logs removed * Review from @MartinHjelmare + fix pylint
This commit is contained in:
parent
cfcacc2827
commit
decab3e15b
5 changed files with 134 additions and 26 deletions
|
@ -14,12 +14,12 @@ from homeassistant.core import callback
|
|||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.setup import async_when_setup
|
||||
|
||||
from .const import DOMAIN
|
||||
from .config_flow import CONF_SECRET
|
||||
from .messages import async_handle_message
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = "owntracks"
|
||||
CONF_MAX_GPS_ACCURACY = "max_gps_accuracy"
|
||||
CONF_WAYPOINT_IMPORT = "waypoints"
|
||||
CONF_WAYPOINT_WHITELIST = "waypoint_whitelist"
|
||||
|
|
|
@ -3,6 +3,8 @@ from homeassistant import config_entries
|
|||
from homeassistant.const import CONF_WEBHOOK_ID
|
||||
from homeassistant.auth.util import generate_secret
|
||||
|
||||
from .const import DOMAIN # noqa pylint: disable=unused-import
|
||||
|
||||
CONF_SECRET = "secret"
|
||||
CONF_CLOUDHOOK = "cloudhook"
|
||||
|
||||
|
@ -17,8 +19,7 @@ def supports_encryption():
|
|||
return False
|
||||
|
||||
|
||||
@config_entries.HANDLERS.register("owntracks")
|
||||
class OwnTracksFlow(config_entries.ConfigFlow):
|
||||
class OwnTracksFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Set up OwnTracks."""
|
||||
|
||||
VERSION = 1
|
||||
|
@ -36,14 +37,9 @@ class OwnTracksFlow(config_entries.ConfigFlow):
|
|||
secret = generate_secret(16)
|
||||
|
||||
if supports_encryption():
|
||||
secret_desc = (
|
||||
"The encryption key is {} "
|
||||
"(on Android under preferences -> advanced)".format(secret)
|
||||
)
|
||||
secret_desc = f"The encryption key is {secret} (on Android under preferences -> advanced)"
|
||||
else:
|
||||
secret_desc = (
|
||||
"Encryption is not supported because libsodium is not " "installed."
|
||||
)
|
||||
secret_desc = "Encryption is not supported because nacl is not installed."
|
||||
|
||||
return self.async_create_entry(
|
||||
title="OwnTracks",
|
||||
|
@ -55,8 +51,7 @@ class OwnTracksFlow(config_entries.ConfigFlow):
|
|||
description_placeholders={
|
||||
"secret": secret_desc,
|
||||
"webhook_url": webhook_url,
|
||||
"android_url": "https://play.google.com/store/apps/details?"
|
||||
"id=org.owntracks.android",
|
||||
"android_url": "https://play.google.com/store/apps/details?id=org.owntracks.android",
|
||||
"ios_url": "https://itunes.apple.com/us/app/owntracks/id692424691?mt=8",
|
||||
"docs_url": "https://www.home-assistant.io/integrations/owntracks/",
|
||||
},
|
||||
|
@ -64,6 +59,8 @@ class OwnTracksFlow(config_entries.ConfigFlow):
|
|||
|
||||
async def async_step_import(self, user_input):
|
||||
"""Import a config flow from configuration."""
|
||||
if self._async_current_entries():
|
||||
return self.async_abort(reason="one_instance_allowed")
|
||||
webhook_id, _webhook_url, cloudhook = await self._get_webhook_id()
|
||||
secret = generate_secret(16)
|
||||
return self.async_create_entry(
|
||||
|
|
3
homeassistant/components/owntracks/const.py
Normal file
3
homeassistant/components/owntracks/const.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""Constants for OwnTracks."""
|
||||
|
||||
DOMAIN = "owntracks"
|
|
@ -107,7 +107,7 @@ def _decrypt_payload(secret, topic, ciphertext):
|
|||
try:
|
||||
keylen, decrypt = get_cipher()
|
||||
except OSError:
|
||||
_LOGGER.warning("Ignoring encrypted payload because libsodium not installed")
|
||||
_LOGGER.warning("Ignoring encrypted payload because nacl not installed")
|
||||
return None
|
||||
|
||||
if isinstance(secret, dict):
|
||||
|
@ -117,8 +117,7 @@ def _decrypt_payload(secret, topic, ciphertext):
|
|||
|
||||
if key is None:
|
||||
_LOGGER.warning(
|
||||
"Ignoring encrypted payload because no decryption key known "
|
||||
"for topic %s",
|
||||
"Ignoring encrypted payload because no decryption key known for topic %s",
|
||||
topic,
|
||||
)
|
||||
return None
|
||||
|
@ -134,8 +133,7 @@ def _decrypt_payload(secret, topic, ciphertext):
|
|||
return message
|
||||
except ValueError:
|
||||
_LOGGER.warning(
|
||||
"Ignoring encrypted payload because unable to decrypt using "
|
||||
"key for topic %s",
|
||||
"Ignoring encrypted payload because unable to decrypt using key for topic %s",
|
||||
topic,
|
||||
)
|
||||
return None
|
||||
|
|
|
@ -1,25 +1,135 @@
|
|||
"""Tests for OwnTracks config flow."""
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import Mock, patch
|
||||
import pytest
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.const import CONF_WEBHOOK_ID
|
||||
from homeassistant.components.owntracks import config_flow
|
||||
from homeassistant.components.owntracks.config_flow import CONF_CLOUDHOOK, CONF_SECRET
|
||||
from homeassistant.components.owntracks.const import DOMAIN
|
||||
from homeassistant.setup import async_setup_component
|
||||
from tests.common import mock_coro
|
||||
|
||||
from tests.common import mock_coro, MockConfigEntry
|
||||
|
||||
|
||||
async def test_config_flow_import(hass):
|
||||
CONF_WEBHOOK_URL = "webhook_url"
|
||||
|
||||
BASE_URL = "http://example.com"
|
||||
CLOUDHOOK = False
|
||||
SECRET = "secret"
|
||||
WEBHOOK_ID = "webhook_id"
|
||||
WEBHOOK_URL = f"{BASE_URL}/api/webhook/webhook_id"
|
||||
|
||||
|
||||
@pytest.fixture(name="webhook_id")
|
||||
def mock_webhook_id():
|
||||
"""Mock webhook_id."""
|
||||
with patch(
|
||||
"homeassistant.components.webhook.async_generate_id", return_value=WEBHOOK_ID
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="secret")
|
||||
def mock_secret():
|
||||
"""Mock secret."""
|
||||
with patch("binascii.hexlify", return_value=str.encode(SECRET)):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="not_supports_encryption")
|
||||
def mock_not_supports_encryption():
|
||||
"""Mock non successful nacl import."""
|
||||
with patch(
|
||||
"homeassistant.components.owntracks.config_flow.supports_encryption",
|
||||
return_value=False,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
def init_config_flow(hass):
|
||||
"""Init a configuration flow."""
|
||||
hass.config.api = Mock(base_url=BASE_URL)
|
||||
flow = config_flow.OwnTracksFlow()
|
||||
flow.hass = hass
|
||||
return flow
|
||||
|
||||
|
||||
async def test_user(hass, webhook_id, secret):
|
||||
"""Test user step."""
|
||||
flow = init_config_flow(hass)
|
||||
|
||||
result = await flow.async_step_user()
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await flow.async_step_user({})
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "OwnTracks"
|
||||
assert result["data"][CONF_WEBHOOK_ID] == WEBHOOK_ID
|
||||
assert result["data"][CONF_SECRET] == SECRET
|
||||
assert result["data"][CONF_CLOUDHOOK] == CLOUDHOOK
|
||||
assert result["description_placeholders"][CONF_WEBHOOK_URL] == WEBHOOK_URL
|
||||
|
||||
|
||||
async def test_import(hass, webhook_id, secret):
|
||||
"""Test import step."""
|
||||
flow = init_config_flow(hass)
|
||||
|
||||
result = await flow.async_step_import({})
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "OwnTracks"
|
||||
assert result["data"][CONF_WEBHOOK_ID] == WEBHOOK_ID
|
||||
assert result["data"][CONF_SECRET] == SECRET
|
||||
assert result["data"][CONF_CLOUDHOOK] == CLOUDHOOK
|
||||
assert result["description_placeholders"] is None
|
||||
|
||||
|
||||
async def test_import_setup(hass):
|
||||
"""Test that we automatically create a config flow."""
|
||||
assert not hass.config_entries.async_entries("owntracks")
|
||||
assert await async_setup_component(hass, "owntracks", {"owntracks": {}})
|
||||
assert not hass.config_entries.async_entries(DOMAIN)
|
||||
assert await async_setup_component(hass, DOMAIN, {"owntracks": {}})
|
||||
await hass.async_block_till_done()
|
||||
assert hass.config_entries.async_entries("owntracks")
|
||||
assert hass.config_entries.async_entries(DOMAIN)
|
||||
|
||||
|
||||
async def test_config_flow_unload(hass):
|
||||
async def test_abort_if_already_setup(hass):
|
||||
"""Test that we can't add more than one instance."""
|
||||
flow = init_config_flow(hass)
|
||||
|
||||
MockConfigEntry(domain=DOMAIN, data={}).add_to_hass(hass)
|
||||
assert hass.config_entries.async_entries(DOMAIN)
|
||||
|
||||
# Should fail, already setup (import)
|
||||
result = await flow.async_step_import({})
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "one_instance_allowed"
|
||||
|
||||
# Should fail, already setup (flow)
|
||||
result = await flow.async_step_user({})
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "one_instance_allowed"
|
||||
|
||||
|
||||
async def test_user_not_supports_encryption(hass, not_supports_encryption):
|
||||
"""Test user step."""
|
||||
flow = init_config_flow(hass)
|
||||
|
||||
result = await flow.async_step_user({})
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert (
|
||||
result["description_placeholders"]["secret"]
|
||||
== "Encryption is not supported because nacl is not installed."
|
||||
)
|
||||
|
||||
|
||||
async def test_unload(hass):
|
||||
"""Test unloading a config flow."""
|
||||
with patch(
|
||||
"homeassistant.config_entries.ConfigEntries" ".async_forward_entry_setup"
|
||||
) as mock_forward:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"owntracks", context={"source": "import"}, data={}
|
||||
DOMAIN, context={"source": "import"}, data={}
|
||||
)
|
||||
|
||||
assert len(mock_forward.mock_calls) == 1
|
||||
|
@ -51,7 +161,7 @@ async def test_with_cloud_sub(hass):
|
|||
return_value=mock_coro("https://hooks.nabu.casa/ABCD"),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"owntracks", context={"source": "user"}, data={}
|
||||
DOMAIN, context={"source": "user"}, data={}
|
||||
)
|
||||
|
||||
entry = result["result"]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue