test_config_flow.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. from unittest.mock import AsyncMock, patch
  2. import pytest
  3. import caldav.lib.error as caldav_error
  4. from homeassistant.config_entries import SOURCE_USER
  5. from homeassistant.core import HomeAssistant
  6. from custom_components.room_cal_ha.const import (
  7. CONF_API_BASE_URL,
  8. CONF_API_TOKEN,
  9. CONF_CALDAV_URL,
  10. CONF_CALENDAR_NAME,
  11. CONF_PASSWORD,
  12. CONF_REFRESH_INTERVAL,
  13. CONF_ROOM_NAME,
  14. CONF_SYNC_MODE,
  15. CONF_USERNAME,
  16. DEFAULT_REFRESH_INTERVAL,
  17. DOMAIN,
  18. SYNC_MODE_API,
  19. SYNC_MODE_CALDAV,
  20. SYNC_MODE_MANUAL,
  21. )
  22. # ---------------------------------------------------------------------------
  23. # Config flow — user step
  24. # ---------------------------------------------------------------------------
  25. async def test_user_step_shows_form(hass: HomeAssistant):
  26. result = await hass.config_entries.flow.async_init(
  27. DOMAIN, context={"source": SOURCE_USER}
  28. )
  29. assert result["type"] == "form"
  30. assert result["step_id"] == "user"
  31. async def test_user_step_creates_entry_with_room_name(hass: HomeAssistant):
  32. result = await hass.config_entries.flow.async_init(
  33. DOMAIN, context={"source": SOURCE_USER}
  34. )
  35. result = await hass.config_entries.flow.async_configure(
  36. result["flow_id"],
  37. user_input={CONF_ROOM_NAME: "Beach House"},
  38. )
  39. assert result["type"] == "create_entry"
  40. assert result["title"] == "Beach House"
  41. assert result["data"][CONF_ROOM_NAME] == "Beach House"
  42. # ---------------------------------------------------------------------------
  43. # Options flow — sync_mode step
  44. # ---------------------------------------------------------------------------
  45. async def test_options_sync_mode_manual(hass: HomeAssistant, mock_entry):
  46. mock_entry.add_to_hass(hass)
  47. result = await hass.config_entries.options.async_init(mock_entry.entry_id)
  48. assert result["type"] == "form"
  49. assert result["step_id"] == "sync_mode"
  50. result = await hass.config_entries.options.async_configure(
  51. result["flow_id"],
  52. user_input={CONF_SYNC_MODE: SYNC_MODE_MANUAL},
  53. )
  54. assert result["type"] == "create_entry"
  55. assert result["data"][CONF_SYNC_MODE] == SYNC_MODE_MANUAL
  56. async def test_options_sync_mode_caldav_proceeds_to_caldav_step(hass: HomeAssistant, mock_entry):
  57. mock_entry.add_to_hass(hass)
  58. result = await hass.config_entries.options.async_init(mock_entry.entry_id)
  59. result = await hass.config_entries.options.async_configure(
  60. result["flow_id"],
  61. user_input={CONF_SYNC_MODE: SYNC_MODE_CALDAV},
  62. )
  63. assert result["type"] == "form"
  64. assert result["step_id"] == "caldav"
  65. async def test_options_sync_mode_api_proceeds_to_api_step(hass: HomeAssistant, mock_entry):
  66. mock_entry.add_to_hass(hass)
  67. result = await hass.config_entries.options.async_init(mock_entry.entry_id)
  68. result = await hass.config_entries.options.async_configure(
  69. result["flow_id"],
  70. user_input={CONF_SYNC_MODE: SYNC_MODE_API},
  71. )
  72. assert result["type"] == "form"
  73. assert result["step_id"] == "api"
  74. # ---------------------------------------------------------------------------
  75. # Options flow — CalDAV step
  76. # ---------------------------------------------------------------------------
  77. async def test_options_caldav_saves_on_success(hass: HomeAssistant, mock_entry):
  78. mock_entry.add_to_hass(hass)
  79. result = await hass.config_entries.options.async_init(mock_entry.entry_id)
  80. await hass.config_entries.options.async_configure(
  81. result["flow_id"], user_input={CONF_SYNC_MODE: SYNC_MODE_CALDAV}
  82. )
  83. with patch("custom_components.room_cal_ha.config_flow._test_caldav_auth", return_value=None):
  84. result = await hass.config_entries.options.async_configure(
  85. result["flow_id"],
  86. user_input={
  87. CONF_CALDAV_URL: "https://cal.example.com",
  88. CONF_CALENDAR_NAME: "Room",
  89. CONF_USERNAME: "user",
  90. CONF_PASSWORD: "pass",
  91. CONF_REFRESH_INTERVAL: 30,
  92. },
  93. )
  94. assert result["type"] == "create_entry"
  95. assert result["data"][CONF_SYNC_MODE] == SYNC_MODE_CALDAV
  96. assert result["data"][CONF_CALDAV_URL] == "https://cal.example.com"
  97. assert result["data"][CONF_REFRESH_INTERVAL] == 30
  98. async def test_options_caldav_bad_credentials_shows_error(hass: HomeAssistant, mock_entry):
  99. mock_entry.add_to_hass(hass)
  100. result = await hass.config_entries.options.async_init(mock_entry.entry_id)
  101. await hass.config_entries.options.async_configure(
  102. result["flow_id"], user_input={CONF_SYNC_MODE: SYNC_MODE_CALDAV}
  103. )
  104. with patch(
  105. "custom_components.room_cal_ha.config_flow._test_caldav_auth",
  106. side_effect=caldav_error.AuthorizationError,
  107. ):
  108. result = await hass.config_entries.options.async_configure(
  109. result["flow_id"],
  110. user_input={
  111. CONF_CALDAV_URL: "https://cal.example.com",
  112. CONF_CALENDAR_NAME: "Room",
  113. CONF_USERNAME: "user",
  114. CONF_PASSWORD: "bad",
  115. CONF_REFRESH_INTERVAL: 15,
  116. },
  117. )
  118. assert result["type"] == "form"
  119. assert result["errors"]["base"] == "invalid_auth"
  120. async def test_options_caldav_connect_error_shows_error(hass: HomeAssistant, mock_entry):
  121. mock_entry.add_to_hass(hass)
  122. result = await hass.config_entries.options.async_init(mock_entry.entry_id)
  123. await hass.config_entries.options.async_configure(
  124. result["flow_id"], user_input={CONF_SYNC_MODE: SYNC_MODE_CALDAV}
  125. )
  126. with patch(
  127. "custom_components.room_cal_ha.config_flow._test_caldav_auth",
  128. side_effect=Exception("timeout"),
  129. ):
  130. result = await hass.config_entries.options.async_configure(
  131. result["flow_id"],
  132. user_input={
  133. CONF_CALDAV_URL: "https://cal.example.com",
  134. CONF_CALENDAR_NAME: "Room",
  135. CONF_USERNAME: "user",
  136. CONF_PASSWORD: "pass",
  137. CONF_REFRESH_INTERVAL: 15,
  138. },
  139. )
  140. assert result["type"] == "form"
  141. assert result["errors"]["base"] == "cannot_connect"
  142. # ---------------------------------------------------------------------------
  143. # Options flow — API step
  144. # ---------------------------------------------------------------------------
  145. async def test_options_api_saves_on_success(hass: HomeAssistant, mock_entry):
  146. mock_entry.add_to_hass(hass)
  147. result = await hass.config_entries.options.async_init(mock_entry.entry_id)
  148. await hass.config_entries.options.async_configure(
  149. result["flow_id"], user_input={CONF_SYNC_MODE: SYNC_MODE_API}
  150. )
  151. with patch(
  152. "custom_components.room_cal_ha.config_flow._test_api_connection",
  153. new=AsyncMock(return_value=None),
  154. ):
  155. result = await hass.config_entries.options.async_configure(
  156. result["flow_id"],
  157. user_input={
  158. CONF_API_BASE_URL: "https://api.example.com",
  159. CONF_API_TOKEN: "tok-secret",
  160. CONF_REFRESH_INTERVAL: 10,
  161. },
  162. )
  163. assert result["type"] == "create_entry"
  164. assert result["data"][CONF_SYNC_MODE] == SYNC_MODE_API
  165. assert result["data"][CONF_API_BASE_URL] == "https://api.example.com"
  166. assert result["data"][CONF_REFRESH_INTERVAL] == 10
  167. async def test_options_api_bad_token_shows_error(hass: HomeAssistant, mock_entry):
  168. from custom_components.room_cal_ha.config_flow import _ApiAuthError
  169. mock_entry.add_to_hass(hass)
  170. result = await hass.config_entries.options.async_init(mock_entry.entry_id)
  171. await hass.config_entries.options.async_configure(
  172. result["flow_id"], user_input={CONF_SYNC_MODE: SYNC_MODE_API}
  173. )
  174. with patch(
  175. "custom_components.room_cal_ha.config_flow._test_api_connection",
  176. new=AsyncMock(side_effect=_ApiAuthError),
  177. ):
  178. result = await hass.config_entries.options.async_configure(
  179. result["flow_id"],
  180. user_input={
  181. CONF_API_BASE_URL: "https://api.example.com",
  182. CONF_API_TOKEN: "bad-token",
  183. CONF_REFRESH_INTERVAL: 15,
  184. },
  185. )
  186. assert result["type"] == "form"
  187. assert result["errors"]["base"] == "invalid_auth"
  188. async def test_options_api_connect_error_shows_error(hass: HomeAssistant, mock_entry):
  189. mock_entry.add_to_hass(hass)
  190. result = await hass.config_entries.options.async_init(mock_entry.entry_id)
  191. await hass.config_entries.options.async_configure(
  192. result["flow_id"], user_input={CONF_SYNC_MODE: SYNC_MODE_API}
  193. )
  194. with patch(
  195. "custom_components.room_cal_ha.config_flow._test_api_connection",
  196. new=AsyncMock(side_effect=Exception("timeout")),
  197. ):
  198. result = await hass.config_entries.options.async_configure(
  199. result["flow_id"],
  200. user_input={
  201. CONF_API_BASE_URL: "https://api.example.com",
  202. CONF_API_TOKEN: "tok",
  203. CONF_REFRESH_INTERVAL: 15,
  204. },
  205. )
  206. assert result["type"] == "form"
  207. assert result["errors"]["base"] == "cannot_connect"