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:
Quentame 2019-11-11 21:30:00 +01:00 committed by Martin Hjelmare
parent cfcacc2827
commit decab3e15b
5 changed files with 134 additions and 26 deletions

View file

@ -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"

View file

@ -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(

View file

@ -0,0 +1,3 @@
"""Constants for OwnTracks."""
DOMAIN = "owntracks"

View file

@ -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

View file

@ -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"]