Make hue config flow tests more robust (#30678)

* Make hue config flow tests robust

* Fix io on setup

* Update last tests
This commit is contained in:
Martin Hjelmare 2020-01-12 05:00:49 +01:00 committed by Paulus Schoutsen
parent 91f738127d
commit 030a399b09

View File

@ -1,48 +1,76 @@
"""Tests for Philips Hue config flow.""" """Tests for Philips Hue config flow."""
import asyncio import asyncio
from unittest.mock import Mock, patch from unittest.mock import Mock
import aiohue import aiohue
from asynctest import CoroutineMock, patch
import pytest import pytest
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries, data_entry_flow from homeassistant import config_entries
from homeassistant.components import ssdp from homeassistant.components import ssdp
from homeassistant.components.hue import config_flow, const from homeassistant.components.hue import config_flow, const
from tests.common import MockConfigEntry, mock_coro from tests.common import MockConfigEntry
@pytest.fixture(name="hue_setup", autouse=True)
def hue_setup_fixture():
"""Mock hue entry setup."""
with patch("homeassistant.components.hue.async_setup_entry", return_value=True):
yield
def get_mock_bridge(
bridge_id="aabbccddeeff", host="1.2.3.4", mock_create_user=None, username=None
):
"""Return a mock bridge."""
mock_bridge = Mock()
mock_bridge.host = host
mock_bridge.username = username
mock_bridge.config.name = "Mock Bridge"
mock_bridge.id = bridge_id
if not mock_create_user:
async def create_user(username):
mock_bridge.username = username
mock_create_user = create_user
mock_bridge.create_user = mock_create_user
mock_bridge.initialize = CoroutineMock()
return mock_bridge
async def test_flow_works(hass): async def test_flow_works(hass):
"""Test config flow .""" """Test config flow ."""
mock_bridge = Mock() mock_bridge = get_mock_bridge()
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( with patch(
"homeassistant.components.hue.config_flow.discover_nupnp", "homeassistant.components.hue.config_flow.discover_nupnp",
return_value=mock_coro([mock_bridge]), return_value=[mock_bridge],
): ):
result = await flow.async_step_init() result = await hass.config_entries.flow.async_init(
const.DOMAIN, context={"source": "user"}
)
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "link" assert result["step_id"] == "link"
assert flow.context["unique_id"] == "aabbccddeeff" flow = next(
(
flow
for flow in hass.config_entries.flow.async_progress()
if flow["flow_id"] == result["flow_id"]
)
)
assert flow["context"]["unique_id"] == "aabbccddeeff"
result = await flow.async_step_link(user_input={}) result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
assert result["type"] == "create_entry" assert result["type"] == "create_entry"
assert result["title"] == "Mock Bridge" assert result["title"] == "Mock Bridge"
@ -57,11 +85,12 @@ async def test_flow_works(hass):
async def test_flow_no_discovered_bridges(hass, aioclient_mock): async def test_flow_no_discovered_bridges(hass, aioclient_mock):
"""Test config flow discovers no bridges.""" """Test config flow discovers no bridges."""
aioclient_mock.get(const.API_NUPNP, json=[]) aioclient_mock.get(const.API_NUPNP, json=[])
flow = config_flow.HueFlowHandler()
flow.hass = hass
result = await flow.async_step_init() result = await hass.config_entries.flow.async_init(
const.DOMAIN, context={"source": "user"}
)
assert result["type"] == "abort" assert result["type"] == "abort"
assert result["reason"] == "no_bridges"
async def test_flow_all_discovered_bridges_exist(hass, aioclient_mock): async def test_flow_all_discovered_bridges_exist(hass, aioclient_mock):
@ -72,12 +101,12 @@ async def test_flow_all_discovered_bridges_exist(hass, aioclient_mock):
MockConfigEntry( MockConfigEntry(
domain="hue", unique_id="bla", data={"host": "1.2.3.4"} domain="hue", unique_id="bla", data={"host": "1.2.3.4"}
).add_to_hass(hass) ).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
result = await flow.async_step_init() result = await hass.config_entries.flow.async_init(
const.DOMAIN, context={"source": "user"}
)
assert result["type"] == "abort" assert result["type"] == "abort"
assert result["reason"] == "all_configured"
async def test_flow_one_bridge_discovered(hass, aioclient_mock): async def test_flow_one_bridge_discovered(hass, aioclient_mock):
@ -85,11 +114,10 @@ async def test_flow_one_bridge_discovered(hass, aioclient_mock):
aioclient_mock.get( aioclient_mock.get(
const.API_NUPNP, json=[{"internalipaddress": "1.2.3.4", "id": "bla"}] 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() result = await hass.config_entries.flow.async_init(
const.DOMAIN, context={"source": "user"}
)
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "link" assert result["step_id"] == "link"
@ -108,10 +136,10 @@ async def test_flow_two_bridges_discovered(hass, aioclient_mock):
{"internalipaddress": "5.6.7.8", "id": "beer"}, {"internalipaddress": "5.6.7.8", "id": "beer"},
], ],
) )
flow = config_flow.HueFlowHandler()
flow.hass = hass
result = await flow.async_step_init() result = await hass.config_entries.flow.async_init(
const.DOMAIN, context={"source": "user"}
)
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "init" assert result["step_id"] == "init"
@ -134,38 +162,52 @@ async def test_flow_two_bridges_discovered_one_new(hass, aioclient_mock):
MockConfigEntry( MockConfigEntry(
domain="hue", unique_id="bla", data={"host": "1.2.3.4"} domain="hue", unique_id="bla", data={"host": "1.2.3.4"}
).add_to_hass(hass) ).add_to_hass(hass)
flow = config_flow.HueFlowHandler()
flow.hass = hass
flow.context = {}
result = await flow.async_step_init() result = await hass.config_entries.flow.async_init(
const.DOMAIN, context={"source": "user"}
)
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "link" assert result["step_id"] == "link"
assert flow.bridge.host == "5.6.7.8" flow = next(
(
flow
for flow in hass.config_entries.flow.async_progress()
if flow["flow_id"] == result["flow_id"]
)
)
assert flow["context"]["unique_id"] == "beer"
async def test_flow_timeout_discovery(hass): async def test_flow_timeout_discovery(hass):
"""Test config flow .""" """Test config flow ."""
flow = config_flow.HueFlowHandler()
flow.hass = hass
with patch( with patch(
"homeassistant.components.hue.config_flow.discover_nupnp", "homeassistant.components.hue.config_flow.discover_nupnp",
side_effect=asyncio.TimeoutError, side_effect=asyncio.TimeoutError,
): ):
result = await flow.async_step_init() result = await hass.config_entries.flow.async_init(
const.DOMAIN, context={"source": "user"}
)
assert result["type"] == "abort" assert result["type"] == "abort"
assert result["reason"] == "discover_timeout"
async def test_flow_link_timeout(hass): async def test_flow_link_timeout(hass):
"""Test config flow .""" """Test config flow."""
flow = config_flow.HueFlowHandler() mock_bridge = get_mock_bridge(
flow.hass = hass mock_create_user=CoroutineMock(side_effect=asyncio.TimeoutError),
flow.bridge = Mock() )
with patch(
"homeassistant.components.hue.config_flow.discover_nupnp",
return_value=[mock_bridge],
):
result = await hass.config_entries.flow.async_init(
const.DOMAIN, context={"source": "user"}
)
with patch("aiohue.Bridge.create_user", side_effect=asyncio.TimeoutError): result = await hass.config_entries.flow.async_configure(
result = await flow.async_step_link({}) result["flow_id"], user_input={}
)
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "link" assert result["step_id"] == "link"
@ -174,13 +216,20 @@ async def test_flow_link_timeout(hass):
async def test_flow_link_button_not_pressed(hass): async def test_flow_link_button_not_pressed(hass):
"""Test config flow .""" """Test config flow ."""
flow = config_flow.HueFlowHandler() mock_bridge = get_mock_bridge(
flow.hass = hass mock_create_user=CoroutineMock(side_effect=aiohue.LinkButtonNotPressed),
flow.bridge = Mock(
username=None, create_user=Mock(side_effect=aiohue.LinkButtonNotPressed)
) )
with patch(
"homeassistant.components.hue.config_flow.discover_nupnp",
return_value=[mock_bridge],
):
result = await hass.config_entries.flow.async_init(
const.DOMAIN, context={"source": "user"}
)
result = await flow.async_step_link({}) result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "link" assert result["step_id"] == "link"
@ -189,12 +238,20 @@ async def test_flow_link_button_not_pressed(hass):
async def test_flow_link_unknown_host(hass): async def test_flow_link_unknown_host(hass):
"""Test config flow .""" """Test config flow ."""
flow = config_flow.HueFlowHandler() mock_bridge = get_mock_bridge(
flow.hass = hass mock_create_user=CoroutineMock(side_effect=aiohue.RequestError),
flow.bridge = Mock() )
with patch(
"homeassistant.components.hue.config_flow.discover_nupnp",
return_value=[mock_bridge],
):
result = await hass.config_entries.flow.async_init(
const.DOMAIN, context={"source": "user"}
)
with patch("aiohue.Bridge.create_user", side_effect=aiohue.RequestError): result = await hass.config_entries.flow.async_configure(
result = await flow.async_step_link({}) result["flow_id"], user_input={}
)
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "link" assert result["step_id"] == "link"
@ -203,16 +260,14 @@ async def test_flow_link_unknown_host(hass):
async def test_bridge_ssdp(hass): async def test_bridge_ssdp(hass):
"""Test a bridge being discovered.""" """Test a bridge being discovered."""
flow = config_flow.HueFlowHandler() result = await hass.config_entries.flow.async_init(
flow.hass = hass const.DOMAIN,
flow.context = {} context={"source": "ssdp"},
data={
result = await flow.async_step_ssdp(
{
ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/", ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL, ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL,
ssdp.ATTR_UPNP_SERIAL: "1234", ssdp.ATTR_UPNP_SERIAL: "1234",
} },
) )
assert result["type"] == "form" assert result["type"] == "form"
@ -221,29 +276,27 @@ async def test_bridge_ssdp(hass):
async def test_bridge_ssdp_discover_other_bridge(hass): async def test_bridge_ssdp_discover_other_bridge(hass):
"""Test that discovery ignores other bridges.""" """Test that discovery ignores other bridges."""
flow = config_flow.HueFlowHandler() result = await hass.config_entries.flow.async_init(
flow.hass = hass const.DOMAIN,
context={"source": "ssdp"},
result = await flow.async_step_ssdp( data={ssdp.ATTR_UPNP_MANUFACTURER_URL: "http://www.notphilips.com"},
{ssdp.ATTR_UPNP_MANUFACTURER_URL: "http://www.notphilips.com"}
) )
assert result["type"] == "abort" assert result["type"] == "abort"
assert result["reason"] == "not_hue_bridge"
async def test_bridge_ssdp_emulated_hue(hass): async def test_bridge_ssdp_emulated_hue(hass):
"""Test if discovery info is from an emulated hue instance.""" """Test if discovery info is from an emulated hue instance."""
flow = config_flow.HueFlowHandler() result = await hass.config_entries.flow.async_init(
flow.hass = hass const.DOMAIN,
flow.context = {} context={"source": "ssdp"},
data={
result = await flow.async_step_ssdp(
{
ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/", ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/",
ssdp.ATTR_UPNP_FRIENDLY_NAME: "Home Assistant Bridge", ssdp.ATTR_UPNP_FRIENDLY_NAME: "Home Assistant Bridge",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL, ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL,
ssdp.ATTR_UPNP_SERIAL: "1234", ssdp.ATTR_UPNP_SERIAL: "1234",
} },
) )
assert result["type"] == "abort" assert result["type"] == "abort"
@ -252,17 +305,15 @@ async def test_bridge_ssdp_emulated_hue(hass):
async def test_bridge_ssdp_espalexa(hass): async def test_bridge_ssdp_espalexa(hass):
"""Test if discovery info is from an Espalexa based device.""" """Test if discovery info is from an Espalexa based device."""
flow = config_flow.HueFlowHandler() result = await hass.config_entries.flow.async_init(
flow.hass = hass const.DOMAIN,
flow.context = {} context={"source": "ssdp"},
data={
result = await flow.async_step_ssdp(
{
ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/", ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/",
ssdp.ATTR_UPNP_FRIENDLY_NAME: "Espalexa (0.0.0.0)", ssdp.ATTR_UPNP_FRIENDLY_NAME: "Espalexa (0.0.0.0)",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL, ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL,
ssdp.ATTR_UPNP_SERIAL: "1234", ssdp.ATTR_UPNP_SERIAL: "1234",
} },
) )
assert result["type"] == "abort" assert result["type"] == "abort"
@ -275,27 +326,25 @@ async def test_bridge_ssdp_already_configured(hass):
domain="hue", unique_id="1234", data={"host": "0.0.0.0"} domain="hue", unique_id="1234", data={"host": "0.0.0.0"}
).add_to_hass(hass) ).add_to_hass(hass)
flow = config_flow.HueFlowHandler() result = await hass.config_entries.flow.async_init(
flow.hass = hass const.DOMAIN,
flow.context = {} context={"source": "ssdp"},
data={
ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL,
ssdp.ATTR_UPNP_SERIAL: "1234",
},
)
with pytest.raises(data_entry_flow.AbortFlow): assert result["type"] == "abort"
await flow.async_step_ssdp( assert result["reason"] == "already_configured"
{
ssdp.ATTR_SSDP_LOCATION: "http://0.0.0.0/",
ssdp.ATTR_UPNP_MANUFACTURER_URL: config_flow.HUE_MANUFACTURERURL,
ssdp.ATTR_UPNP_SERIAL: "1234",
}
)
async def test_import_with_no_config(hass): async def test_import_with_no_config(hass):
"""Test importing a host without an existing config file.""" """Test importing a host without an existing config file."""
flow = config_flow.HueFlowHandler() result = await hass.config_entries.flow.async_init(
flow.hass = hass const.DOMAIN, context={"source": "import"}, data={"host": "0.0.0.0"},
flow.context = {} )
result = await flow.async_step_import({"host": "0.0.0.0"})
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "link" assert result["step_id"] == "link"
@ -319,11 +368,9 @@ async def test_creating_entry_removes_entries_for_same_host_or_bridge(hass):
assert len(hass.config_entries.async_entries("hue")) == 2 assert len(hass.config_entries.async_entries("hue")) == 2
bridge = Mock() bridge = get_mock_bridge(
bridge.username = "username-abc" bridge_id="id-1234", host="2.2.2.2", username="username-abc"
bridge.config.name = "Mock Bridge" )
bridge.host = "0.0.0.0"
bridge.id = "id-1234"
with patch( with patch(
"aiohue.Bridge", return_value=bridge, "aiohue.Bridge", return_value=bridge,
@ -335,19 +382,15 @@ async def test_creating_entry_removes_entries_for_same_host_or_bridge(hass):
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "link" assert result["step_id"] == "link"
with patch( with patch("homeassistant.components.hue.config_flow.authenticate_bridge"), patch(
"homeassistant.components.hue.config_flow.authenticate_bridge", "homeassistant.components.hue.async_unload_entry", return_value=True
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"], {}) result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
assert result["type"] == "create_entry" assert result["type"] == "create_entry"
assert result["title"] == "Mock Bridge" assert result["title"] == "Mock Bridge"
assert result["data"] == { assert result["data"] == {
"host": "0.0.0.0", "host": "2.2.2.2",
"username": "username-abc", "username": "username-abc",
} }
entries = hass.config_entries.async_entries("hue") entries = hass.config_entries.async_entries("hue")
@ -359,17 +402,15 @@ async def test_creating_entry_removes_entries_for_same_host_or_bridge(hass):
async def test_bridge_homekit(hass): async def test_bridge_homekit(hass):
"""Test a bridge being discovered via HomeKit.""" """Test a bridge being discovered via HomeKit."""
flow = config_flow.HueFlowHandler() result = await hass.config_entries.flow.async_init(
flow.hass = hass const.DOMAIN,
flow.context = {} context={"source": "homekit"},
data={
result = await flow.async_step_homekit(
{
"host": "0.0.0.0", "host": "0.0.0.0",
"serial": "1234", "serial": "1234",
"manufacturerURL": config_flow.HUE_MANUFACTURERURL, "manufacturerURL": config_flow.HUE_MANUFACTURERURL,
"properties": {"id": "aa:bb:cc:dd:ee:ff"}, "properties": {"id": "aa:bb:cc:dd:ee:ff"},
} },
) )
assert result["type"] == "form" assert result["type"] == "form"
@ -382,11 +423,11 @@ async def test_bridge_homekit_already_configured(hass):
domain="hue", unique_id="aabbccddeeff", data={"host": "0.0.0.0"} domain="hue", unique_id="aabbccddeeff", data={"host": "0.0.0.0"}
).add_to_hass(hass) ).add_to_hass(hass)
flow = config_flow.HueFlowHandler() result = await hass.config_entries.flow.async_init(
flow.hass = hass const.DOMAIN,
flow.context = {} context={"source": "homekit"},
data={"host": "0.0.0.0", "properties": {"id": "aa:bb:cc:dd:ee:ff"}},
)
with pytest.raises(data_entry_flow.AbortFlow): assert result["type"] == "abort"
await flow.async_step_homekit( assert result["reason"] == "already_configured"
{"host": "0.0.0.0", "properties": {"id": "aa:bb:cc:dd:ee:ff"}}
)