core/tests/components/hue/test_config_flow.py

399 lines
12 KiB
Python
Raw Normal View History

"""Tests for Philips Hue config flow."""
import asyncio
from unittest.mock import Mock, patch
import aiohue
import pytest
import voluptuous as vol
from homeassistant.components.hue import config_flow, const, errors
from tests.common import MockConfigEntry, mock_coro
async def test_flow_works(hass, aioclient_mock):
"""Test config flow ."""
2019-07-31 19:25:30 +00:00
aioclient_mock.get(
const.API_NUPNP, json=[{"internalipaddress": "1.2.3.4", "id": "bla"}]
)
flow = config_flow.HueFlowHandler()
flow.hass = hass
await flow.async_step_init()
2019-07-31 19:25:30 +00:00
with patch("aiohue.Bridge") as mock_bridge:
def mock_constructor(host, websession, username=None):
"""Fake the bridge constructor."""
mock_bridge.host = host
return mock_bridge
mock_bridge.side_effect = mock_constructor
2019-07-31 19:25:30 +00:00
mock_bridge.username = "username-abc"
mock_bridge.config.name = "Mock Bridge"
mock_bridge.config.bridgeid = "bridge-id-1234"
mock_bridge.create_user.return_value = mock_coro()
mock_bridge.initialize.return_value = mock_coro()
result = await flow.async_step_link(user_input={})
2019-07-31 19:25:30 +00:00
assert mock_bridge.host == "1.2.3.4"
assert len(mock_bridge.create_user.mock_calls) == 1
assert len(mock_bridge.initialize.mock_calls) == 1
2019-07-31 19:25:30 +00:00
assert result["type"] == "create_entry"
assert result["title"] == "Mock Bridge"
assert result["data"] == {
"host": "1.2.3.4",
"bridge_id": "bridge-id-1234",
"username": "username-abc",
}
async def test_flow_no_discovered_bridges(hass, aioclient_mock):
"""Test config flow discovers no bridges."""
aioclient_mock.get(const.API_NUPNP, json=[])
flow = config_flow.HueFlowHandler()
flow.hass = hass
result = await flow.async_step_init()
2019-07-31 19:25:30 +00:00
assert result["type"] == "abort"
async def test_flow_all_discovered_bridges_exist(hass, aioclient_mock):
"""Test config flow discovers only already configured bridges."""
2019-07-31 19:25:30 +00:00
aioclient_mock.get(
const.API_NUPNP, json=[{"internalipaddress": "1.2.3.4", "id": "bla"}]
)
MockConfigEntry(domain="hue", data={"host": "1.2.3.4"}).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
result = await flow.async_step_init()
2019-07-31 19:25:30 +00:00
assert result["type"] == "abort"
async def test_flow_one_bridge_discovered(hass, aioclient_mock):
"""Test config flow discovers one bridge."""
2019-07-31 19:25:30 +00:00
aioclient_mock.get(
const.API_NUPNP, json=[{"internalipaddress": "1.2.3.4", "id": "bla"}]
)
flow = config_flow.HueFlowHandler()
flow.hass = hass
result = await flow.async_step_init()
2019-07-31 19:25:30 +00:00
assert result["type"] == "form"
assert result["step_id"] == "link"
async def test_flow_two_bridges_discovered(hass, aioclient_mock):
"""Test config flow discovers two bridges."""
2019-07-31 19:25:30 +00:00
aioclient_mock.get(
const.API_NUPNP,
json=[
{"internalipaddress": "1.2.3.4", "id": "bla"},
{"internalipaddress": "5.6.7.8", "id": "beer"},
],
)
flow = config_flow.HueFlowHandler()
flow.hass = hass
result = await flow.async_step_init()
2019-07-31 19:25:30 +00:00
assert result["type"] == "form"
assert result["step_id"] == "init"
with pytest.raises(vol.Invalid):
2019-07-31 19:25:30 +00:00
assert result["data_schema"]({"host": "0.0.0.0"})
2019-07-31 19:25:30 +00:00
result["data_schema"]({"host": "1.2.3.4"})
result["data_schema"]({"host": "5.6.7.8"})
async def test_flow_two_bridges_discovered_one_new(hass, aioclient_mock):
"""Test config flow discovers two bridges."""
2019-07-31 19:25:30 +00:00
aioclient_mock.get(
const.API_NUPNP,
json=[
{"internalipaddress": "1.2.3.4", "id": "bla"},
{"internalipaddress": "5.6.7.8", "id": "beer"},
],
)
MockConfigEntry(domain="hue", data={"host": "1.2.3.4"}).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
result = await flow.async_step_init()
2019-07-31 19:25:30 +00:00
assert result["type"] == "form"
assert result["step_id"] == "link"
assert flow.host == "5.6.7.8"
async def test_flow_timeout_discovery(hass):
"""Test config flow ."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
2019-07-31 19:25:30 +00:00
with patch(
"homeassistant.components.hue.config_flow.discover_nupnp",
side_effect=asyncio.TimeoutError,
):
result = await flow.async_step_init()
2019-07-31 19:25:30 +00:00
assert result["type"] == "abort"
async def test_flow_link_timeout(hass):
"""Test config flow ."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
2019-07-31 19:25:30 +00:00
with patch("aiohue.Bridge.create_user", side_effect=asyncio.TimeoutError):
result = await flow.async_step_link({})
2019-07-31 19:25:30 +00:00
assert result["type"] == "form"
assert result["step_id"] == "link"
assert result["errors"] == {"base": "linking"}
async def test_flow_link_button_not_pressed(hass):
"""Test config flow ."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
2019-07-31 19:25:30 +00:00
with patch("aiohue.Bridge.create_user", side_effect=aiohue.LinkButtonNotPressed):
result = await flow.async_step_link({})
2019-07-31 19:25:30 +00:00
assert result["type"] == "form"
assert result["step_id"] == "link"
assert result["errors"] == {"base": "register_failed"}
async def test_flow_link_unknown_host(hass):
"""Test config flow ."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
2019-07-31 19:25:30 +00:00
with patch("aiohue.Bridge.create_user", side_effect=aiohue.RequestError):
result = await flow.async_step_link({})
2019-07-31 19:25:30 +00:00
assert result["type"] == "form"
assert result["step_id"] == "link"
assert result["errors"] == {"base": "linking"}
async def test_bridge_ssdp(hass):
"""Test a bridge being discovered."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
2019-07-31 19:25:30 +00:00
with patch.object(
config_flow, "get_bridge", side_effect=errors.AuthenticationRequired
):
result = await flow.async_step_ssdp(
{
"host": "0.0.0.0",
"serial": "1234",
"manufacturerURL": config_flow.HUE_MANUFACTURERURL,
}
)
2019-07-31 19:25:30 +00:00
assert result["type"] == "form"
assert result["step_id"] == "link"
async def test_bridge_ssdp_discover_other_bridge(hass):
"""Test that discovery ignores other bridges."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
2019-07-31 19:25:30 +00:00
result = await flow.async_step_ssdp(
{"manufacturerURL": "http://www.notphilips.com"}
)
2019-07-31 19:25:30 +00:00
assert result["type"] == "abort"
async def test_bridge_ssdp_emulated_hue(hass):
"""Test if discovery info is from an emulated hue instance."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
2019-07-31 19:25:30 +00:00
result = await flow.async_step_ssdp(
{
"name": "HASS Bridge",
"host": "0.0.0.0",
"serial": "1234",
"manufacturerURL": config_flow.HUE_MANUFACTURERURL,
}
)
2019-07-31 19:25:30 +00:00
assert result["type"] == "abort"
async def test_bridge_ssdp_already_configured(hass):
"""Test if a discovered bridge has already been configured."""
2019-07-31 19:25:30 +00:00
MockConfigEntry(domain="hue", data={"host": "0.0.0.0"}).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
2019-07-31 19:25:30 +00:00
result = await flow.async_step_ssdp(
{
"host": "0.0.0.0",
"serial": "1234",
"manufacturerURL": config_flow.HUE_MANUFACTURERURL,
}
)
2019-07-31 19:25:30 +00:00
assert result["type"] == "abort"
async def test_import_with_existing_config(hass):
"""Test importing a host with an existing config file."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
bridge = Mock()
2019-07-31 19:25:30 +00:00
bridge.username = "username-abc"
bridge.config.bridgeid = "bridge-id-1234"
bridge.config.name = "Mock Bridge"
bridge.host = "0.0.0.0"
with patch.object(
config_flow, "_find_username_from_config", return_value="mock-user"
), patch.object(config_flow, "get_bridge", return_value=mock_coro(bridge)):
result = await flow.async_step_import({"host": "0.0.0.0", "path": "bla.conf"})
assert result["type"] == "create_entry"
assert result["title"] == "Mock Bridge"
assert result["data"] == {
"host": "0.0.0.0",
"bridge_id": "bridge-id-1234",
"username": "username-abc",
}
async def test_import_with_no_config(hass):
"""Test importing a host without an existing config file."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
2019-07-31 19:25:30 +00:00
with patch.object(
config_flow, "get_bridge", side_effect=errors.AuthenticationRequired
):
result = await flow.async_step_import({"host": "0.0.0.0"})
2019-07-31 19:25:30 +00:00
assert result["type"] == "form"
assert result["step_id"] == "link"
async def test_import_with_existing_but_invalid_config(hass):
"""Test importing a host with a config file with invalid username."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
2019-07-31 19:25:30 +00:00
with patch.object(
config_flow, "_find_username_from_config", return_value="mock-user"
), patch.object(
config_flow, "get_bridge", side_effect=errors.AuthenticationRequired
):
result = await flow.async_step_import({"host": "0.0.0.0", "path": "bla.conf"})
2019-07-31 19:25:30 +00:00
assert result["type"] == "form"
assert result["step_id"] == "link"
async def test_import_cannot_connect(hass):
"""Test importing a host that we cannot conncet to."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
2019-07-31 19:25:30 +00:00
with patch.object(config_flow, "get_bridge", side_effect=errors.CannotConnect):
result = await flow.async_step_import({"host": "0.0.0.0"})
2019-07-31 19:25:30 +00:00
assert result["type"] == "abort"
assert result["reason"] == "cannot_connect"
async def test_creating_entry_removes_entries_for_same_host_or_bridge(hass):
"""Test that we clean up entries for same host and bridge.
An IP can only hold a single bridge and a single bridge can only be
accessible via a single IP. So when we create a new entry, we'll remove
all existing entries that either have same IP or same bridge_id.
"""
2019-07-31 19:25:30 +00:00
MockConfigEntry(
domain="hue", data={"host": "0.0.0.0", "bridge_id": "id-1234"}
).add_to_hass(hass)
2019-07-31 19:25:30 +00:00
MockConfigEntry(
domain="hue", data={"host": "1.2.3.4", "bridge_id": "id-1234"}
).add_to_hass(hass)
2019-07-31 19:25:30 +00:00
assert len(hass.config_entries.async_entries("hue")) == 2
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
bridge = Mock()
2019-07-31 19:25:30 +00:00
bridge.username = "username-abc"
bridge.config.bridgeid = "id-1234"
bridge.config.name = "Mock Bridge"
bridge.host = "0.0.0.0"
with patch.object(config_flow, "get_bridge", return_value=mock_coro(bridge)):
result = await flow.async_step_import({"host": "0.0.0.0"})
assert result["type"] == "create_entry"
assert result["title"] == "Mock Bridge"
assert result["data"] == {
"host": "0.0.0.0",
"bridge_id": "id-1234",
"username": "username-abc",
}
# We did not process the result of this entry but already removed the old
# ones. So we should have 0 entries.
2019-07-31 19:25:30 +00:00
assert len(hass.config_entries.async_entries("hue")) == 0
async def test_bridge_homekit(hass):
"""Test a bridge being discovered via HomeKit."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
2019-07-31 19:25:30 +00:00
with patch.object(
config_flow, "get_bridge", side_effect=errors.AuthenticationRequired
):
result = await flow.async_step_homekit(
{
"host": "0.0.0.0",
"serial": "1234",
"manufacturerURL": config_flow.HUE_MANUFACTURERURL,
}
)
2019-07-31 19:25:30 +00:00
assert result["type"] == "form"
assert result["step_id"] == "link"
async def test_bridge_homekit_already_configured(hass):
"""Test if a HomeKit discovered bridge has already been configured."""
2019-07-31 19:25:30 +00:00
MockConfigEntry(domain="hue", data={"host": "0.0.0.0"}).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
2019-07-31 19:25:30 +00:00
result = await flow.async_step_homekit({"host": "0.0.0.0"})
2019-07-31 19:25:30 +00:00
assert result["type"] == "abort"