"""Tests for the reauth flows (CalDAV and API modes).""" from unittest.mock import AsyncMock, patch import caldav.lib.error as caldav_error import pytest from homeassistant.config_entries import SOURCE_REAUTH from homeassistant.core import HomeAssistant from pytest_homeassistant_custom_component.common import MockConfigEntry from custom_components.room_cal_ha.const import ( CONF_API_BASE_URL, CONF_API_TOKEN, CONF_CALDAV_URL, CONF_PASSWORD, CONF_SYNC_MODE, CONF_USERNAME, DOMAIN, SYNC_MODE_API, SYNC_MODE_CALDAV, ) # --------------------------------------------------------------------------- # Fixtures # --------------------------------------------------------------------------- @pytest.fixture def caldav_entry(hass: HomeAssistant): entry = MockConfigEntry( domain=DOMAIN, data={CONF_USERNAME: "user", CONF_PASSWORD: "pass"}, options={CONF_SYNC_MODE: SYNC_MODE_CALDAV, CONF_CALDAV_URL: "https://cal.example.com"}, ) entry.add_to_hass(hass) return entry @pytest.fixture def api_entry(hass: HomeAssistant): entry = MockConfigEntry( domain=DOMAIN, data={CONF_API_TOKEN: "old-token"}, options={CONF_SYNC_MODE: SYNC_MODE_API, CONF_API_BASE_URL: "https://api.example.com"}, ) entry.add_to_hass(hass) return entry def _start_reauth(hass, entry): return hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_REAUTH, "entry_id": entry.entry_id}, data=entry.data, ) # --------------------------------------------------------------------------- # CalDAV reauth # --------------------------------------------------------------------------- async def test_caldav_reauth_shows_confirm_form(hass: HomeAssistant, caldav_entry): result = await _start_reauth(hass, caldav_entry) assert result["type"] == "form" assert result["step_id"] == "reauth_confirm" async def test_caldav_reauth_bad_credentials_shows_invalid_auth(hass: HomeAssistant, caldav_entry): result = await _start_reauth(hass, caldav_entry) with patch( "custom_components.room_cal_ha.config_flow._test_caldav_auth", side_effect=caldav_error.AuthorizationError, ): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_USERNAME: "user", CONF_PASSWORD: "wrong"}, ) assert result["type"] == "form" assert result["errors"]["base"] == "invalid_auth" async def test_caldav_reauth_connect_error_shows_cannot_connect(hass: HomeAssistant, caldav_entry): result = await _start_reauth(hass, caldav_entry) with patch( "custom_components.room_cal_ha.config_flow._test_caldav_auth", side_effect=Exception("timeout"), ): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_USERNAME: "user", CONF_PASSWORD: "pass"}, ) assert result["type"] == "form" assert result["errors"]["base"] == "cannot_connect" async def test_caldav_reauth_success_updates_credentials(hass: HomeAssistant, caldav_entry): result = await _start_reauth(hass, caldav_entry) with ( patch("custom_components.room_cal_ha.config_flow._test_caldav_auth", return_value=None), patch("custom_components.room_cal_ha.coordinator.CalDavBackend") as MockBackend, ): MockBackend.return_value.async_fetch = AsyncMock(return_value=(None, None)) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_USERNAME: "new_user", CONF_PASSWORD: "new_pass"}, ) # Drain the event loop so the triggered entry reload completes while patches are active. await hass.async_block_till_done() assert result["type"] == "abort" assert result["reason"] == "reauth_successful" assert caldav_entry.data[CONF_USERNAME] == "new_user" assert caldav_entry.data[CONF_PASSWORD] == "new_pass" # --------------------------------------------------------------------------- # API reauth # --------------------------------------------------------------------------- async def test_api_reauth_shows_api_form(hass: HomeAssistant, api_entry): result = await _start_reauth(hass, api_entry) assert result["type"] == "form" assert result["step_id"] == "reauth_api" async def test_api_reauth_bad_token_shows_invalid_auth(hass: HomeAssistant, api_entry): from custom_components.room_cal_ha.config_flow import _ApiAuthError result = await _start_reauth(hass, api_entry) with patch( "custom_components.room_cal_ha.config_flow._test_api_connection", new=AsyncMock(side_effect=_ApiAuthError), ): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_API_TOKEN: "bad-token"}, ) assert result["type"] == "form" assert result["errors"]["base"] == "invalid_auth" async def test_api_reauth_connect_error_shows_cannot_connect(hass: HomeAssistant, api_entry): result = await _start_reauth(hass, api_entry) with patch( "custom_components.room_cal_ha.config_flow._test_api_connection", new=AsyncMock(side_effect=Exception("timeout")), ): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_API_TOKEN: "tok"}, ) assert result["type"] == "form" assert result["errors"]["base"] == "cannot_connect" async def test_api_reauth_success_updates_token(hass: HomeAssistant, api_entry): result = await _start_reauth(hass, api_entry) with ( patch("custom_components.room_cal_ha.config_flow._test_api_connection", new=AsyncMock(return_value=None)), patch("custom_components.room_cal_ha.coordinator.RoomCalApiBackend") as MockBackend, ): instance = MockBackend.return_value instance.async_fetch = AsyncMock(return_value=(None, None)) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_API_TOKEN: "fresh-token"}, ) await hass.async_block_till_done() assert result["type"] == "abort" assert result["reason"] == "reauth_successful" assert api_entry.data[CONF_API_TOKEN] == "fresh-token"