core/tests/components/opower/test_config_flow.py

362 lines
12 KiB
Python
Raw Normal View History

"""Test the Opower config flow."""
from collections.abc import Generator
from unittest.mock import AsyncMock, patch
from opower import CannotConnect, InvalidAuth
import pytest
from homeassistant import config_entries
from homeassistant.components.opower.const import DOMAIN
from homeassistant.components.recorder import Recorder
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry
@pytest.fixture(autouse=True, name="mock_setup_entry")
def override_async_setup_entry() -> Generator[AsyncMock, None, None]:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.opower.async_setup_entry", return_value=True
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture
def mock_unload_entry() -> Generator[AsyncMock, None, None]:
"""Mock unloading a config entry."""
with patch(
"homeassistant.components.opower.async_unload_entry",
return_value=True,
) as mock_unload_entry:
yield mock_unload_entry
async def test_form(
recorder_mock: Recorder, hass: HomeAssistant, mock_setup_entry: AsyncMock
) -> None:
"""Test we get the form."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.FORM
assert not result["errors"]
with patch(
"homeassistant.components.opower.config_flow.Opower.async_login",
) as mock_login:
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"utility": "Pacific Gas and Electric Company (PG&E)",
"username": "test-username",
"password": "test-password",
},
)
await hass.async_block_till_done()
assert result2["type"] == FlowResultType.CREATE_ENTRY
assert result2["title"] == "Pacific Gas and Electric Company (PG&E) (test-username)"
assert result2["data"] == {
"utility": "Pacific Gas and Electric Company (PG&E)",
"username": "test-username",
"password": "test-password",
}
assert len(mock_setup_entry.mock_calls) == 1
assert mock_login.call_count == 1
async def test_form_with_mfa(
recorder_mock: Recorder, hass: HomeAssistant, mock_setup_entry: AsyncMock
) -> None:
"""Test we get the form."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.FORM
assert not result["errors"]
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"utility": "Consolidated Edison (ConEd)",
"username": "test-username",
"password": "test-password",
},
)
assert result2["type"] == FlowResultType.FORM
assert not result2["errors"]
with patch(
"homeassistant.components.opower.config_flow.Opower.async_login",
) as mock_login:
result3 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"totp_secret": "test-totp",
},
)
assert result3["type"] == FlowResultType.CREATE_ENTRY
assert result3["title"] == "Consolidated Edison (ConEd) (test-username)"
assert result3["data"] == {
"utility": "Consolidated Edison (ConEd)",
"username": "test-username",
"password": "test-password",
"totp_secret": "test-totp",
}
assert len(mock_setup_entry.mock_calls) == 1
assert mock_login.call_count == 1
async def test_form_with_mfa_bad_secret(
recorder_mock: Recorder, hass: HomeAssistant, mock_setup_entry: AsyncMock
) -> None:
"""Test MFA asks for password again when validation fails."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.FORM
assert not result["errors"]
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"utility": "Consolidated Edison (ConEd)",
"username": "test-username",
"password": "test-password",
},
)
assert result2["type"] == FlowResultType.FORM
assert not result2["errors"]
with patch(
"homeassistant.components.opower.config_flow.Opower.async_login",
side_effect=InvalidAuth,
) as mock_login:
result3 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"totp_secret": "test-totp",
},
)
assert result3["type"] == FlowResultType.FORM
assert result3["errors"] == {
"base": "invalid_auth",
}
with patch(
"homeassistant.components.opower.config_flow.Opower.async_login",
) as mock_login:
result4 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"username": "test-username",
"password": "updated-password",
"totp_secret": "updated-totp",
},
)
assert result4["type"] == FlowResultType.CREATE_ENTRY
assert result4["title"] == "Consolidated Edison (ConEd) (test-username)"
assert result4["data"] == {
"utility": "Consolidated Edison (ConEd)",
"username": "test-username",
"password": "updated-password",
"totp_secret": "updated-totp",
}
assert len(mock_setup_entry.mock_calls) == 1
assert mock_login.call_count == 1
@pytest.mark.parametrize(
("api_exception", "expected_error"),
[
(InvalidAuth(), "invalid_auth"),
(CannotConnect(), "cannot_connect"),
],
)
async def test_form_exceptions(
recorder_mock: Recorder, hass: HomeAssistant, api_exception, expected_error
) -> None:
"""Test we handle exceptions."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch(
"homeassistant.components.opower.config_flow.Opower.async_login",
side_effect=api_exception,
) as mock_login:
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"utility": "Pacific Gas and Electric Company (PG&E)",
"username": "test-username",
"password": "test-password",
},
)
assert result2["type"] == FlowResultType.FORM
assert result2["errors"] == {"base": expected_error}
assert mock_login.call_count == 1
async def test_form_already_configured(
recorder_mock: Recorder,
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test user input for config_entry that already exists."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch(
"homeassistant.components.opower.config_flow.Opower.async_login",
) as mock_login:
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"utility": "Pacific Gas and Electric Company (PG&E)",
"username": "test-username",
"password": "test-password",
},
)
assert result2["type"] == FlowResultType.ABORT
assert result2["reason"] == "already_configured"
assert mock_login.call_count == 0
async def test_form_not_already_configured(
recorder_mock: Recorder,
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test user input for config_entry different than the existing one."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch(
"homeassistant.components.opower.config_flow.Opower.async_login",
) as mock_login:
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"utility": "Pacific Gas and Electric Company (PG&E)",
"username": "test-username2",
"password": "test-password",
},
)
await hass.async_block_till_done()
assert result2["type"] == FlowResultType.CREATE_ENTRY
assert (
result2["title"] == "Pacific Gas and Electric Company (PG&E) (test-username2)"
)
assert result2["data"] == {
"utility": "Pacific Gas and Electric Company (PG&E)",
"username": "test-username2",
"password": "test-password",
}
assert len(mock_setup_entry.mock_calls) == 2
assert mock_login.call_count == 1
async def test_form_valid_reauth(
recorder_mock: Recorder,
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_unload_entry: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test that we can handle a valid reauth."""
mock_config_entry.state = ConfigEntryState.LOADED
mock_config_entry.async_start_reauth(hass)
await hass.async_block_till_done()
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1
result = flows[0]
assert result["step_id"] == "reauth_confirm"
assert result["context"]["source"] == "reauth"
assert result["context"]["title_placeholders"] == {"name": mock_config_entry.title}
with patch(
"homeassistant.components.opower.config_flow.Opower.async_login",
) as mock_login:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"username": "test-username", "password": "test-password2"},
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "reauth_successful"
await hass.async_block_till_done()
assert mock_config_entry.data == {
"utility": "Pacific Gas and Electric Company (PG&E)",
"username": "test-username",
"password": "test-password2",
}
assert len(mock_unload_entry.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
assert mock_login.call_count == 1
async def test_form_valid_reauth_with_mfa(
recorder_mock: Recorder,
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_unload_entry: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test that we can handle a valid reauth."""
hass.config_entries.async_update_entry(
mock_config_entry,
data={
**mock_config_entry.data,
# Requires MFA
"utility": "Consolidated Edison (ConEd)",
},
)
mock_config_entry.state = ConfigEntryState.LOADED
mock_config_entry.async_start_reauth(hass)
await hass.async_block_till_done()
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1
result = flows[0]
with patch(
"homeassistant.components.opower.config_flow.Opower.async_login",
) as mock_login:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"username": "test-username",
"password": "test-password2",
"totp_secret": "test-totp",
},
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "reauth_successful"
await hass.async_block_till_done()
assert mock_config_entry.data == {
"utility": "Consolidated Edison (ConEd)",
"username": "test-username",
"password": "test-password2",
"totp_secret": "test-totp",
}
assert len(mock_unload_entry.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
assert mock_login.call_count == 1