hass-core/tests/components/hue/test_config_flow.py
Paulus Schoutsen 58b5833d64
Convert Hue to use unique ID (#30000)
* Convert Hue to use unique ID

* Fix normalization

* Store/restore unique ID

* Fix tests
2019-12-16 19:45:09 +01:00

386 lines
11 KiB
Python

"""Tests for Philips Hue config flow."""
import asyncio
from unittest.mock import Mock, patch
import aiohue
import pytest
import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.components.hue import config_flow, const
from tests.common import MockConfigEntry, mock_coro
async def test_flow_works(hass):
"""Test config flow ."""
mock_bridge = Mock()
mock_bridge.host = "1.2.3.4"
mock_bridge.username = None
mock_bridge.config.name = "Mock Bridge"
mock_bridge.id = "aabbccddeeff"
async def mock_create_user(username):
mock_bridge.username = username
mock_bridge.create_user = mock_create_user
mock_bridge.initialize.return_value = mock_coro()
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
with patch(
"homeassistant.components.hue.config_flow.discover_nupnp",
return_value=mock_coro([mock_bridge]),
):
result = await flow.async_step_init()
assert result["type"] == "form"
assert result["step_id"] == "link"
assert flow.context["unique_id"] == "aabbccddeeff"
result = await flow.async_step_link(user_input={})
assert result["type"] == "create_entry"
assert result["title"] == "Mock Bridge"
assert result["data"] == {
"host": "1.2.3.4",
"username": "home-assistant#test-home",
}
assert len(mock_bridge.initialize.mock_calls) == 1
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()
assert result["type"] == "abort"
async def test_flow_all_discovered_bridges_exist(hass, aioclient_mock):
"""Test config flow discovers only already configured bridges."""
aioclient_mock.get(
const.API_NUPNP, json=[{"internalipaddress": "1.2.3.4", "id": "bla"}]
)
MockConfigEntry(
domain="hue", unique_id="bla", data={"host": "1.2.3.4"}
).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
result = await flow.async_step_init()
assert result["type"] == "abort"
async def test_flow_one_bridge_discovered(hass, aioclient_mock):
"""Test config flow discovers one bridge."""
aioclient_mock.get(
const.API_NUPNP, json=[{"internalipaddress": "1.2.3.4", "id": "bla"}]
)
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
result = await flow.async_step_init()
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."""
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()
assert result["type"] == "form"
assert result["step_id"] == "init"
with pytest.raises(vol.Invalid):
assert result["data_schema"]({"id": "not-discovered"})
result["data_schema"]({"id": "bla"})
result["data_schema"]({"id": "beer"})
async def test_flow_two_bridges_discovered_one_new(hass, aioclient_mock):
"""Test config flow discovers two bridges."""
aioclient_mock.get(
const.API_NUPNP,
json=[
{"internalipaddress": "1.2.3.4", "id": "bla"},
{"internalipaddress": "5.6.7.8", "id": "beer"},
],
)
MockConfigEntry(
domain="hue", unique_id="bla", data={"host": "1.2.3.4"}
).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
result = await flow.async_step_init()
assert result["type"] == "form"
assert result["step_id"] == "link"
assert flow.bridge.host == "5.6.7.8"
async def test_flow_timeout_discovery(hass):
"""Test config flow ."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
with patch(
"homeassistant.components.hue.config_flow.discover_nupnp",
side_effect=asyncio.TimeoutError,
):
result = await flow.async_step_init()
assert result["type"] == "abort"
async def test_flow_link_timeout(hass):
"""Test config flow ."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.bridge = Mock()
with patch("aiohue.Bridge.create_user", side_effect=asyncio.TimeoutError):
result = await flow.async_step_link({})
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
flow.bridge = Mock(
username=None, create_user=Mock(side_effect=aiohue.LinkButtonNotPressed)
)
result = await flow.async_step_link({})
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
flow.bridge = Mock()
with patch("aiohue.Bridge.create_user", side_effect=aiohue.RequestError):
result = await flow.async_step_link({})
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 = {}
result = await flow.async_step_ssdp(
{
"host": "0.0.0.0",
"serial": "1234",
"manufacturerURL": config_flow.HUE_MANUFACTURERURL,
}
)
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
result = await flow.async_step_ssdp(
{"manufacturerURL": "http://www.notphilips.com"}
)
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 = {}
result = await flow.async_step_ssdp(
{
"name": "HASS Bridge",
"host": "0.0.0.0",
"serial": "1234",
"manufacturerURL": config_flow.HUE_MANUFACTURERURL,
}
)
assert result["type"] == "abort"
assert result["reason"] == "not_hue_bridge"
async def test_bridge_ssdp_espalexa(hass):
"""Test if discovery info is from an Espalexa based device."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
result = await flow.async_step_ssdp(
{
"name": "Espalexa (0.0.0.0)",
"host": "0.0.0.0",
"serial": "1234",
"manufacturerURL": config_flow.HUE_MANUFACTURERURL,
}
)
assert result["type"] == "abort"
assert result["reason"] == "not_hue_bridge"
async def test_bridge_ssdp_already_configured(hass):
"""Test if a discovered bridge has already been configured."""
MockConfigEntry(
domain="hue", unique_id="1234", data={"host": "0.0.0.0"}
).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
with pytest.raises(data_entry_flow.AbortFlow):
await flow.async_step_ssdp(
{
"host": "0.0.0.0",
"serial": "1234",
"manufacturerURL": config_flow.HUE_MANUFACTURERURL,
}
)
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 = {}
result = await flow.async_step_import({"host": "0.0.0.0"})
assert result["type"] == "form"
assert result["step_id"] == "link"
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.
"""
orig_entry = MockConfigEntry(
domain="hue", data={"host": "0.0.0.0", "username": "aaaa"}, unique_id="id-1234",
)
orig_entry.add_to_hass(hass)
MockConfigEntry(
domain="hue", data={"host": "1.2.3.4", "username": "bbbb"}, unique_id="id-5678",
).add_to_hass(hass)
assert len(hass.config_entries.async_entries("hue")) == 2
bridge = Mock()
bridge.username = "username-abc"
bridge.config.name = "Mock Bridge"
bridge.host = "0.0.0.0"
bridge.id = "id-1234"
with patch(
"aiohue.Bridge", return_value=bridge,
):
result = await hass.config_entries.flow.async_init(
"hue", data={"host": "2.2.2.2"}, context={"source": "import"}
)
assert result["type"] == "form"
assert result["step_id"] == "link"
with patch(
"homeassistant.components.hue.config_flow.authenticate_bridge",
return_value=mock_coro(),
), patch(
"homeassistant.components.hue.async_setup_entry",
side_effect=lambda _, _2: mock_coro(True),
):
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
assert result["type"] == "create_entry"
assert result["title"] == "Mock Bridge"
assert result["data"] == {
"host": "0.0.0.0",
"username": "username-abc",
}
entries = hass.config_entries.async_entries("hue")
assert len(entries) == 2
new_entry = entries[-1]
assert orig_entry.entry_id != new_entry.entry_id
assert new_entry.unique_id == "id-1234"
async def test_bridge_homekit(hass):
"""Test a bridge being discovered via HomeKit."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
result = await flow.async_step_homekit(
{
"host": "0.0.0.0",
"serial": "1234",
"manufacturerURL": config_flow.HUE_MANUFACTURERURL,
"properties": {"id": "aa:bb:cc:dd:ee:ff"},
}
)
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."""
MockConfigEntry(
domain="hue", unique_id="aabbccddeeff", data={"host": "0.0.0.0"}
).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
with pytest.raises(data_entry_flow.AbortFlow):
await flow.async_step_homekit(
{"host": "0.0.0.0", "properties": {"id": "aa:bb:cc:dd:ee:ff"}}
)