Axis - Allow manual configuration to update existing configuration (#30467)

* Allow manual configuration to update existing configuration

* Harmonize tests

* Understand what Elupus means...
This commit is contained in:
Robert Svensson 2020-01-05 10:11:17 +01:00 committed by GitHub
parent 063193e6e5
commit 35e19eec18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 183 additions and 79 deletions

View file

@ -64,9 +64,13 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
device = await get_device(self.hass, self.device_config)
self.serial_number = device.vapix.params.system_serialnumber
await self.async_set_unique_id(self.serial_number)
self._abort_if_unique_id_configured()
config_entry = await self.async_set_unique_id(self.serial_number)
if config_entry:
return self._update_entry(
config_entry,
host=user_input[CONF_HOST],
port=user_input[CONF_PORT],
)
self.model = device.vapix.params.prodnbr
@ -143,15 +147,14 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
if discovery_info[CONF_HOST].startswith("169.254"):
return self.async_abort(reason="link_local_address")
for entry in self.hass.config_entries.async_entries(DOMAIN):
if serial_number == entry.unique_id:
return self._update_entry(
entry,
host=discovery_info[CONF_HOST],
port=discovery_info[CONF_PORT],
)
config_entry = await self.async_set_unique_id(serial_number)
if config_entry:
return self._update_entry(
config_entry,
host=discovery_info[CONF_HOST],
port=discovery_info[CONF_PORT],
)
await self.async_set_unique_id(serial_number)
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context["title_placeholders"] = {
"name": discovery_info["hostname"][:-7],

View file

@ -1,9 +1,6 @@
"""Test Axis config flow."""
from unittest.mock import Mock, patch
import pytest
import homeassistant
from homeassistant.components import axis
from homeassistant.components.axis import config_flow
@ -12,31 +9,37 @@ from .test_device import MAC, setup_axis_integration
from tests.common import MockConfigEntry, mock_coro
async def test_flow_works(hass):
def setup_mock_axis_device(mock_device):
"""Prepare mock axis device."""
def mock_constructor(loop, host, username, password, port, web_proto):
"""Fake the controller constructor."""
mock_device.loop = loop
mock_device.host = host
mock_device.username = username
mock_device.password = password
mock_device.port = port
return mock_device
mock_device.side_effect = mock_constructor
mock_device.vapix.params.system_serialnumber = MAC
mock_device.vapix.params.prodnbr = "prodnbr"
mock_device.vapix.params.prodtype = "prodtype"
mock_device.vapix.params.firmware_version = "firmware_version"
async def test_flow_manual_configuration(hass):
"""Test that config flow works."""
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": "user"}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
with patch("axis.AxisDevice") as mock_device:
def mock_constructor(loop, host, username, password, port, web_proto):
"""Fake the controller constructor."""
mock_device.loop = loop
mock_device.host = host
mock_device.username = username
mock_device.password = password
mock_device.port = port
return mock_device
mock_device.side_effect = mock_constructor
mock_device.vapix.params.system_serialnumber = "serialnumber"
mock_device.vapix.params.prodnbr = "prodnbr"
mock_device.vapix.params.prodtype = "prodtype"
mock_device.vapix.params.firmware_version = "firmware_version"
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": "user"}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
setup_mock_axis_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
@ -49,7 +52,7 @@ async def test_flow_works(hass):
)
assert result["type"] == "create_entry"
assert result["title"] == f"prodnbr - serialnumber"
assert result["title"] == f"prodnbr - {MAC}"
assert result["data"] == {
axis.CONF_DEVICE: {
config_flow.CONF_HOST: "1.2.3.4",
@ -57,19 +60,22 @@ async def test_flow_works(hass):
config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PORT: 80,
},
config_flow.CONF_MAC: "serialnumber",
config_flow.CONF_MAC: MAC,
config_flow.CONF_MODEL: "prodnbr",
config_flow.CONF_NAME: "prodnbr 0",
}
async def test_flow_fails_already_configured(hass):
async def test_manual_configuration_update_configuration(hass):
"""Test that config flow fails on already configured device."""
await setup_axis_integration(hass)
device = await setup_axis_integration(hass)
flow = config_flow.AxisFlowHandler()
flow.hass = hass
flow.context = {}
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": "user"}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
mock_device = Mock()
mock_device.vapix.params.system_serialnumber = MAC
@ -77,33 +83,78 @@ async def test_flow_fails_already_configured(hass):
with patch(
"homeassistant.components.axis.config_flow.get_device",
return_value=mock_coro(mock_device),
), pytest.raises(homeassistant.data_entry_flow.AbortFlow):
await flow.async_step_user(
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
config_flow.CONF_HOST: "2.3.4.5",
config_flow.CONF_USERNAME: "user",
config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PORT: 80,
},
)
assert result["type"] == "abort"
assert result["reason"] == "updated_configuration"
assert (
device.config_entry.data[config_flow.CONF_DEVICE][config_flow.CONF_HOST]
== "2.3.4.5"
)
async def test_flow_fails_already_configured(hass):
"""Test that config flow fails on already configured device."""
await setup_axis_integration(hass)
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": "user"}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
mock_device = Mock()
mock_device.vapix.params.system_serialnumber = MAC
with patch(
"homeassistant.components.axis.config_flow.get_device",
return_value=mock_coro(mock_device),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_USERNAME: "user",
config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PORT: 80,
}
},
)
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
async def test_flow_fails_faulty_credentials(hass):
"""Test that config flow fails on faulty credentials."""
flow = config_flow.AxisFlowHandler()
flow.hass = hass
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": "user"}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
with patch(
"homeassistant.components.axis.config_flow.get_device",
side_effect=config_flow.AuthenticationRequired,
):
result = await flow.async_step_user(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_USERNAME: "user",
config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PORT: 80,
}
},
)
assert result["errors"] == {"base": "faulty_credentials"}
@ -111,56 +162,79 @@ async def test_flow_fails_faulty_credentials(hass):
async def test_flow_fails_device_unavailable(hass):
"""Test that config flow fails on device unavailable."""
flow = config_flow.AxisFlowHandler()
flow.hass = hass
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": "user"}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
with patch(
"homeassistant.components.axis.config_flow.get_device",
side_effect=config_flow.CannotConnect,
):
result = await flow.async_step_user(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_USERNAME: "user",
config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PORT: 80,
}
},
)
assert result["errors"] == {"base": "device_unavailable"}
async def test_flow_create_entry(hass):
"""Test that create entry can generate a name without other entries."""
flow = config_flow.AxisFlowHandler()
flow.hass = hass
flow.model = "model"
result = await flow._create_entry()
assert result["data"][config_flow.CONF_NAME] == "model 0"
async def test_flow_create_entry_more_entries(hass):
async def test_flow_create_entry_multiple_existing_entries_of_same_model(hass):
"""Test that create entry can generate a name with other entries."""
entry = MockConfigEntry(
domain=axis.DOMAIN,
data={config_flow.CONF_NAME: "model 0", config_flow.CONF_MODEL: "model"},
data={config_flow.CONF_NAME: "prodnbr 0", config_flow.CONF_MODEL: "prodnbr"},
)
entry.add_to_hass(hass)
entry2 = MockConfigEntry(
domain=axis.DOMAIN,
data={config_flow.CONF_NAME: "model 1", config_flow.CONF_MODEL: "model"},
data={config_flow.CONF_NAME: "prodnbr 1", config_flow.CONF_MODEL: "prodnbr"},
)
entry2.add_to_hass(hass)
flow = config_flow.AxisFlowHandler()
flow.hass = hass
flow.model = "model"
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": "user"}
)
result = await flow._create_entry()
assert result["type"] == "form"
assert result["step_id"] == "user"
assert result["data"][config_flow.CONF_NAME] == "model 2"
with patch("axis.AxisDevice") as mock_device:
setup_mock_axis_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_USERNAME: "user",
config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PORT: 80,
},
)
assert result["type"] == "create_entry"
assert result["title"] == f"prodnbr - {MAC}"
assert result["data"] == {
axis.CONF_DEVICE: {
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_USERNAME: "user",
config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PORT: 80,
},
config_flow.CONF_MAC: MAC,
config_flow.CONF_MODEL: "prodnbr",
config_flow.CONF_NAME: "prodnbr 2",
}
assert result["data"][config_flow.CONF_NAME] == "prodnbr 2"
async def test_zeroconf_flow(hass):
@ -172,7 +246,7 @@ async def test_zeroconf_flow(hass):
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_PORT: 80,
"hostname": "name",
"properties": {"macaddress": "00408C12345"},
"properties": {"macaddress": MAC},
},
context={"source": "zeroconf"},
)
@ -180,6 +254,36 @@ async def test_zeroconf_flow(hass):
assert result["type"] == "form"
assert result["step_id"] == "user"
with patch("axis.AxisDevice") as mock_device:
setup_mock_axis_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_USERNAME: "user",
config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PORT: 80,
},
)
assert result["type"] == "create_entry"
assert result["title"] == f"prodnbr - {MAC}"
assert result["data"] == {
axis.CONF_DEVICE: {
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_USERNAME: "user",
config_flow.CONF_PASSWORD: "pass",
config_flow.CONF_PORT: 80,
},
config_flow.CONF_MAC: MAC,
config_flow.CONF_MODEL: "prodnbr",
config_flow.CONF_NAME: "prodnbr 0",
}
assert result["data"][config_flow.CONF_NAME] == "prodnbr 0"
async def test_zeroconf_flow_already_configured(hass):
"""Test that zeroconf doesn't setup already configured devices."""
@ -192,7 +296,7 @@ async def test_zeroconf_flow_already_configured(hass):
config_flow.CONF_HOST: "1.2.3.4",
config_flow.CONF_PORT: 80,
"hostname": "name",
"properties": {"macaddress": "00408C12345"},
"properties": {"macaddress": MAC},
},
context={"source": "zeroconf"},
)
@ -245,10 +349,7 @@ async def test_zeroconf_flow_ignore_link_local_address(hass):
"""Test that zeroconf doesn't setup devices with link local addresses."""
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN,
data={
config_flow.CONF_HOST: "169.254.3.4",
"properties": {"macaddress": "00408C12345"},
},
data={config_flow.CONF_HOST: "169.254.3.4", "properties": {"macaddress": MAC}},
context={"source": "zeroconf"},
)