test_config_flow.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. """Tests for the config flow."""
  2. from unittest.mock import ANY, AsyncMock, MagicMock, patch
  3. import pytest
  4. import voluptuous as vol
  5. from homeassistant.const import CONF_HOST, CONF_NAME
  6. from homeassistant.data_entry_flow import FlowResultType
  7. from pytest_homeassistant_custom_component.common import MockConfigEntry
  8. from custom_components.tuya_local import (
  9. async_migrate_entry,
  10. config_flow,
  11. )
  12. from custom_components.tuya_local.const import (
  13. CONF_DEVICE_CID,
  14. CONF_DEVICE_ID,
  15. CONF_LOCAL_KEY,
  16. CONF_POLL_ONLY,
  17. CONF_PROTOCOL_VERSION,
  18. CONF_TYPE,
  19. DOMAIN,
  20. )
  21. # Designed to contain "special" characters that users constantly suspect.
  22. TESTKEY = ")<jO<@)'P1|kR$Kd"
  23. @pytest.fixture(autouse=True)
  24. def auto_enable_custom_integrations(enable_custom_integrations):
  25. yield
  26. @pytest.fixture(autouse=True)
  27. def prevent_task_creation():
  28. with patch(
  29. "custom_components.tuya_local.device.TuyaLocalDevice.register_entity",
  30. ):
  31. yield
  32. @pytest.fixture
  33. def bypass_setup():
  34. """Prevent actual setup of the integration after config flow."""
  35. with patch(
  36. "custom_components.tuya_local.async_setup_entry",
  37. return_value=True,
  38. ):
  39. yield
  40. @pytest.fixture
  41. def bypass_data_fetch():
  42. """Prevent actual data fetching from the device."""
  43. with patch(
  44. "tinytuya.Device.status",
  45. return_value={"1": True},
  46. ):
  47. yield
  48. @pytest.mark.asyncio
  49. async def test_init_entry(hass, bypass_data_fetch):
  50. """Test initialisation of the config flow."""
  51. entry = MockConfigEntry(
  52. domain=DOMAIN,
  53. version=11,
  54. title="test",
  55. data={
  56. CONF_DEVICE_ID: "deviceid",
  57. CONF_HOST: "hostname",
  58. CONF_LOCAL_KEY: TESTKEY,
  59. CONF_POLL_ONLY: False,
  60. CONF_PROTOCOL_VERSION: "auto",
  61. CONF_TYPE: "kogan_kahtp_heater",
  62. CONF_DEVICE_CID: None,
  63. },
  64. options={},
  65. )
  66. entry.add_to_hass(hass)
  67. await hass.config_entries.async_setup(entry.entry_id)
  68. await hass.async_block_till_done()
  69. assert hass.states.get("climate.test")
  70. assert hass.states.get("lock.test_child_lock")
  71. @pytest.mark.asyncio
  72. @patch("custom_components.tuya_local.setup_device")
  73. async def test_migrate_entry(mock_setup, hass):
  74. """Test migration from old entry format."""
  75. mock_device = MagicMock()
  76. mock_device.async_inferred_type = AsyncMock(return_value="goldair_gpph_heater")
  77. mock_setup.return_value = mock_device
  78. entry = MockConfigEntry(
  79. domain=DOMAIN,
  80. version=1,
  81. title="test",
  82. data={
  83. CONF_DEVICE_ID: "deviceid",
  84. CONF_HOST: "hostname",
  85. CONF_LOCAL_KEY: TESTKEY,
  86. CONF_TYPE: "auto",
  87. "climate": True,
  88. "child_lock": True,
  89. "display_light": True,
  90. },
  91. )
  92. entry.add_to_hass(hass)
  93. assert await async_migrate_entry(hass, entry)
  94. mock_device.async_inferred_type = AsyncMock(return_value=None)
  95. mock_device.reset_mock()
  96. entry = MockConfigEntry(
  97. domain=DOMAIN,
  98. version=1,
  99. title="test2",
  100. data={
  101. CONF_DEVICE_ID: "deviceid",
  102. CONF_HOST: "hostname",
  103. CONF_LOCAL_KEY: TESTKEY,
  104. CONF_TYPE: "unknown",
  105. "climate": False,
  106. },
  107. )
  108. entry.add_to_hass(hass)
  109. assert not await async_migrate_entry(hass, entry)
  110. mock_device.reset_mock()
  111. entry = MockConfigEntry(
  112. domain=DOMAIN,
  113. version=2,
  114. title="test3",
  115. data={
  116. CONF_DEVICE_ID: "deviceid",
  117. CONF_HOST: "hostname",
  118. CONF_LOCAL_KEY: TESTKEY,
  119. CONF_TYPE: "auto",
  120. },
  121. options={
  122. "climate": False,
  123. },
  124. )
  125. entry.add_to_hass(hass)
  126. assert not await async_migrate_entry(hass, entry)
  127. mock_device.async_inferred_type = AsyncMock(return_value="smartplugv1")
  128. mock_device.reset_mock()
  129. entry = MockConfigEntry(
  130. domain=DOMAIN,
  131. version=3,
  132. title="test4",
  133. data={
  134. CONF_DEVICE_ID: "deviceid",
  135. CONF_HOST: "hostname",
  136. CONF_LOCAL_KEY: TESTKEY,
  137. CONF_TYPE: "smartplugv1",
  138. },
  139. options={
  140. "switch": True,
  141. },
  142. )
  143. entry.add_to_hass(hass)
  144. assert await async_migrate_entry(hass, entry)
  145. mock_device.async_inferred_type = AsyncMock(return_value="smartplugv2")
  146. mock_device.reset_mock()
  147. entry = MockConfigEntry(
  148. domain=DOMAIN,
  149. version=3,
  150. title="test5",
  151. data={
  152. CONF_DEVICE_ID: "deviceid",
  153. CONF_HOST: "hostname",
  154. CONF_LOCAL_KEY: TESTKEY,
  155. CONF_TYPE: "smartplugv1",
  156. },
  157. options={
  158. "switch": True,
  159. },
  160. )
  161. entry.add_to_hass(hass)
  162. assert await async_migrate_entry(hass, entry)
  163. mock_device.async_inferred_type = AsyncMock(return_value="goldair_dehumidifier")
  164. mock_device.reset_mock()
  165. entry = MockConfigEntry(
  166. domain=DOMAIN,
  167. version=4,
  168. title="test6",
  169. data={
  170. CONF_DEVICE_ID: "deviceid",
  171. CONF_HOST: "hostname",
  172. CONF_LOCAL_KEY: TESTKEY,
  173. CONF_TYPE: "goldair_dehumidifier",
  174. },
  175. options={
  176. "humidifier": True,
  177. "fan": True,
  178. "light": True,
  179. "lock": False,
  180. "switch": True,
  181. },
  182. )
  183. entry.add_to_hass(hass)
  184. assert await async_migrate_entry(hass, entry)
  185. mock_device.async_inferred_type = AsyncMock(
  186. return_value="grid_connect_usb_double_power_point"
  187. )
  188. mock_device.reset_mock()
  189. entry = MockConfigEntry(
  190. domain=DOMAIN,
  191. version=6,
  192. title="test7",
  193. data={
  194. CONF_DEVICE_ID: "deviceid",
  195. CONF_HOST: "hostname",
  196. CONF_LOCAL_KEY: TESTKEY,
  197. CONF_TYPE: "grid_connect_usb_double_power_point",
  198. },
  199. options={
  200. "switch_main_switch": True,
  201. "switch_left_outlet": True,
  202. "switch_right_outlet": True,
  203. },
  204. )
  205. entry.add_to_hass(hass)
  206. assert await async_migrate_entry(hass, entry)
  207. @pytest.mark.asyncio
  208. async def test_flow_user_init(hass):
  209. """Test the initialisation of the form in the first page of the manual config flow path."""
  210. result = await hass.config_entries.flow.async_init(
  211. DOMAIN, context={"source": "local"}
  212. )
  213. expected = {
  214. "data_schema": ANY,
  215. "description_placeholders": None,
  216. "errors": {},
  217. "flow_id": ANY,
  218. "handler": DOMAIN,
  219. "step_id": "local",
  220. "type": "form",
  221. "last_step": ANY,
  222. "preview": ANY,
  223. }
  224. assert expected == result
  225. # Check the schema. Simple comparison does not work since they are not
  226. # the same object
  227. try:
  228. result["data_schema"](
  229. {CONF_DEVICE_ID: "test", CONF_LOCAL_KEY: TESTKEY, CONF_HOST: "test"}
  230. )
  231. except vol.MultipleInvalid:
  232. assert False
  233. try:
  234. result["data_schema"]({CONF_DEVICE_ID: "missing_some"})
  235. assert False
  236. except vol.MultipleInvalid:
  237. pass
  238. @pytest.mark.asyncio
  239. @patch("custom_components.tuya_local.config_flow.TuyaLocalDevice")
  240. async def test_async_test_connection_valid(mock_device, hass):
  241. """Test that device is returned when connection is valid."""
  242. mock_instance = AsyncMock()
  243. mock_instance.has_returned_state = True
  244. mock_instance.pause = MagicMock()
  245. mock_instance.resume = MagicMock()
  246. mock_device.return_value = mock_instance
  247. hass.data[DOMAIN] = {"deviceid": {"device": mock_instance}}
  248. device = await config_flow.async_test_connection(
  249. {
  250. CONF_DEVICE_ID: "deviceid",
  251. CONF_LOCAL_KEY: TESTKEY,
  252. CONF_HOST: "hostname",
  253. CONF_PROTOCOL_VERSION: "auto",
  254. },
  255. hass,
  256. )
  257. assert device == mock_instance
  258. mock_instance.pause.assert_called_once()
  259. mock_instance.resume.assert_called_once()
  260. @pytest.mark.asyncio
  261. @patch("custom_components.tuya_local.config_flow.TuyaLocalDevice")
  262. async def test_async_test_connection_for_subdevice_valid(mock_device, hass):
  263. """Test that subdevice is returned when connection is valid."""
  264. mock_instance = AsyncMock()
  265. mock_instance.has_returned_state = True
  266. mock_instance.pause = MagicMock()
  267. mock_instance.resume = MagicMock()
  268. mock_device.return_value = mock_instance
  269. hass.data[DOMAIN] = {"subdeviceid": {"device": mock_instance}}
  270. device = await config_flow.async_test_connection(
  271. {
  272. CONF_DEVICE_ID: "deviceid",
  273. CONF_LOCAL_KEY: TESTKEY,
  274. CONF_HOST: "hostname",
  275. CONF_PROTOCOL_VERSION: "auto",
  276. CONF_DEVICE_CID: "subdeviceid",
  277. },
  278. hass,
  279. )
  280. assert device == mock_instance
  281. mock_instance.pause.assert_called_once()
  282. mock_instance.resume.assert_called_once()
  283. @pytest.mark.asyncio
  284. @patch("custom_components.tuya_local.config_flow.TuyaLocalDevice")
  285. async def test_async_test_connection_invalid(mock_device, hass):
  286. """Test that None is returned when connection is invalid."""
  287. mock_instance = AsyncMock()
  288. mock_instance.has_returned_state = False
  289. mock_device.return_value = mock_instance
  290. device = await config_flow.async_test_connection(
  291. {
  292. CONF_DEVICE_ID: "deviceid",
  293. CONF_LOCAL_KEY: TESTKEY,
  294. CONF_HOST: "hostname",
  295. CONF_PROTOCOL_VERSION: "auto",
  296. },
  297. hass,
  298. )
  299. assert device is None
  300. @pytest.mark.asyncio
  301. @patch("custom_components.tuya_local.config_flow.async_test_connection")
  302. async def test_flow_user_init_invalid_config(mock_test, hass):
  303. """Test errors populated when config is invalid."""
  304. mock_test.return_value = None
  305. flow = await hass.config_entries.flow.async_init(
  306. DOMAIN, context={"source": "local"}
  307. )
  308. result = await hass.config_entries.flow.async_configure(
  309. flow["flow_id"],
  310. user_input={
  311. CONF_DEVICE_ID: "deviceid",
  312. CONF_HOST: "hostname",
  313. CONF_LOCAL_KEY: "badkey",
  314. CONF_PROTOCOL_VERSION: "auto",
  315. CONF_POLL_ONLY: False,
  316. },
  317. )
  318. assert {"base": "connection"} == result["errors"]
  319. def setup_device_mock(mock, failure=False, type="test"):
  320. mock_type = MagicMock()
  321. mock_type.legacy_type = type
  322. mock_type.config_type = type
  323. mock_type.match_quality.return_value = 100
  324. mock.async_possible_types = AsyncMock(
  325. return_value=[mock_type] if not failure else []
  326. )
  327. @pytest.mark.asyncio
  328. @patch("custom_components.tuya_local.config_flow.async_test_connection")
  329. async def test_flow_user_init_data_valid(mock_test, hass):
  330. """Test we advance to the next step when connection config is valid."""
  331. mock_device = MagicMock()
  332. setup_device_mock(mock_device)
  333. mock_test.return_value = mock_device
  334. flow = await hass.config_entries.flow.async_init(
  335. DOMAIN, context={"source": "local"}
  336. )
  337. result = await hass.config_entries.flow.async_configure(
  338. flow["flow_id"],
  339. user_input={
  340. CONF_DEVICE_ID: "deviceid",
  341. CONF_HOST: "hostname",
  342. CONF_LOCAL_KEY: TESTKEY,
  343. },
  344. )
  345. assert "form" == result["type"]
  346. assert "select_type" == result["step_id"]
  347. @pytest.mark.asyncio
  348. @patch.object(config_flow.ConfigFlowHandler, "device")
  349. async def test_flow_select_type_init(mock_device, hass):
  350. """Test the initialisation of the form in the 2nd step of the config flow."""
  351. setup_device_mock(mock_device)
  352. result = await hass.config_entries.flow.async_init(
  353. DOMAIN, context={"source": "select_type"}
  354. )
  355. expected = {
  356. "data_schema": ANY,
  357. "description_placeholders": None,
  358. "errors": None,
  359. "flow_id": ANY,
  360. "handler": DOMAIN,
  361. "step_id": "select_type",
  362. "type": "form",
  363. "last_step": ANY,
  364. "preview": ANY,
  365. }
  366. assert expected == result
  367. # Check the schema. Simple comparison does not work since they are not
  368. # the same object
  369. try:
  370. result["data_schema"]({CONF_TYPE: "test"})
  371. except vol.MultipleInvalid:
  372. assert False
  373. try:
  374. result["data_schema"]({CONF_TYPE: "not_test"})
  375. assert False
  376. except vol.MultipleInvalid:
  377. pass
  378. @pytest.mark.asyncio
  379. @patch.object(config_flow.ConfigFlowHandler, "device")
  380. async def test_flow_select_type_aborts_when_no_match(mock_device, hass):
  381. """Test the flow aborts when an unsupported device is used."""
  382. setup_device_mock(mock_device, failure=True)
  383. result = await hass.config_entries.flow.async_init(
  384. DOMAIN, context={"source": "select_type"}
  385. )
  386. assert result["type"] == "abort"
  387. assert result["reason"] == "not_supported"
  388. @pytest.mark.asyncio
  389. @patch.object(config_flow.ConfigFlowHandler, "device")
  390. async def test_flow_select_type_data_valid(mock_device, hass):
  391. """Test the flow continues when valid data is supplied."""
  392. setup_device_mock(mock_device, type="smartplugv1")
  393. flow = await hass.config_entries.flow.async_init(
  394. DOMAIN, context={"source": "select_type"}
  395. )
  396. result = await hass.config_entries.flow.async_configure(
  397. flow["flow_id"],
  398. user_input={CONF_TYPE: "smartplugv1"},
  399. )
  400. assert "form" == result["type"]
  401. assert "choose_entities" == result["step_id"]
  402. @pytest.mark.asyncio
  403. async def test_flow_choose_entities_init(hass):
  404. """Test the initialisation of the form in the 3rd step of the config flow."""
  405. with patch.dict(config_flow.ConfigFlowHandler.data, {CONF_TYPE: "smartplugv1"}):
  406. result = await hass.config_entries.flow.async_init(
  407. DOMAIN, context={"source": "choose_entities"}
  408. )
  409. expected = {
  410. "data_schema": ANY,
  411. "description_placeholders": None,
  412. "errors": None,
  413. "flow_id": ANY,
  414. "handler": DOMAIN,
  415. "step_id": "choose_entities",
  416. "type": "form",
  417. "last_step": ANY,
  418. "preview": ANY,
  419. }
  420. assert expected == result
  421. # Check the schema. Simple comparison does not work since they are not
  422. # the same object
  423. try:
  424. result["data_schema"]({CONF_NAME: "test"})
  425. except vol.MultipleInvalid:
  426. assert False
  427. try:
  428. result["data_schema"]({"climate": True})
  429. assert False
  430. except vol.MultipleInvalid:
  431. pass
  432. @pytest.mark.asyncio
  433. async def test_flow_choose_entities_creates_config_entry(hass, bypass_setup):
  434. """Test the flow ends when data is valid."""
  435. with patch.dict(
  436. config_flow.ConfigFlowHandler.data,
  437. {
  438. CONF_DEVICE_ID: "deviceid",
  439. CONF_LOCAL_KEY: TESTKEY,
  440. CONF_HOST: "hostname",
  441. CONF_POLL_ONLY: False,
  442. CONF_PROTOCOL_VERSION: "auto",
  443. CONF_TYPE: "kogan_kahtp_heater",
  444. CONF_DEVICE_CID: None,
  445. },
  446. ):
  447. flow = await hass.config_entries.flow.async_init(
  448. DOMAIN, context={"source": "choose_entities"}
  449. )
  450. result = await hass.config_entries.flow.async_configure(
  451. flow["flow_id"],
  452. user_input={
  453. CONF_NAME: "test",
  454. },
  455. )
  456. expected = {
  457. "version": 13,
  458. "minor_version": ANY,
  459. "context": {"source": "choose_entities"},
  460. "type": FlowResultType.CREATE_ENTRY,
  461. "flow_id": ANY,
  462. "handler": DOMAIN,
  463. "title": "test",
  464. "description": None,
  465. "description_placeholders": None,
  466. "result": ANY,
  467. "subentries": (),
  468. "options": {},
  469. "data": {
  470. CONF_DEVICE_ID: "deviceid",
  471. CONF_HOST: "hostname",
  472. CONF_LOCAL_KEY: TESTKEY,
  473. CONF_POLL_ONLY: False,
  474. CONF_PROTOCOL_VERSION: "auto",
  475. CONF_TYPE: "kogan_kahtp_heater",
  476. CONF_DEVICE_CID: None,
  477. },
  478. }
  479. assert expected == result
  480. @pytest.mark.asyncio
  481. async def test_options_flow_init(hass, bypass_data_fetch):
  482. """Test config flow options."""
  483. config_entry = MockConfigEntry(
  484. domain=DOMAIN,
  485. version=13,
  486. unique_id="uniqueid",
  487. data={
  488. CONF_DEVICE_ID: "deviceid",
  489. CONF_HOST: "hostname",
  490. CONF_LOCAL_KEY: TESTKEY,
  491. CONF_NAME: "test",
  492. CONF_POLL_ONLY: False,
  493. CONF_PROTOCOL_VERSION: "auto",
  494. CONF_TYPE: "smartplugv1",
  495. CONF_DEVICE_CID: "",
  496. },
  497. )
  498. config_entry.add_to_hass(hass)
  499. assert await hass.config_entries.async_setup(config_entry.entry_id)
  500. await hass.async_block_till_done()
  501. # show initial form
  502. result = await hass.config_entries.options.async_init(config_entry.entry_id)
  503. assert "form" == result["type"]
  504. assert "user" == result["step_id"]
  505. assert {} == result["errors"]
  506. assert result["data_schema"](
  507. {
  508. CONF_HOST: "hostname",
  509. CONF_LOCAL_KEY: TESTKEY,
  510. }
  511. )
  512. @pytest.mark.asyncio
  513. @patch("custom_components.tuya_local.config_flow.async_test_connection")
  514. async def test_options_flow_modifies_config(mock_test, hass, bypass_setup):
  515. mock_device = MagicMock()
  516. mock_test.return_value = mock_device
  517. config_entry = MockConfigEntry(
  518. domain=DOMAIN,
  519. version=13,
  520. unique_id="uniqueid",
  521. data={
  522. CONF_DEVICE_ID: "deviceid",
  523. CONF_HOST: "hostname",
  524. CONF_LOCAL_KEY: TESTKEY,
  525. CONF_NAME: "test",
  526. CONF_POLL_ONLY: False,
  527. CONF_PROTOCOL_VERSION: "auto",
  528. CONF_TYPE: "ble_pt216_temp_humidity",
  529. CONF_DEVICE_CID: "subdeviceid",
  530. },
  531. )
  532. config_entry.add_to_hass(hass)
  533. assert await hass.config_entries.async_setup(config_entry.entry_id)
  534. await hass.async_block_till_done()
  535. # show initial form
  536. form = await hass.config_entries.options.async_init(config_entry.entry_id)
  537. # submit updated config
  538. result = await hass.config_entries.options.async_configure(
  539. form["flow_id"],
  540. user_input={
  541. CONF_HOST: "new_hostname",
  542. CONF_LOCAL_KEY: "new_key",
  543. CONF_POLL_ONLY: False,
  544. CONF_PROTOCOL_VERSION: 3.3,
  545. },
  546. )
  547. expected = {
  548. CONF_HOST: "new_hostname",
  549. CONF_LOCAL_KEY: "new_key",
  550. CONF_POLL_ONLY: False,
  551. CONF_PROTOCOL_VERSION: 3.3,
  552. }
  553. assert "create_entry" == result["type"]
  554. assert "" == result["title"]
  555. assert result["result"] is True
  556. assert expected == result["data"]
  557. @pytest.mark.asyncio
  558. @patch("custom_components.tuya_local.config_flow.async_test_connection")
  559. async def test_options_flow_fails_when_connection_fails(
  560. mock_test, hass, bypass_data_fetch
  561. ):
  562. mock_test.return_value = None
  563. config_entry = MockConfigEntry(
  564. domain=DOMAIN,
  565. version=13,
  566. unique_id="uniqueid",
  567. data={
  568. CONF_DEVICE_ID: "deviceid",
  569. CONF_HOST: "hostname",
  570. CONF_LOCAL_KEY: TESTKEY,
  571. CONF_NAME: "test",
  572. CONF_POLL_ONLY: False,
  573. CONF_PROTOCOL_VERSION: "auto",
  574. CONF_TYPE: "smartplugv1",
  575. CONF_DEVICE_CID: "",
  576. },
  577. )
  578. config_entry.add_to_hass(hass)
  579. assert await hass.config_entries.async_setup(config_entry.entry_id)
  580. await hass.async_block_till_done()
  581. # show initial form
  582. form = await hass.config_entries.options.async_init(config_entry.entry_id)
  583. # submit updated config
  584. result = await hass.config_entries.options.async_configure(
  585. form["flow_id"],
  586. user_input={
  587. CONF_HOST: "new_hostname",
  588. CONF_LOCAL_KEY: "new_key",
  589. },
  590. )
  591. assert "form" == result["type"]
  592. assert "user" == result["step_id"]
  593. assert {"base": "connection"} == result["errors"]
  594. @pytest.mark.asyncio
  595. @patch("custom_components.tuya_local.config_flow.async_test_connection")
  596. async def test_options_flow_fails_when_config_is_missing(mock_test, hass):
  597. mock_device = MagicMock()
  598. mock_test.return_value = mock_device
  599. config_entry = MockConfigEntry(
  600. domain=DOMAIN,
  601. version=13,
  602. unique_id="uniqueid",
  603. data={
  604. CONF_DEVICE_ID: "deviceid",
  605. CONF_HOST: "hostname",
  606. CONF_LOCAL_KEY: TESTKEY,
  607. CONF_NAME: "test",
  608. CONF_POLL_ONLY: False,
  609. CONF_PROTOCOL_VERSION: "auto",
  610. CONF_TYPE: "non_existing",
  611. },
  612. )
  613. config_entry.add_to_hass(hass)
  614. await hass.config_entries.async_setup(config_entry.entry_id)
  615. await hass.async_block_till_done()
  616. # show initial form
  617. result = await hass.config_entries.options.async_init(config_entry.entry_id)
  618. assert result["type"] == "abort"
  619. assert result["reason"] == "not_supported"