core/tests/components/geniushub/test_config_flow.py
GeoffAtHome 890b54e36f
Add config flow to Genius hub (#116173)
* Adding config flow

* Fix setup issues.

* Added test for config_flow

* Refactor schemas.

* Fixed ruff-format on const.py

* Added geniushub-cleint to requirements_test_all.txt

* Updates from review.

* Correct multiple logger comment errors.

* User menu rather than check box.

* Correct logger messages.

* Correct test_config_flow

* Import config entry from YAML

* Config flow integration

* Refactor genius hub test_config_flow.

* Improvements and simplification from code review.

* Correct tests

* Stop device being added twice.

* Correct validate_input.

* Changes to meet code review three week ago.

* Fix Ruff undefined error

* Update homeassistant/components/geniushub/config_flow.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* Update homeassistant/components/geniushub/config_flow.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* Change case Cloud and Local to CLOUD and LOCAL.

* More from code review

* Fix

* Fix

* Update homeassistant/components/geniushub/strings.json

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2024-07-21 19:57:41 +02:00

483 lines
14 KiB
Python

"""Test the Geniushub config flow."""
from http import HTTPStatus
import socket
from typing import Any
from unittest.mock import AsyncMock
from aiohttp import ClientConnectionError, ClientResponseError
import pytest
from homeassistant.components.geniushub import DOMAIN
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
from homeassistant.const import (
CONF_HOST,
CONF_MAC,
CONF_PASSWORD,
CONF_TOKEN,
CONF_USERNAME,
)
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry
async def test_full_local_flow(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_geniushub_client: AsyncMock,
) -> None:
"""Test full local flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "local_api"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "local_api"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_HOST: "10.0.0.130",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "10.0.0.130"
assert result["data"] == {
CONF_HOST: "10.0.0.130",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
}
assert result["result"].unique_id == "aa:bb:cc:dd:ee:ff"
@pytest.mark.parametrize(
("exception", "error"),
[
(socket.gaierror, "invalid_host"),
(
ClientResponseError(AsyncMock(), (), status=HTTPStatus.UNAUTHORIZED),
"invalid_auth",
),
(
ClientResponseError(AsyncMock(), (), status=HTTPStatus.NOT_FOUND),
"invalid_host",
),
(TimeoutError, "cannot_connect"),
(ClientConnectionError, "cannot_connect"),
(Exception, "unknown"),
],
)
async def test_local_flow_exceptions(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_geniushub_client: AsyncMock,
exception: Exception,
error: str,
) -> None:
"""Test local flow exceptions."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "local_api"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "local_api"
mock_geniushub_client.request.side_effect = exception
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_HOST: "10.0.0.130",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": error}
mock_geniushub_client.request.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_HOST: "10.0.0.130",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
async def test_local_duplicate_data(
hass: HomeAssistant,
mock_geniushub_client: AsyncMock,
mock_local_config_entry: MockConfigEntry,
) -> None:
"""Test local flow aborts on duplicate data."""
mock_local_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "local_api"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "local_api"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_HOST: "10.0.0.130",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_local_duplicate_mac(
hass: HomeAssistant,
mock_geniushub_client: AsyncMock,
mock_local_config_entry: MockConfigEntry,
) -> None:
"""Test local flow aborts on duplicate MAC."""
mock_local_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "local_api"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "local_api"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_HOST: "10.0.0.131",
CONF_USERNAME: "test-username1",
CONF_PASSWORD: "test-password",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_full_cloud_flow(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_geniushub_client: AsyncMock,
) -> None:
"""Test full cloud flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "cloud_api"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "cloud_api"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_TOKEN: "abcdef",
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Genius hub"
assert result["data"] == {
CONF_TOKEN: "abcdef",
}
@pytest.mark.parametrize(
("exception", "error"),
[
(socket.gaierror, "invalid_host"),
(
ClientResponseError(AsyncMock(), (), status=HTTPStatus.UNAUTHORIZED),
"invalid_auth",
),
(
ClientResponseError(AsyncMock(), (), status=HTTPStatus.NOT_FOUND),
"invalid_host",
),
(TimeoutError, "cannot_connect"),
(ClientConnectionError, "cannot_connect"),
(Exception, "unknown"),
],
)
async def test_cloud_flow_exceptions(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_geniushub_client: AsyncMock,
exception: Exception,
error: str,
) -> None:
"""Test cloud flow exceptions."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "cloud_api"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "cloud_api"
mock_geniushub_client.request.side_effect = exception
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_TOKEN: "abcdef",
},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": error}
mock_geniushub_client.request.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_TOKEN: "abcdef",
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
async def test_cloud_duplicate(
hass: HomeAssistant,
mock_geniushub_client: AsyncMock,
mock_cloud_config_entry: MockConfigEntry,
) -> None:
"""Test cloud flow aborts on duplicate data."""
mock_cloud_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "cloud_api"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "cloud_api"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_TOKEN: "abcdef",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
@pytest.mark.parametrize(
("data"),
[
{
CONF_HOST: "10.0.0.130",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
},
{
CONF_HOST: "10.0.0.130",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
CONF_MAC: "aa:bb:cc:dd:ee:ff",
},
],
)
async def test_import_local_flow(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_geniushub_client: AsyncMock,
data: dict[str, Any],
) -> None:
"""Test full local import flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=data,
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "10.0.0.130"
assert result["data"] == data
assert result["result"].unique_id == "aa:bb:cc:dd:ee:ff"
@pytest.mark.parametrize(
("data"),
[
{
CONF_TOKEN: "abcdef",
},
{
CONF_TOKEN: "abcdef",
CONF_MAC: "aa:bb:cc:dd:ee:ff",
},
],
)
async def test_import_cloud_flow(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_geniushub_client: AsyncMock,
data: dict[str, Any],
) -> None:
"""Test full cloud import flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=data,
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Genius hub"
assert result["data"] == data
@pytest.mark.parametrize(
("data"),
[
{
CONF_HOST: "10.0.0.130",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
},
{
CONF_HOST: "10.0.0.130",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
CONF_MAC: "aa:bb:cc:dd:ee:ff",
},
{
CONF_TOKEN: "abcdef",
},
{
CONF_TOKEN: "abcdef",
CONF_MAC: "aa:bb:cc:dd:ee:ff",
},
],
)
@pytest.mark.parametrize(
("exception", "reason"),
[
(socket.gaierror, "invalid_host"),
(
ClientResponseError(AsyncMock(), (), status=HTTPStatus.UNAUTHORIZED),
"invalid_auth",
),
(
ClientResponseError(AsyncMock(), (), status=HTTPStatus.NOT_FOUND),
"invalid_host",
),
(TimeoutError, "cannot_connect"),
(ClientConnectionError, "cannot_connect"),
(Exception, "unknown"),
],
)
async def test_import_flow_exceptions(
hass: HomeAssistant,
mock_geniushub_client: AsyncMock,
data: dict[str, Any],
exception: Exception,
reason: str,
) -> None:
"""Test import flow exceptions."""
mock_geniushub_client.request.side_effect = exception
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=data,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == reason
@pytest.mark.parametrize(
("data"),
[
{
CONF_HOST: "10.0.0.130",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
},
{
CONF_HOST: "10.0.0.131",
CONF_USERNAME: "test-username1",
CONF_PASSWORD: "test-password",
},
],
)
async def test_import_flow_local_duplicate(
hass: HomeAssistant,
mock_geniushub_client: AsyncMock,
mock_local_config_entry: MockConfigEntry,
data: dict[str, Any],
) -> None:
"""Test import flow aborts on local duplicate data."""
mock_local_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=data,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_import_flow_cloud_duplicate(
hass: HomeAssistant,
mock_geniushub_client: AsyncMock,
mock_cloud_config_entry: MockConfigEntry,
) -> None:
"""Test import flow aborts on cloud duplicate data."""
mock_cloud_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data={
CONF_TOKEN: "abcdef",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"