diff --git a/homeassistant/components/smlight/config_flow.py b/homeassistant/components/smlight/config_flow.py index 667e6e2884b..fcfc364d983 100644 --- a/homeassistant/components/smlight/config_flow.py +++ b/homeassistant/components/smlight/config_flow.py @@ -77,12 +77,14 @@ class SmlightConfigFlow(ConfigFlow, domain=DOMAIN): if user_input is not None: try: - info = await self.client.get_info() - - if info.model not in Devices: - return self.async_abort(reason="unsupported_device") - if not await self._async_check_auth_required(user_input): + info = await self.client.get_info() + self._host = str(info.device_ip) + self._device_name = str(info.hostname) + + if info.model not in Devices: + return self.async_abort(reason="unsupported_device") + return await self._async_complete_entry(user_input) except SmlightConnectionError: return self.async_abort(reason="cannot_connect") diff --git a/tests/components/smlight/conftest.py b/tests/components/smlight/conftest.py index 0b1bf24c19a..7a1b16f1d6b 100644 --- a/tests/components/smlight/conftest.py +++ b/tests/components/smlight/conftest.py @@ -3,6 +3,7 @@ from collections.abc import AsyncGenerator, Generator from unittest.mock import AsyncMock, MagicMock, patch +from pysmlight.exceptions import SmlightAuthError from pysmlight.sse import sseClient from pysmlight.web import CmdWrapper, Firmware, Info, Sensors import pytest @@ -81,9 +82,16 @@ def mock_smlight_client(request: pytest.FixtureRequest) -> Generator[MagicMock]: ): api = smlight_mock.return_value api.host = MOCK_HOST - api.get_info.return_value = Info.from_dict( - load_json_object_fixture("info.json", DOMAIN) - ) + + def get_info_side_effect(*args, **kwargs) -> Info: + """Return the info.""" + if api.check_auth_needed.return_value and not api.authenticate.called: + raise SmlightAuthError + + return Info.from_dict(load_json_object_fixture("info.json", DOMAIN)) + + api.get_info.side_effect = get_info_side_effect + api.get_sensors.return_value = Sensors.from_dict( load_json_object_fixture("sensors.json", DOMAIN) ) diff --git a/tests/components/smlight/test_button.py b/tests/components/smlight/test_button.py index 3721ee815e6..51e9414c00e 100644 --- a/tests/components/smlight/test_button.py +++ b/tests/components/smlight/test_button.py @@ -45,6 +45,7 @@ async def test_buttons( mock_smlight_client: MagicMock, ) -> None: """Test creation of button entities.""" + mock_smlight_client.get_info.side_effect = None mock_smlight_client.get_info.return_value = MOCK_ROUTER await setup_integration(hass, mock_config_entry) @@ -78,6 +79,7 @@ async def test_disabled_by_default_buttons( mock_smlight_client: MagicMock, ) -> None: """Test the disabled by default buttons.""" + mock_smlight_client.get_info.side_effect = None mock_smlight_client.get_info.return_value = MOCK_ROUTER await setup_integration(hass, mock_config_entry) @@ -96,7 +98,8 @@ async def test_remove_router_reconnect( mock_smlight_client: MagicMock, ) -> None: """Test removal of orphaned router reconnect button.""" - save_mock = mock_smlight_client.get_info.return_value + save_mock = mock_smlight_client.get_info.side_effect + mock_smlight_client.get_info.side_effect = None mock_smlight_client.get_info.return_value = MOCK_ROUTER mock_config_entry = await setup_integration(hass, mock_config_entry) @@ -106,7 +109,7 @@ async def test_remove_router_reconnect( assert len(entities) == 4 assert entities[3].unique_id == "aa:bb:cc:dd:ee:ff-reconnect_zigbee_router" - mock_smlight_client.get_info.return_value = save_mock + mock_smlight_client.get_info.side_effect = save_mock freezer.tick(SCAN_INTERVAL) async_fire_time_changed(hass) diff --git a/tests/components/smlight/test_config_flow.py b/tests/components/smlight/test_config_flow.py index a1c9c9d6945..c8933029ce6 100644 --- a/tests/components/smlight/test_config_flow.py +++ b/tests/components/smlight/test_config_flow.py @@ -66,6 +66,46 @@ async def test_user_flow(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> No assert len(mock_setup_entry.mock_calls) == 1 +async def test_user_flow_auth( + hass: HomeAssistant, mock_smlight_client: MagicMock, mock_setup_entry: AsyncMock +) -> None: + """Test the full manual user flow with authentication.""" + + mock_smlight_client.check_auth_needed.return_value = True + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: "slzb-06p7.local", + }, + ) + assert result2["type"] is FlowResultType.FORM + assert result2["step_id"] == "auth" + + result3 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: MOCK_USERNAME, + CONF_PASSWORD: MOCK_PASSWORD, + }, + ) + assert result3["type"] is FlowResultType.CREATE_ENTRY + assert result3["title"] == "SLZB-06p7" + assert result3["data"] == { + CONF_USERNAME: MOCK_USERNAME, + CONF_PASSWORD: MOCK_PASSWORD, + CONF_HOST: MOCK_HOST, + } + assert result3["context"]["unique_id"] == "aa:bb:cc:dd:ee:ff" + assert len(mock_setup_entry.mock_calls) == 1 + + async def test_zeroconf_flow( hass: HomeAssistant, mock_smlight_client: MagicMock, @@ -145,7 +185,7 @@ async def test_zeroconf_flow_auth( assert result3["type"] is FlowResultType.CREATE_ENTRY assert result3["context"]["source"] == "zeroconf" assert result3["context"]["unique_id"] == "aa:bb:cc:dd:ee:ff" - assert result3["title"] == "slzb-06" + assert result3["title"] == "SLZB-06p7" assert result3["data"] == { CONF_USERNAME: MOCK_USERNAME, CONF_PASSWORD: MOCK_PASSWORD, @@ -162,6 +202,7 @@ async def test_zeroconf_unsupported_abort( mock_smlight_client: MagicMock, ) -> None: """Test we abort zeroconf flow if device unsupported.""" + mock_smlight_client.get_info.side_effect = None mock_smlight_client.get_info.return_value = Info(model="SLZB-X") result = await hass.config_entries.flow.async_init( @@ -186,6 +227,7 @@ async def test_user_unsupported_abort( mock_smlight_client: MagicMock, ) -> None: """Test we abort user flow if unsupported device.""" + mock_smlight_client.get_info.side_effect = None mock_smlight_client.get_info.return_value = Info(model="SLZB-X") result = await hass.config_entries.flow.async_init( @@ -206,15 +248,13 @@ async def test_user_unsupported_abort( assert result2["reason"] == "unsupported_device" -async def test_user_unsupported_abort_auth( +async def test_user_unsupported_device_abort_auth( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_smlight_client: MagicMock, ) -> None: """Test we abort user flow if unsupported device (with auth).""" mock_smlight_client.check_auth_needed.return_value = True - mock_smlight_client.authenticate.side_effect = SmlightAuthError - mock_smlight_client.get_info.side_effect = SmlightAuthError result = await hass.config_entries.flow.async_init( DOMAIN, @@ -366,7 +406,7 @@ async def test_user_invalid_auth( } assert len(mock_setup_entry.mock_calls) == 1 - assert len(mock_smlight_client.get_info.mock_calls) == 4 + assert len(mock_smlight_client.get_info.mock_calls) == 3 async def test_user_cannot_connect( diff --git a/tests/components/smlight/test_init.py b/tests/components/smlight/test_init.py index 0acbab9f3a4..692255a53e6 100644 --- a/tests/components/smlight/test_init.py +++ b/tests/components/smlight/test_init.py @@ -165,6 +165,7 @@ async def test_device_legacy_firmware( """Test device setup for old firmware version that dont support required API.""" LEGACY_VERSION = "v0.9.9" mock_smlight_client.get_sensors.side_effect = SmlightError + mock_smlight_client.get_info.side_effect = None mock_smlight_client.get_info.return_value = Info( legacy_api=2, sw_version=LEGACY_VERSION, MAC="AA:BB:CC:DD:EE:FF" ) diff --git a/tests/components/smlight/test_update.py b/tests/components/smlight/test_update.py index 632f1b5f26b..86d19968910 100644 --- a/tests/components/smlight/test_update.py +++ b/tests/components/smlight/test_update.py @@ -132,6 +132,7 @@ async def test_update_firmware( event_function(MOCK_FIRMWARE_DONE) + mock_smlight_client.get_info.side_effect = None mock_smlight_client.get_info.return_value = Info( sw_version="v2.7.5", ) @@ -153,6 +154,7 @@ async def test_update_zigbee2_firmware( mock_smlight_client: MagicMock, ) -> None: """Test update of zigbee2 firmware where available.""" + mock_smlight_client.get_info.side_effect = None mock_smlight_client.get_info.return_value = Info.from_dict( load_json_object_fixture("info-MR1.json", DOMAIN) ) @@ -195,6 +197,7 @@ async def test_update_legacy_firmware_v2( mock_smlight_client: MagicMock, ) -> None: """Test firmware update for legacy v2 firmware.""" + mock_smlight_client.get_info.side_effect = None mock_smlight_client.get_info.return_value = Info( sw_version="v2.0.18", legacy_api=1, @@ -220,6 +223,7 @@ async def test_update_legacy_firmware_v2( event_function(MOCK_FIRMWARE_DONE) + mock_smlight_client.get_info.side_effect = None mock_smlight_client.get_info.return_value = Info( sw_version="v2.7.5", ) @@ -333,6 +337,7 @@ async def test_update_release_notes( hass_ws_client: WebSocketGenerator, ) -> None: """Test firmware release notes.""" + mock_smlight_client.get_info.side_effect = None mock_smlight_client.get_info.return_value = Info.from_dict( load_json_object_fixture("info-MR1.json", DOMAIN) )