Refactor tests for Shelly config flow (#143517)

* Add mock_setup_entry

* Add mock_setup

* Improve test_form_gen1_custom_port

* Improve test_form_errors_get_info

* Improve test_form_errors_test_connection

* Improve test_reconfigure_with_exception

* Improve test_form_auth_errors_test_connection_gen1

* Improve test_form_auth_errors_test_connection_gen2

* Cleaning

* Upate quality scale

* Always use result variable

* Remove unnecessary async_block_till_done
This commit is contained in:
Maciej Bieniek 2025-04-26 12:00:45 +02:00 committed by GitHub
parent f5d3495c62
commit f1b3b0c155
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 326 additions and 197 deletions

View File

@ -6,9 +6,7 @@ rules:
appropriate-polling: done appropriate-polling: done
brands: done brands: done
common-modules: done common-modules: done
config-flow-test-coverage: config-flow-test-coverage: done
status: todo
comment: make sure flows end with created entry or abort
config-flow: done config-flow: done
dependency-transparency: done dependency-transparency: done
docs-actions: docs-actions:

View File

@ -1,5 +1,6 @@
"""Test configuration for Shelly.""" """Test configuration for Shelly."""
from collections.abc import Generator
from copy import deepcopy from copy import deepcopy
from unittest.mock import AsyncMock, Mock, PropertyMock, patch from unittest.mock import AsyncMock, Mock, PropertyMock, patch
@ -690,3 +691,21 @@ async def mock_sleepy_rpc_device():
rpc_device_mock.return_value.mock_initialized = Mock(side_effect=initialized) rpc_device_mock.return_value.mock_initialized = Mock(side_effect=initialized)
yield rpc_device_mock.return_value yield rpc_device_mock.return_value
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock]:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.shelly.async_setup_entry", return_value=True
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture
def mock_setup() -> Generator[AsyncMock]:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.shelly.async_setup", return_value=True
) as mock_setup:
yield mock_setup

View File

@ -82,6 +82,8 @@ async def test_form(
port: int, port: int,
mock_block_device: Mock, mock_block_device: Mock,
mock_rpc_device: Mock, mock_rpc_device: Mock,
mock_setup_entry: AsyncMock,
mock_setup: AsyncMock,
) -> None: ) -> None:
"""Test we get the form.""" """Test we get the form."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -101,23 +103,15 @@ async def test_form(
"port": port, "port": port,
}, },
), ),
patch(
"homeassistant.components.shelly.async_setup", return_value=True
) as mock_setup,
patch(
"homeassistant.components.shelly.async_setup_entry",
return_value=True,
) as mock_setup_entry,
): ):
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: port}, {CONF_HOST: "1.1.1.1", CONF_PORT: port},
) )
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == "Test name" assert result["title"] == "Test name"
assert result2["data"] == { assert result["data"] == {
CONF_HOST: "1.1.1.1", CONF_HOST: "1.1.1.1",
CONF_PORT: port, CONF_PORT: port,
CONF_MODEL: model, CONF_MODEL: model,
@ -131,26 +125,19 @@ async def test_form(
async def test_user_flow_overrides_existing_discovery( async def test_user_flow_overrides_existing_discovery(
hass: HomeAssistant, hass: HomeAssistant,
mock_rpc_device: Mock, mock_rpc_device: Mock,
mock_setup_entry: AsyncMock,
mock_setup: AsyncMock,
) -> None: ) -> None:
"""Test setting up from the user flow when the devices is already discovered.""" """Test setting up from the user flow when the devices is already discovered."""
with ( with patch(
patch( "homeassistant.components.shelly.config_flow.get_info",
"homeassistant.components.shelly.config_flow.get_info", return_value={
return_value={ "mac": "AABBCCDDEEFF",
"mac": "AABBCCDDEEFF", "model": MODEL_PLUS_2PM,
"model": MODEL_PLUS_2PM, "auth": False,
"auth": False, "gen": 2,
"gen": 2, "port": 80,
"port": 80, },
},
),
patch(
"homeassistant.components.shelly.async_setup", return_value=True
) as mock_setup,
patch(
"homeassistant.components.shelly.async_setup_entry",
return_value=True,
) as mock_setup_entry,
): ):
discovery_result = await hass.config_entries.flow.async_init( discovery_result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
@ -172,22 +159,21 @@ async def test_user_flow_overrides_existing_discovery(
) )
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["errors"] == {} assert result["errors"] == {}
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80}, {CONF_HOST: "1.1.1.1", CONF_PORT: 80},
) )
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == "Test name" assert result["title"] == "Test name"
assert result2["data"] == { assert result["data"] == {
CONF_HOST: "1.1.1.1", CONF_HOST: "1.1.1.1",
CONF_PORT: 80, CONF_PORT: 80,
CONF_MODEL: MODEL_PLUS_2PM, CONF_MODEL: MODEL_PLUS_2PM,
CONF_SLEEP_PERIOD: 0, CONF_SLEEP_PERIOD: 0,
CONF_GEN: 2, CONF_GEN: 2,
} }
assert result2["context"]["unique_id"] == "AABBCCDDEEFF" assert result["context"]["unique_id"] == "AABBCCDDEEFF"
assert len(mock_setup.mock_calls) == 1 assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
assert len(hass.config_entries.async_entries(DOMAIN)) == 1 assert len(hass.config_entries.async_entries(DOMAIN)) == 1
@ -198,6 +184,8 @@ async def test_user_flow_overrides_existing_discovery(
async def test_form_gen1_custom_port( async def test_form_gen1_custom_port(
hass: HomeAssistant, hass: HomeAssistant,
mock_block_device: Mock, mock_block_device: Mock,
mock_setup_entry: AsyncMock,
mock_setup: AsyncMock,
) -> None: ) -> None:
"""Test we get the form.""" """Test we get the form."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -216,13 +204,35 @@ async def test_form_gen1_custom_port(
side_effect=CustomPortNotSupported, side_effect=CustomPortNotSupported,
), ),
): ):
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{"host": "1.1.1.1", "port": "1100"}, {CONF_HOST: "1.1.1.1", CONF_PORT: "1100"},
) )
assert result2["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result2["errors"]["base"] == "custom_port_not_supported" assert result["errors"]["base"] == "custom_port_not_supported"
with patch(
"homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "type": MODEL_1, "gen": 1},
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: DEFAULT_HTTP_PORT},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Test name"
assert result["data"] == {
CONF_HOST: "1.1.1.1",
CONF_PORT: DEFAULT_HTTP_PORT,
CONF_MODEL: MODEL_1,
CONF_SLEEP_PERIOD: 0,
CONF_GEN: 1,
}
assert result["context"]["unique_id"] == "test-mac"
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -256,6 +266,8 @@ async def test_form_auth(
username: str, username: str,
mock_block_device: Mock, mock_block_device: Mock,
mock_rpc_device: Mock, mock_rpc_device: Mock,
mock_setup_entry: AsyncMock,
mock_setup: AsyncMock,
) -> None: ) -> None:
"""Test manual configuration if auth is required.""" """Test manual configuration if auth is required."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -268,31 +280,21 @@ async def test_form_auth(
"homeassistant.components.shelly.config_flow.get_info", "homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "type": MODEL_1, "auth": True, "gen": gen}, return_value={"mac": "test-mac", "type": MODEL_1, "auth": True, "gen": gen},
): ):
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1"}, {CONF_HOST: "1.1.1.1"},
) )
assert result2["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["errors"] == {} assert result["errors"] == {}
with ( result = await hass.config_entries.flow.async_configure(
patch( result["flow_id"], user_input
"homeassistant.components.shelly.async_setup", return_value=True )
) as mock_setup,
patch(
"homeassistant.components.shelly.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"], user_input
)
await hass.async_block_till_done()
assert result3["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result3["title"] == "Test name" assert result["title"] == "Test name"
assert result3["data"] == { assert result["data"] == {
CONF_HOST: "1.1.1.1", CONF_HOST: "1.1.1.1",
CONF_PORT: DEFAULT_HTTP_PORT, CONF_PORT: DEFAULT_HTTP_PORT,
CONF_MODEL: model, CONF_MODEL: model,
@ -314,7 +316,12 @@ async def test_form_auth(
], ],
) )
async def test_form_errors_get_info( async def test_form_errors_get_info(
hass: HomeAssistant, exc: Exception, base_error: str hass: HomeAssistant,
mock_block_device: Mock,
mock_setup: AsyncMock,
mock_setup_entry: AsyncMock,
exc: Exception,
base_error: str,
) -> None: ) -> None:
"""Test we handle errors.""" """Test we handle errors."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -322,13 +329,35 @@ async def test_form_errors_get_info(
) )
with patch("homeassistant.components.shelly.config_flow.get_info", side_effect=exc): with patch("homeassistant.components.shelly.config_flow.get_info", side_effect=exc):
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1"}, {CONF_HOST: "1.1.1.1"},
) )
assert result2["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": base_error} assert result["errors"] == {"base": base_error}
with patch(
"homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "type": MODEL_1, "gen": 1},
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1"},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Test name"
assert result["data"] == {
CONF_HOST: "1.1.1.1",
CONF_PORT: DEFAULT_HTTP_PORT,
CONF_MODEL: MODEL_1,
CONF_SLEEP_PERIOD: 0,
CONF_GEN: 1,
}
assert result["context"]["unique_id"] == "test-mac"
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
async def test_form_missing_model_key( async def test_form_missing_model_key(
@ -343,13 +372,13 @@ async def test_form_missing_model_key(
"homeassistant.components.shelly.config_flow.get_info", "homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "auth": False, "gen": "2"}, return_value={"mac": "test-mac", "auth": False, "gen": "2"},
): ):
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1"}, {CONF_HOST: "1.1.1.1"},
) )
assert result2["type"] is FlowResultType.ABORT assert result["type"] is FlowResultType.ABORT
assert result2["reason"] == "firmware_not_fully_provisioned" assert result["reason"] == "firmware_not_fully_provisioned"
async def test_form_missing_model_key_auth_enabled( async def test_form_missing_model_key_auth_enabled(
@ -366,20 +395,20 @@ async def test_form_missing_model_key_auth_enabled(
"homeassistant.components.shelly.config_flow.get_info", "homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "auth": True, "gen": 2}, return_value={"mac": "test-mac", "auth": True, "gen": 2},
): ):
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1"}, {CONF_HOST: "1.1.1.1"},
) )
assert result2["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["errors"] == {} assert result["errors"] == {}
monkeypatch.setattr(mock_rpc_device, "shelly", {"gen": 2}) monkeypatch.setattr(mock_rpc_device, "shelly", {"gen": 2})
result3 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result2["flow_id"], {CONF_PASSWORD: "1234"} result["flow_id"], {CONF_PASSWORD: "1234"}
) )
assert result3["type"] is FlowResultType.ABORT assert result["type"] is FlowResultType.ABORT
assert result3["reason"] == "firmware_not_fully_provisioned" assert result["reason"] == "firmware_not_fully_provisioned"
async def test_form_missing_model_key_zeroconf( async def test_form_missing_model_key_zeroconf(
@ -398,6 +427,7 @@ async def test_form_missing_model_key_zeroconf(
data=DISCOVERY_INFO, data=DISCOVERY_INFO,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "firmware_not_fully_provisioned" assert result["reason"] == "firmware_not_fully_provisioned"
@ -411,7 +441,12 @@ async def test_form_missing_model_key_zeroconf(
], ],
) )
async def test_form_errors_test_connection( async def test_form_errors_test_connection(
hass: HomeAssistant, exc: Exception, base_error: str hass: HomeAssistant,
mock_block_device: Mock,
mock_setup_entry: AsyncMock,
mock_setup: AsyncMock,
exc: Exception,
base_error: str,
) -> None: ) -> None:
"""Test we handle errors.""" """Test we handle errors."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -427,13 +462,35 @@ async def test_form_errors_test_connection(
"aioshelly.block_device.BlockDevice.create", new=AsyncMock(side_effect=exc) "aioshelly.block_device.BlockDevice.create", new=AsyncMock(side_effect=exc)
), ),
): ):
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1"}, {CONF_HOST: "1.1.1.1"},
) )
assert result2["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": base_error} assert result["errors"] == {"base": base_error}
with patch(
"homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "auth": False},
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1"},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Test name"
assert result["data"] == {
CONF_HOST: "1.1.1.1",
CONF_PORT: DEFAULT_HTTP_PORT,
CONF_MODEL: MODEL_1,
CONF_SLEEP_PERIOD: 0,
CONF_GEN: 1,
}
assert result["context"]["unique_id"] == "test-mac"
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
async def test_form_already_configured(hass: HomeAssistant) -> None: async def test_form_already_configured(hass: HomeAssistant) -> None:
@ -452,20 +509,23 @@ async def test_form_already_configured(hass: HomeAssistant) -> None:
"homeassistant.components.shelly.config_flow.get_info", "homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "type": MODEL_1, "auth": False}, return_value={"mac": "test-mac", "type": MODEL_1, "auth": False},
): ):
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1"}, {CONF_HOST: "1.1.1.1"},
) )
assert result2["type"] is FlowResultType.ABORT assert result["type"] is FlowResultType.ABORT
assert result2["reason"] == "already_configured" assert result["reason"] == "already_configured"
# Test config entry got updated with latest IP # Test config entry got updated with latest IP
assert entry.data[CONF_HOST] == "1.1.1.1" assert entry.data[CONF_HOST] == "1.1.1.1"
async def test_user_setup_ignored_device( async def test_user_setup_ignored_device(
hass: HomeAssistant, mock_block_device: Mock hass: HomeAssistant,
mock_block_device: Mock,
mock_setup_entry: AsyncMock,
mock_setup: AsyncMock,
) -> None: ) -> None:
"""Test user can successfully setup an ignored device.""" """Test user can successfully setup an ignored device."""
@ -481,25 +541,16 @@ async def test_user_setup_ignored_device(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with ( with patch(
patch( "homeassistant.components.shelly.config_flow.get_info",
"homeassistant.components.shelly.config_flow.get_info", return_value={"mac": "test-mac", "type": MODEL_1, "auth": False},
return_value={"mac": "test-mac", "type": MODEL_1, "auth": False},
),
patch(
"homeassistant.components.shelly.async_setup", return_value=True
) as mock_setup,
patch(
"homeassistant.components.shelly.async_setup_entry",
return_value=True,
) as mock_setup_entry,
): ):
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1"}, {CONF_HOST: "1.1.1.1"},
) )
assert result2["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
# Test config entry got updated with latest IP # Test config entry got updated with latest IP
assert entry.data[CONF_HOST] == "1.1.1.1" assert entry.data[CONF_HOST] == "1.1.1.1"
@ -517,7 +568,12 @@ async def test_user_setup_ignored_device(
], ],
) )
async def test_form_auth_errors_test_connection_gen1( async def test_form_auth_errors_test_connection_gen1(
hass: HomeAssistant, exc: Exception, base_error: str hass: HomeAssistant,
mock_block_device: Mock,
mock_setup: AsyncMock,
mock_setup_entry: AsyncMock,
exc: Exception,
base_error: str,
) -> None: ) -> None:
"""Test we handle errors in Gen1 authenticated devices.""" """Test we handle errors in Gen1 authenticated devices."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -528,21 +584,45 @@ async def test_form_auth_errors_test_connection_gen1(
"homeassistant.components.shelly.config_flow.get_info", "homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "auth": True}, return_value={"mac": "test-mac", "auth": True},
): ):
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1"}, {CONF_HOST: "1.1.1.1"},
) )
with patch( with patch(
"aioshelly.block_device.BlockDevice.create", "aioshelly.block_device.BlockDevice.create",
new=AsyncMock(side_effect=exc), side_effect=exc,
): ):
result3 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result2["flow_id"], result["flow_id"],
{CONF_USERNAME: "test username", CONF_PASSWORD: "test password"}, {CONF_USERNAME: "test username", CONF_PASSWORD: "test password"},
) )
assert result3["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result3["errors"] == {"base": base_error} assert result["errors"] == {"base": base_error}
with patch(
"homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "auth": True},
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_USERNAME: "test username", CONF_PASSWORD: "test password"},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Test name"
assert result["data"] == {
CONF_HOST: "1.1.1.1",
CONF_PORT: DEFAULT_HTTP_PORT,
CONF_MODEL: MODEL_1,
CONF_SLEEP_PERIOD: 0,
CONF_GEN: 1,
CONF_USERNAME: "test username",
CONF_PASSWORD: "test password",
}
assert result["context"]["unique_id"] == "test-mac"
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -555,7 +635,12 @@ async def test_form_auth_errors_test_connection_gen1(
], ],
) )
async def test_form_auth_errors_test_connection_gen2( async def test_form_auth_errors_test_connection_gen2(
hass: HomeAssistant, exc: Exception, base_error: str hass: HomeAssistant,
mock_rpc_device: Mock,
mock_setup: AsyncMock,
mock_setup_entry: AsyncMock,
exc: Exception,
base_error: str,
) -> None: ) -> None:
"""Test we handle errors in Gen2 authenticated devices.""" """Test we handle errors in Gen2 authenticated devices."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -566,20 +651,44 @@ async def test_form_auth_errors_test_connection_gen2(
"homeassistant.components.shelly.config_flow.get_info", "homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "auth": True, "gen": 2}, return_value={"mac": "test-mac", "auth": True, "gen": 2},
): ):
result2 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1"}, {CONF_HOST: "1.1.1.1"},
) )
with patch( with patch(
"aioshelly.rpc_device.RpcDevice.create", "aioshelly.rpc_device.RpcDevice.create",
new=AsyncMock(side_effect=exc), side_effect=exc,
): ):
result3 = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result2["flow_id"], {CONF_PASSWORD: "test password"} result["flow_id"], {CONF_PASSWORD: "test password"}
) )
assert result3["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result3["errors"] == {"base": base_error} assert result["errors"] == {"base": base_error}
with patch(
"homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "auth": True, "gen": 2},
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_PASSWORD: "test password"},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Test name"
assert result["data"] == {
CONF_HOST: "1.1.1.1",
CONF_PORT: DEFAULT_HTTP_PORT,
CONF_MODEL: "SNSW-002P16EU",
CONF_SLEEP_PERIOD: 0,
CONF_GEN: 2,
CONF_USERNAME: "admin",
CONF_PASSWORD: "test password",
}
assert result["context"]["unique_id"] == "test-mac"
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -609,6 +718,8 @@ async def test_zeroconf(
get_info: dict[str, Any], get_info: dict[str, Any],
mock_block_device: Mock, mock_block_device: Mock,
mock_rpc_device: Mock, mock_rpc_device: Mock,
mock_setup_entry: AsyncMock,
mock_setup: AsyncMock,
) -> None: ) -> None:
"""Test we get the form.""" """Test we get the form."""
@ -629,24 +740,15 @@ async def test_zeroconf(
) )
assert context["title_placeholders"]["name"] == "shelly1pm-12345" assert context["title_placeholders"]["name"] == "shelly1pm-12345"
assert context["confirm_only"] is True assert context["confirm_only"] is True
with (
patch(
"homeassistant.components.shelly.async_setup", return_value=True
) as mock_setup,
patch(
"homeassistant.components.shelly.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY result = await hass.config_entries.flow.async_configure(
assert result2["title"] == "Test name" result["flow_id"],
assert result2["data"] == { {},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Test name"
assert result["data"] == {
CONF_HOST: "1.1.1.1", CONF_HOST: "1.1.1.1",
CONF_MODEL: model, CONF_MODEL: model,
CONF_SLEEP_PERIOD: 0, CONF_SLEEP_PERIOD: 0,
@ -657,7 +759,11 @@ async def test_zeroconf(
async def test_zeroconf_sleeping_device( async def test_zeroconf_sleeping_device(
hass: HomeAssistant, mock_block_device: Mock, monkeypatch: pytest.MonkeyPatch hass: HomeAssistant,
mock_block_device: Mock,
monkeypatch: pytest.MonkeyPatch,
mock_setup_entry: AsyncMock,
mock_setup: AsyncMock,
) -> None: ) -> None:
"""Test sleeping device configuration via zeroconf.""" """Test sleeping device configuration via zeroconf."""
monkeypatch.setitem( monkeypatch.setitem(
@ -687,24 +793,15 @@ async def test_zeroconf_sleeping_device(
if flow["flow_id"] == result["flow_id"] if flow["flow_id"] == result["flow_id"]
) )
assert context["title_placeholders"]["name"] == "shelly1pm-12345" assert context["title_placeholders"]["name"] == "shelly1pm-12345"
with (
patch(
"homeassistant.components.shelly.async_setup", return_value=True
) as mock_setup,
patch(
"homeassistant.components.shelly.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY result = await hass.config_entries.flow.async_configure(
assert result2["title"] == "Test name" result["flow_id"],
assert result2["data"] == { {},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Test name"
assert result["data"] == {
CONF_HOST: "1.1.1.1", CONF_HOST: "1.1.1.1",
CONF_MODEL: MODEL_1, CONF_MODEL: MODEL_1,
CONF_SLEEP_PERIOD: 600, CONF_SLEEP_PERIOD: 600,
@ -736,8 +833,9 @@ async def test_zeroconf_sleeping_device_error(hass: HomeAssistant) -> None:
data=DISCOVERY_INFO, data=DISCOVERY_INFO,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "cannot_connect" assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "cannot_connect"
async def test_options_flow_abort_setup_retry( async def test_options_flow_abort_setup_retry(
@ -789,8 +887,9 @@ async def test_zeroconf_already_configured(hass: HomeAssistant) -> None:
data=DISCOVERY_INFO, data=DISCOVERY_INFO,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured" assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
# Test config entry got updated with latest IP # Test config entry got updated with latest IP
assert entry.data[CONF_HOST] == "1.1.1.1" assert entry.data[CONF_HOST] == "1.1.1.1"
@ -816,8 +915,9 @@ async def test_zeroconf_ignored(hass: HomeAssistant) -> None:
data=DISCOVERY_INFO, data=DISCOVERY_INFO,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured" assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_zeroconf_with_wifi_ap_ip(hass: HomeAssistant) -> None: async def test_zeroconf_with_wifi_ap_ip(hass: HomeAssistant) -> None:
@ -839,8 +939,9 @@ async def test_zeroconf_with_wifi_ap_ip(hass: HomeAssistant) -> None:
), ),
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured" assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
# Test config entry was not updated with the wifi ap ip # Test config entry was not updated with the wifi ap ip
assert entry.data[CONF_HOST] == "2.2.2.2" assert entry.data[CONF_HOST] == "2.2.2.2"
@ -857,12 +958,16 @@ async def test_zeroconf_cannot_connect(hass: HomeAssistant) -> None:
data=DISCOVERY_INFO, data=DISCOVERY_INFO,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "cannot_connect" assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "cannot_connect"
async def test_zeroconf_require_auth( async def test_zeroconf_require_auth(
hass: HomeAssistant, mock_block_device: Mock hass: HomeAssistant,
mock_block_device: Mock,
mock_setup_entry: AsyncMock,
mock_setup: AsyncMock,
) -> None: ) -> None:
"""Test zeroconf if auth is required.""" """Test zeroconf if auth is required."""
@ -875,27 +980,18 @@ async def test_zeroconf_require_auth(
data=DISCOVERY_INFO, data=DISCOVERY_INFO,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
with ( assert result["type"] is FlowResultType.FORM
patch( assert result["errors"] == {}
"homeassistant.components.shelly.async_setup", return_value=True
) as mock_setup,
patch(
"homeassistant.components.shelly.async_setup_entry",
return_value=True,
) as mock_setup_entry,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_USERNAME: "test username", CONF_PASSWORD: "test password"},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY result = await hass.config_entries.flow.async_configure(
assert result2["title"] == "Test name" result["flow_id"],
assert result2["data"] == { {CONF_USERNAME: "test username", CONF_PASSWORD: "test password"},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Test name"
assert result["data"] == {
CONF_HOST: "1.1.1.1", CONF_HOST: "1.1.1.1",
CONF_PORT: DEFAULT_HTTP_PORT, CONF_PORT: DEFAULT_HTTP_PORT,
CONF_MODEL: MODEL_1, CONF_MODEL: MODEL_1,
@ -944,8 +1040,8 @@ async def test_reauth_successful(
user_input=user_input, user_input=user_input,
) )
assert result["type"] is FlowResultType.ABORT assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reauth_successful" assert result["reason"] == "reauth_successful"
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -1001,8 +1097,8 @@ async def test_reauth_unsuccessful(
user_input=user_input, user_input=user_input,
) )
assert result["type"] is FlowResultType.ABORT assert result["type"] is FlowResultType.ABORT
assert result["reason"] == abort_reason assert result["reason"] == abort_reason
async def test_reauth_get_info_error(hass: HomeAssistant) -> None: async def test_reauth_get_info_error(hass: HomeAssistant) -> None:
@ -1024,8 +1120,8 @@ async def test_reauth_get_info_error(hass: HomeAssistant) -> None:
user_input={CONF_PASSWORD: "test2 password"}, user_input={CONF_PASSWORD: "test2 password"},
) )
assert result["type"] is FlowResultType.ABORT assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reauth_unsuccessful" assert result["reason"] == "reauth_unsuccessful"
async def test_options_flow_disabled_gen_1( async def test_options_flow_disabled_gen_1(
@ -1105,7 +1201,6 @@ async def test_options_flow_ble(hass: HomeAssistant, mock_rpc_device: Mock) -> N
CONF_BLE_SCANNER_MODE: BLEScannerMode.DISABLED, CONF_BLE_SCANNER_MODE: BLEScannerMode.DISABLED,
}, },
) )
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"][CONF_BLE_SCANNER_MODE] is BLEScannerMode.DISABLED assert result["data"][CONF_BLE_SCANNER_MODE] is BLEScannerMode.DISABLED
@ -1121,7 +1216,6 @@ async def test_options_flow_ble(hass: HomeAssistant, mock_rpc_device: Mock) -> N
CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE, CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE,
}, },
) )
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"][CONF_BLE_SCANNER_MODE] is BLEScannerMode.ACTIVE assert result["data"][CONF_BLE_SCANNER_MODE] is BLEScannerMode.ACTIVE
@ -1137,7 +1231,6 @@ async def test_options_flow_ble(hass: HomeAssistant, mock_rpc_device: Mock) -> N
CONF_BLE_SCANNER_MODE: BLEScannerMode.PASSIVE, CONF_BLE_SCANNER_MODE: BLEScannerMode.PASSIVE,
}, },
) )
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"][CONF_BLE_SCANNER_MODE] is BLEScannerMode.PASSIVE assert result["data"][CONF_BLE_SCANNER_MODE] is BLEScannerMode.PASSIVE
@ -1173,8 +1266,9 @@ async def test_zeroconf_already_configured_triggers_refresh_mac_in_name(
data=DISCOVERY_INFO_WITH_MAC, data=DISCOVERY_INFO_WITH_MAC,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured" assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
monkeypatch.setattr(mock_rpc_device, "connected", False) monkeypatch.setattr(mock_rpc_device, "connected", False)
mock_rpc_device.mock_disconnected() mock_rpc_device.mock_disconnected()
@ -1213,8 +1307,9 @@ async def test_zeroconf_already_configured_triggers_refresh(
data=DISCOVERY_INFO, data=DISCOVERY_INFO,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured" assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
monkeypatch.setattr(mock_rpc_device, "connected", False) monkeypatch.setattr(mock_rpc_device, "connected", False)
mock_rpc_device.mock_disconnected() mock_rpc_device.mock_disconnected()
@ -1263,8 +1358,9 @@ async def test_zeroconf_sleeping_device_not_triggers_refresh(
data=DISCOVERY_INFO, data=DISCOVERY_INFO,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured" assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
monkeypatch.setattr(mock_rpc_device, "connected", False) monkeypatch.setattr(mock_rpc_device, "connected", False)
mock_rpc_device.mock_disconnected() mock_rpc_device.mock_disconnected()
@ -1317,8 +1413,9 @@ async def test_zeroconf_sleeping_device_attempts_configure(
data=DISCOVERY_INFO, data=DISCOVERY_INFO,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured" assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
assert mock_rpc_device.update_outbound_websocket.mock_calls == [] assert mock_rpc_device.update_outbound_websocket.mock_calls == []
@ -1382,8 +1479,9 @@ async def test_zeroconf_sleeping_device_attempts_configure_ws_disabled(
data=DISCOVERY_INFO, data=DISCOVERY_INFO,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured" assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
assert mock_rpc_device.update_outbound_websocket.mock_calls == [] assert mock_rpc_device.update_outbound_websocket.mock_calls == []
@ -1447,8 +1545,9 @@ async def test_zeroconf_sleeping_device_attempts_configure_no_url_available(
data=DISCOVERY_INFO, data=DISCOVERY_INFO,
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},
) )
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured" assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
assert mock_rpc_device.update_outbound_websocket.mock_calls == [] assert mock_rpc_device.update_outbound_websocket.mock_calls == []
@ -1493,8 +1592,8 @@ async def test_sleeping_device_gen2_with_new_firmware(
result["flow_id"], result["flow_id"],
{CONF_HOST: "1.1.1.1"}, {CONF_HOST: "1.1.1.1"},
) )
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == { assert result["data"] == {
CONF_HOST: "1.1.1.1", CONF_HOST: "1.1.1.1",
CONF_PORT: DEFAULT_HTTP_PORT, CONF_PORT: DEFAULT_HTTP_PORT,
@ -1608,6 +1707,19 @@ async def test_reconfigure_with_exception(
assert result["errors"] == {"base": base_error} assert result["errors"] == {"base": base_error}
with patch(
"homeassistant.components.shelly.config_flow.get_info",
return_value={"mac": "test-mac", "type": MODEL_1, "auth": False, "gen": 2},
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_HOST: "10.10.10.10", CONF_PORT: 99},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert entry.data == {CONF_HOST: "10.10.10.10", CONF_PORT: 99, CONF_GEN: 2}
async def test_zeroconf_rejects_ipv6(hass: HomeAssistant) -> None: async def test_zeroconf_rejects_ipv6(hass: HomeAssistant) -> None:
"""Test zeroconf discovery rejects ipv6.""" """Test zeroconf discovery rejects ipv6."""