core/tests/components/hue/test_config_flow.py

399 lines
12 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.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 ."""
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()
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
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={})
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
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()
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", data={"host": "1.2.3.4"}).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
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
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"]({"host": "0.0.0.0"})
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."""
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()
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
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
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
with patch("aiohue.Bridge.create_user", 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
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 = {}
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,
}
)
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"
async def test_bridge_ssdp_already_configured(hass):
"""Test if a discovered bridge has already been configured."""
MockConfigEntry(domain="hue", data={"host": "0.0.0.0"}).add_to_hass(hass)
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"] == "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()
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 = {}
with patch.object(
config_flow, "get_bridge", side_effect=errors.AuthenticationRequired
):
result = await flow.async_step_import({"host": "0.0.0.0"})
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 = {}
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"})
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 = {}
with patch.object(config_flow, "get_bridge", side_effect=errors.CannotConnect):
result = await flow.async_step_import({"host": "0.0.0.0"})
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.
"""
MockConfigEntry(
domain="hue", data={"host": "0.0.0.0", "bridge_id": "id-1234"}
).add_to_hass(hass)
MockConfigEntry(
domain="hue", data={"host": "1.2.3.4", "bridge_id": "id-1234"}
).add_to_hass(hass)
assert len(hass.config_entries.async_entries("hue")) == 2
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
bridge = Mock()
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.
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 = {}
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,
}
)
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", data={"host": "0.0.0.0"}).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
result = await flow.async_step_homekit({"host": "0.0.0.0"})
assert result["type"] == "abort"