Skip to content

Commit f8c76f4

Browse files
RaHehlbdraco
andauthored
Add session clearing on config entry removal for UniFi Protect integration (home-assistant#157360)
Co-authored-by: J. Nick Koston <[email protected]>
1 parent 21d914c commit f8c76f4

File tree

2 files changed

+99
-1
lines changed

2 files changed

+99
-1
lines changed

homeassistant/components/unifiprotect/__init__.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# diagnostics module will not be imported in the executor.
1616
from uiprotect.test_util.anonymize import anonymize_data # noqa: F401
1717

18-
from homeassistant.config_entries import ConfigEntry
18+
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
1919
from homeassistant.const import CONF_API_KEY, EVENT_HOMEASSISTANT_STOP
2020
from homeassistant.core import HomeAssistant
2121
from homeassistant.exceptions import (
@@ -172,6 +172,24 @@ async def async_unload_entry(hass: HomeAssistant, entry: UFPConfigEntry) -> bool
172172
return unload_ok
173173

174174

175+
async def async_remove_entry(hass: HomeAssistant, entry: UFPConfigEntry) -> None:
176+
"""Handle removal of a config entry."""
177+
# Clear the stored session credentials when the integration is removed
178+
if entry.state is ConfigEntryState.LOADED:
179+
# Integration is loaded, use the existing API client
180+
try:
181+
await entry.runtime_data.api.clear_session()
182+
except Exception as err: # noqa: BLE001
183+
_LOGGER.warning("Failed to clear session credentials: %s", err)
184+
else:
185+
# Integration is not loaded, create temporary client to clear session
186+
protect = async_create_api_client(hass, entry)
187+
try:
188+
await protect.clear_session()
189+
except Exception as err: # noqa: BLE001
190+
_LOGGER.warning("Failed to clear session credentials: %s", err)
191+
192+
175193
async def async_remove_config_entry_device(
176194
hass: HomeAssistant, config_entry: UFPConfigEntry, device_entry: dr.DeviceEntry
177195
) -> bool:

tests/components/unifiprotect/test_init.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,86 @@ async def test_unload(hass: HomeAssistant, ufp: MockUFPFixture, light: Light) ->
113113
assert ufp.api.async_disconnect_ws.called
114114

115115

116+
async def test_remove_entry(hass: HomeAssistant, ufp: MockUFPFixture) -> None:
117+
"""Test removal of unifiprotect entry clears session."""
118+
119+
await init_entry(hass, ufp, [])
120+
assert ufp.entry.state is ConfigEntryState.LOADED
121+
122+
# Mock clear_session method
123+
ufp.api.clear_session = AsyncMock()
124+
125+
await hass.config_entries.async_remove(ufp.entry.entry_id)
126+
await hass.async_block_till_done()
127+
128+
# Verify clear_session was called
129+
assert ufp.api.clear_session.called
130+
131+
132+
async def test_remove_entry_not_loaded(
133+
hass: HomeAssistant, ufp: MockUFPFixture
134+
) -> None:
135+
"""Test removal of unloaded unifiprotect entry still clears session."""
136+
137+
# Add entry but don't load it
138+
ufp.entry.add_to_hass(hass)
139+
140+
# Mock clear_session method
141+
ufp.api.clear_session = AsyncMock()
142+
143+
with patch(
144+
"homeassistant.components.unifiprotect.async_create_api_client",
145+
return_value=ufp.api,
146+
):
147+
await hass.config_entries.async_remove(ufp.entry.entry_id)
148+
await hass.async_block_till_done()
149+
150+
# Verify clear_session was called even though entry wasn't loaded
151+
assert ufp.api.clear_session.called
152+
153+
154+
async def test_remove_entry_clear_session_fails(
155+
hass: HomeAssistant, ufp: MockUFPFixture
156+
) -> None:
157+
"""Test removal succeeds even when clear_session fails."""
158+
await init_entry(hass, ufp, [])
159+
assert ufp.entry.state is ConfigEntryState.LOADED
160+
161+
# Mock clear_session to raise an exception
162+
ufp.api.clear_session = AsyncMock(side_effect=PermissionError("Permission denied"))
163+
164+
# Should not raise - removal should succeed
165+
await hass.config_entries.async_remove(ufp.entry.entry_id)
166+
await hass.async_block_till_done()
167+
168+
# Verify clear_session was attempted
169+
assert ufp.api.clear_session.called
170+
171+
172+
async def test_remove_entry_not_loaded_clear_session_fails(
173+
hass: HomeAssistant, ufp: MockUFPFixture
174+
) -> None:
175+
"""Test removal succeeds when not loaded and clear_session fails."""
176+
# Don't initialize the integration - entry is not loaded
177+
ufp.entry.add_to_hass(hass)
178+
assert ufp.entry.state is not ConfigEntryState.LOADED
179+
180+
# Mock clear_session to raise an exception for the temporary client
181+
with patch(
182+
"homeassistant.components.unifiprotect.async_create_api_client"
183+
) as mock_create:
184+
mock_api = Mock(spec=ProtectApiClient)
185+
mock_api.clear_session = AsyncMock(side_effect=OSError("Read-only file system"))
186+
mock_create.return_value = mock_api
187+
188+
# Should not raise - removal should succeed
189+
await hass.config_entries.async_remove(ufp.entry.entry_id)
190+
await hass.async_block_till_done()
191+
192+
# Verify clear_session was attempted
193+
assert mock_api.clear_session.called
194+
195+
116196
async def test_setup_too_old(
117197
hass: HomeAssistant, ufp: MockUFPFixture, old_nvr: NVR
118198
) -> None:

0 commit comments

Comments
 (0)