mirror of
https://github.com/home-assistant/core.git
synced 2026-09-28 02:18:10 -04:00
Add Config Flow to Modem Caller ID integration (#46677)
* Add phone_modem integration * Use original domain * Add init tests for Modem Caller ID * Clean up tests * Clean up tests * apply suggestions * Fix tests * Make only one instance possible * Allow more than 1 device and remove hangup service * simplify already configured * Update sensor.py * Update config_flow.py * Fix manifest * More cleanup * Fix tests * Ue target * Clean up sensor.py * Minor tweaks * Close modem on restart and unload * Update requirements * fix tests * Bump phone_modem * rework * add typing * use async_setup_platform * typing * tweak * cleanup * fix init * preserve original name * remove callback line * use list of serial devices on host * tweak * rework * Rework for usb dicsovery * Update requirements_test_all.txt * Update config_flow.py * tweaks * tweak * move api out of try statement * suggested tweaks * clean up * typing * tweak * tweak * async name the service
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
"""Tests for the Modem Caller ID integration."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from phone_modem import DEFAULT_PORT
|
||||
|
||||
from homeassistant.const import CONF_DEVICE
|
||||
|
||||
CONF_DATA = {CONF_DEVICE: DEFAULT_PORT}
|
||||
|
||||
IMPORT_DATA = {"sensor": {"platform": "modem_callerid"}}
|
||||
|
||||
|
||||
def _patch_init_modem(mocked_modem):
|
||||
return patch(
|
||||
"homeassistant.components.modem_callerid.PhoneModem",
|
||||
return_value=mocked_modem,
|
||||
)
|
||||
|
||||
|
||||
def _patch_config_flow_modem(mocked_modem):
|
||||
return patch(
|
||||
"homeassistant.components.modem_callerid.config_flow.PhoneModem",
|
||||
return_value=mocked_modem,
|
||||
)
|
||||
@@ -0,0 +1,204 @@
|
||||
"""Test Modem Caller ID config flow."""
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import phone_modem
|
||||
import serial.tools.list_ports
|
||||
|
||||
from homeassistant.components import usb
|
||||
from homeassistant.components.modem_callerid.const import DEFAULT_NAME, DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USB, SOURCE_USER
|
||||
from homeassistant.const import CONF_DEVICE, CONF_SOURCE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import (
|
||||
RESULT_TYPE_ABORT,
|
||||
RESULT_TYPE_CREATE_ENTRY,
|
||||
RESULT_TYPE_FORM,
|
||||
)
|
||||
|
||||
from . import CONF_DATA, IMPORT_DATA, _patch_config_flow_modem
|
||||
|
||||
DISCOVERY_INFO = {
|
||||
"device": phone_modem.DEFAULT_PORT,
|
||||
"pid": "1340",
|
||||
"vid": "0572",
|
||||
"serial_number": "1234",
|
||||
"description": "modem",
|
||||
"manufacturer": "Connexant",
|
||||
}
|
||||
|
||||
|
||||
def _patch_setup():
|
||||
return patch(
|
||||
"homeassistant.components.modem_callerid.async_setup_entry",
|
||||
return_value=True,
|
||||
)
|
||||
|
||||
|
||||
def com_port():
|
||||
"""Mock of a serial port."""
|
||||
port = serial.tools.list_ports_common.ListPortInfo(phone_modem.DEFAULT_PORT)
|
||||
port.serial_number = "1234"
|
||||
port.manufacturer = "Virtual serial port"
|
||||
port.device = phone_modem.DEFAULT_PORT
|
||||
port.description = "Some serial port"
|
||||
|
||||
return port
|
||||
|
||||
|
||||
@patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()]))
|
||||
async def test_flow_usb(hass: HomeAssistant):
|
||||
"""Test usb discovery flow."""
|
||||
port = com_port()
|
||||
with _patch_config_flow_modem(AsyncMock()), _patch_setup():
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={CONF_SOURCE: SOURCE_USB},
|
||||
data=DISCOVERY_INFO,
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "usb_confirm"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_DEVICE: phone_modem.DEFAULT_PORT},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["data"] == {CONF_DEVICE: port.device}
|
||||
|
||||
|
||||
@patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()]))
|
||||
async def test_flow_usb_cannot_connect(hass: HomeAssistant):
|
||||
"""Test usb flow connection error."""
|
||||
with _patch_config_flow_modem(AsyncMock()) as modemmock:
|
||||
modemmock.side_effect = phone_modem.exceptions.SerialError
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={CONF_SOURCE: SOURCE_USB}, data=DISCOVERY_INFO
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "cannot_connect"
|
||||
|
||||
|
||||
@patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()]))
|
||||
async def test_flow_user(hass: HomeAssistant):
|
||||
"""Test user initialized flow."""
|
||||
port = com_port()
|
||||
port_select = usb.human_readable_device_name(
|
||||
port.device,
|
||||
port.serial_number,
|
||||
port.manufacturer,
|
||||
port.description,
|
||||
port.vid,
|
||||
port.pid,
|
||||
)
|
||||
mocked_modem = AsyncMock()
|
||||
with _patch_config_flow_modem(mocked_modem), _patch_setup():
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={CONF_SOURCE: SOURCE_USER},
|
||||
data={CONF_DEVICE: port_select},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["data"] == {CONF_DEVICE: port.device}
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={CONF_SOURCE: SOURCE_USER},
|
||||
data={CONF_DEVICE: port_select},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "no_devices_found"
|
||||
|
||||
|
||||
@patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()]))
|
||||
async def test_flow_user_error(hass: HomeAssistant):
|
||||
"""Test user initialized flow with unreachable device."""
|
||||
port = com_port()
|
||||
port_select = usb.human_readable_device_name(
|
||||
port.device,
|
||||
port.serial_number,
|
||||
port.manufacturer,
|
||||
port.description,
|
||||
port.vid,
|
||||
port.pid,
|
||||
)
|
||||
with _patch_config_flow_modem(AsyncMock()) as modemmock:
|
||||
modemmock.side_effect = phone_modem.exceptions.SerialError
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={CONF_SOURCE: SOURCE_USER}, data={CONF_DEVICE: port_select}
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
modemmock.side_effect = None
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_DEVICE: port_select},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["data"] == {CONF_DEVICE: port.device}
|
||||
|
||||
|
||||
@patch("serial.tools.list_ports.comports", MagicMock())
|
||||
async def test_flow_user_no_port_list(hass: HomeAssistant):
|
||||
"""Test user with no list of ports."""
|
||||
with _patch_config_flow_modem(AsyncMock()):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={CONF_SOURCE: SOURCE_USER},
|
||||
data={CONF_DEVICE: phone_modem.DEFAULT_PORT},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "no_devices_found"
|
||||
|
||||
|
||||
async def test_abort_user_with_existing_flow(hass: HomeAssistant):
|
||||
"""Test user flow is aborted when another discovery has happened."""
|
||||
with _patch_config_flow_modem(AsyncMock()):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={CONF_SOURCE: SOURCE_USB},
|
||||
data=DISCOVERY_INFO,
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "usb_confirm"
|
||||
|
||||
result2 = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={CONF_SOURCE: SOURCE_USER},
|
||||
data={},
|
||||
)
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_ABORT
|
||||
assert result2["reason"] == "already_in_progress"
|
||||
|
||||
|
||||
@patch("serial.tools.list_ports.comports", MagicMock(return_value=[com_port()]))
|
||||
async def test_flow_import(hass: HomeAssistant):
|
||||
"""Test import step."""
|
||||
with _patch_config_flow_modem(AsyncMock()):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={CONF_SOURCE: SOURCE_IMPORT}, data=IMPORT_DATA
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == DEFAULT_NAME
|
||||
assert result["data"] == CONF_DATA
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={CONF_SOURCE: SOURCE_IMPORT}, data=IMPORT_DATA
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_flow_import_cannot_connect(hass: HomeAssistant):
|
||||
"""Test import connection error."""
|
||||
with _patch_config_flow_modem(AsyncMock()) as modemmock:
|
||||
modemmock.side_effect = phone_modem.exceptions.SerialError
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={CONF_SOURCE: SOURCE_IMPORT}, data=IMPORT_DATA
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "cannot_connect"
|
||||
@@ -0,0 +1,63 @@
|
||||
"""Test Modem Caller ID integration."""
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from phone_modem import exceptions
|
||||
|
||||
from homeassistant.components.modem_callerid.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import CONF_DATA, _patch_init_modem
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_setup_config(hass: HomeAssistant):
|
||||
"""Test Modem Caller ID setup."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=CONF_DATA,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
mocked_modem = AsyncMock()
|
||||
with _patch_init_modem(mocked_modem):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert entry.state == ConfigEntryState.LOADED
|
||||
|
||||
|
||||
async def test_async_setup_entry_not_ready(hass: HomeAssistant):
|
||||
"""Test that it throws ConfigEntryNotReady when exception occurs during setup."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=CONF_DATA,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.modem_callerid.PhoneModem",
|
||||
side_effect=exceptions.SerialError(),
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
assert entry.state == ConfigEntryState.SETUP_ERROR
|
||||
assert not hass.data.get(DOMAIN)
|
||||
|
||||
|
||||
async def test_unload_config_entry(hass: HomeAssistant):
|
||||
"""Test unload."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=CONF_DATA,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
mocked_modem = AsyncMock()
|
||||
with _patch_init_modem(mocked_modem):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.NOT_LOADED
|
||||
assert not hass.data.get(DOMAIN)
|
||||
Reference in New Issue
Block a user