mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add some basic tests for doorbird (#122135)
* basic tests * basic tests * basic tests * basic tests * cover * cover * Update tests/components/doorbird/test_init.py
This commit is contained in:
parent
d2cc25cee6
commit
a0a5f640dc
@ -1 +1,40 @@
|
|||||||
"""Tests for the DoorBird integration."""
|
"""Tests for the DoorBird integration."""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from unittest.mock import AsyncMock, MagicMock, Mock
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
from doorbirdpy import DoorBird, DoorBirdScheduleEntry
|
||||||
|
|
||||||
|
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME
|
||||||
|
|
||||||
|
VALID_CONFIG = {
|
||||||
|
CONF_HOST: "1.2.3.4",
|
||||||
|
CONF_USERNAME: "friend",
|
||||||
|
CONF_PASSWORD: "password",
|
||||||
|
CONF_NAME: "mydoorbird",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def mock_unauthorized_exception() -> aiohttp.ClientResponseError:
|
||||||
|
"""Return a mock unauthorized exception."""
|
||||||
|
return aiohttp.ClientResponseError(request_info=Mock(), history=Mock(), status=401)
|
||||||
|
|
||||||
|
|
||||||
|
def get_mock_doorbird_api(
|
||||||
|
info: dict[str, Any] | None = None,
|
||||||
|
info_side_effect: Exception | None = None,
|
||||||
|
schedule: list[DoorBirdScheduleEntry] | None = None,
|
||||||
|
) -> DoorBird:
|
||||||
|
"""Return a mock DoorBirdAPI object with return values."""
|
||||||
|
doorbirdapi_mock = MagicMock(spec_set=DoorBird)
|
||||||
|
type(doorbirdapi_mock).info = AsyncMock(
|
||||||
|
side_effect=info_side_effect, return_value=info
|
||||||
|
)
|
||||||
|
type(doorbirdapi_mock).favorites = AsyncMock(return_value={})
|
||||||
|
type(doorbirdapi_mock).change_favorite = AsyncMock(return_value=True)
|
||||||
|
type(doorbirdapi_mock).schedule = AsyncMock(return_value=schedule)
|
||||||
|
type(doorbirdapi_mock).doorbell_state = AsyncMock(
|
||||||
|
side_effect=mock_unauthorized_exception()
|
||||||
|
)
|
||||||
|
return doorbirdapi_mock
|
||||||
|
101
tests/components/doorbird/conftest.py
Normal file
101
tests/components/doorbird/conftest.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
"""Test configuration for DoorBird tests."""
|
||||||
|
|
||||||
|
from collections.abc import Generator
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
from doorbirdpy import DoorBird, DoorBirdScheduleEntry
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.doorbird.const import CONF_EVENTS, DOMAIN
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from . import VALID_CONFIG, get_mock_doorbird_api
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry, load_json_value_fixture
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MockDoorbirdEntry:
|
||||||
|
"""Mock DoorBird config entry."""
|
||||||
|
|
||||||
|
entry: MockConfigEntry
|
||||||
|
api: MagicMock
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def doorbird_info() -> dict[str, Any]:
|
||||||
|
"""Return a loaded DoorBird info fixture."""
|
||||||
|
return load_json_value_fixture("info.json", "doorbird")["BHA"]["VERSION"][0]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def doorbird_schedule() -> list[DoorBirdScheduleEntry]:
|
||||||
|
"""Return a loaded DoorBird schedule fixture."""
|
||||||
|
return DoorBirdScheduleEntry.parse_all(
|
||||||
|
load_json_value_fixture("schedule.json", "doorbird")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def doorbird_api(
|
||||||
|
doorbird_info: dict[str, Any], doorbird_schedule: dict[str, Any]
|
||||||
|
) -> Generator[Any, Any, DoorBird]:
|
||||||
|
"""Mock the DoorBirdAPI."""
|
||||||
|
api = get_mock_doorbird_api(info=doorbird_info, schedule=doorbird_schedule)
|
||||||
|
with patch_doorbird_api_entry_points(api):
|
||||||
|
yield api
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def patch_doorbird_api_entry_points(api: MagicMock) -> Generator[Any, Any, DoorBird]:
|
||||||
|
"""Mock the DoorBirdAPI."""
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.doorbird.DoorBird",
|
||||||
|
return_value=api,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.doorbird.config_flow.DoorBird",
|
||||||
|
return_value=api,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
yield api
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def doorbird_mocker(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
doorbird_info: dict[str, Any],
|
||||||
|
doorbird_schedule: dict[str, Any],
|
||||||
|
) -> MockDoorbirdEntry:
|
||||||
|
"""Create a MockDoorbirdEntry."""
|
||||||
|
|
||||||
|
async def _async_mock(
|
||||||
|
entry: MockConfigEntry | None = None,
|
||||||
|
api: DoorBird | None = None,
|
||||||
|
info: dict[str, Any] | None = None,
|
||||||
|
info_side_effect: Exception | None = None,
|
||||||
|
schedule: list[DoorBirdScheduleEntry] | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Create a MockDoorbirdEntry from defaults or specific values."""
|
||||||
|
entry = entry or MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
unique_id="1CCAE3AAAAAA",
|
||||||
|
data=VALID_CONFIG,
|
||||||
|
options={CONF_EVENTS: ["event1", "event2", "event3"]},
|
||||||
|
)
|
||||||
|
api = api or get_mock_doorbird_api(
|
||||||
|
info=info or doorbird_info,
|
||||||
|
info_side_effect=info_side_effect,
|
||||||
|
schedule=schedule or doorbird_schedule,
|
||||||
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
with patch_doorbird_api_entry_points(api):
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
return MockDoorbirdEntry(entry=entry, api=api)
|
||||||
|
|
||||||
|
return _async_mock
|
23
tests/components/doorbird/fixtures/info.json
Normal file
23
tests/components/doorbird/fixtures/info.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"BHA": {
|
||||||
|
"RETURNCODE": "1",
|
||||||
|
"VERSION": [
|
||||||
|
{
|
||||||
|
"FIRMWARE": "000125",
|
||||||
|
"BUILD_NUMBER": "15870439",
|
||||||
|
"WIFI_MAC_ADDR": "1234ABCD",
|
||||||
|
"RELAYS": [
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"ghchdi@1",
|
||||||
|
"ghchdi@2",
|
||||||
|
"ghchdi@3",
|
||||||
|
"ghdwkh@1",
|
||||||
|
"ghdwkh@2",
|
||||||
|
"ghdwkh@3"
|
||||||
|
],
|
||||||
|
"DEVICE-TYPE": "DoorBird D2101V"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
67
tests/components/doorbird/fixtures/schedule.json
Normal file
67
tests/components/doorbird/fixtures/schedule.json
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"input": "doorbell",
|
||||||
|
"param": "1",
|
||||||
|
"output": [
|
||||||
|
{
|
||||||
|
"event": "notify",
|
||||||
|
"param": "",
|
||||||
|
"schedule": {
|
||||||
|
"weekdays": [
|
||||||
|
{
|
||||||
|
"to": "107999",
|
||||||
|
"from": "108000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "http",
|
||||||
|
"param": "0",
|
||||||
|
"schedule": {
|
||||||
|
"weekdays": [
|
||||||
|
{
|
||||||
|
"to": "107999",
|
||||||
|
"from": "108000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "motion",
|
||||||
|
"param": "",
|
||||||
|
"output": [
|
||||||
|
{
|
||||||
|
"event": "notify",
|
||||||
|
"param": "",
|
||||||
|
"schedule": {
|
||||||
|
"weekdays": [
|
||||||
|
{
|
||||||
|
"to": "107999",
|
||||||
|
"from": "108000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"event": "http",
|
||||||
|
"param": "5",
|
||||||
|
"schedule": {
|
||||||
|
"weekdays": [
|
||||||
|
{
|
||||||
|
"to": "107999",
|
||||||
|
"from": "108000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "relay",
|
||||||
|
"param": "1",
|
||||||
|
"output": []
|
||||||
|
}
|
||||||
|
]
|
@ -1,9 +1,10 @@
|
|||||||
"""Test the DoorBird config flow."""
|
"""Test the DoorBird config flow."""
|
||||||
|
|
||||||
from ipaddress import ip_address
|
from ipaddress import ip_address
|
||||||
from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from doorbirdpy import DoorBird
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
@ -18,35 +19,12 @@ from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNA
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
|
||||||
|
from . import VALID_CONFIG, get_mock_doorbird_api, mock_unauthorized_exception
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
VALID_CONFIG = {
|
|
||||||
CONF_HOST: "1.2.3.4",
|
|
||||||
CONF_USERNAME: "friend",
|
|
||||||
CONF_PASSWORD: "password",
|
|
||||||
CONF_NAME: "mydoorbird",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
async def test_user_form(hass: HomeAssistant, doorbird_api: DoorBird) -> None:
|
||||||
def _get_mock_doorbirdapi_return_values(info=None):
|
|
||||||
doorbirdapi_mock = MagicMock()
|
|
||||||
type(doorbirdapi_mock).info = AsyncMock(return_value=info)
|
|
||||||
type(doorbirdapi_mock).doorbell_state = AsyncMock(
|
|
||||||
side_effect=aiohttp.ClientResponseError(
|
|
||||||
request_info=Mock(), history=Mock(), status=401
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return doorbirdapi_mock
|
|
||||||
|
|
||||||
|
|
||||||
def _get_mock_doorbirdapi_side_effects(info=None):
|
|
||||||
doorbirdapi_mock = MagicMock()
|
|
||||||
type(doorbirdapi_mock).info = AsyncMock(side_effect=info)
|
|
||||||
|
|
||||||
return doorbirdapi_mock
|
|
||||||
|
|
||||||
|
|
||||||
async def test_user_form(hass: HomeAssistant) -> None:
|
|
||||||
"""Test we get the user form."""
|
"""Test we get the user form."""
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
@ -55,12 +33,7 @@ async def test_user_form(hass: HomeAssistant) -> None:
|
|||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
doorbirdapi = _get_mock_doorbirdapi_return_values(info={"WIFI_MAC_ADDR": "macaddr"})
|
|
||||||
with (
|
with (
|
||||||
patch(
|
|
||||||
"homeassistant.components.doorbird.config_flow.DoorBird",
|
|
||||||
return_value=doorbirdapi,
|
|
||||||
),
|
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.doorbird.async_setup", return_value=True
|
"homeassistant.components.doorbird.async_setup", return_value=True
|
||||||
) as mock_setup,
|
) as mock_setup,
|
||||||
@ -178,14 +151,11 @@ async def test_form_zeroconf_non_ipv4_ignored(hass: HomeAssistant) -> None:
|
|||||||
assert result["reason"] == "not_ipv4_address"
|
assert result["reason"] == "not_ipv4_address"
|
||||||
|
|
||||||
|
|
||||||
async def test_form_zeroconf_correct_oui(hass: HomeAssistant) -> None:
|
async def test_form_zeroconf_correct_oui(
|
||||||
|
hass: HomeAssistant, doorbird_api: DoorBird
|
||||||
|
) -> None:
|
||||||
"""Test we can setup from zeroconf with the correct OUI source."""
|
"""Test we can setup from zeroconf with the correct OUI source."""
|
||||||
doorbirdapi = _get_mock_doorbirdapi_return_values(info={"WIFI_MAC_ADDR": "macaddr"})
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.doorbird.config_flow.DoorBird",
|
|
||||||
return_value=doorbirdapi,
|
|
||||||
):
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": config_entries.SOURCE_ZEROCONF},
|
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||||
@ -205,10 +175,6 @@ async def test_form_zeroconf_correct_oui(hass: HomeAssistant) -> None:
|
|||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch(
|
|
||||||
"homeassistant.components.doorbird.config_flow.DoorBird",
|
|
||||||
return_value=doorbirdapi,
|
|
||||||
),
|
|
||||||
patch("homeassistant.components.logbook.async_setup", return_value=True),
|
patch("homeassistant.components.logbook.async_setup", return_value=True),
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.doorbird.async_setup", return_value=True
|
"homeassistant.components.doorbird.async_setup", return_value=True
|
||||||
@ -244,10 +210,12 @@ async def test_form_zeroconf_correct_oui(hass: HomeAssistant) -> None:
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_form_zeroconf_correct_oui_wrong_device(
|
async def test_form_zeroconf_correct_oui_wrong_device(
|
||||||
hass: HomeAssistant, doorbell_state_side_effect
|
hass: HomeAssistant,
|
||||||
|
doorbird_api: DoorBird,
|
||||||
|
doorbell_state_side_effect: Exception | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test we can setup from zeroconf with the correct OUI source but not a doorstation."""
|
"""Test we can setup from zeroconf with the correct OUI source but not a doorstation."""
|
||||||
doorbirdapi = _get_mock_doorbirdapi_return_values(info={"WIFI_MAC_ADDR": "macaddr"})
|
doorbirdapi = get_mock_doorbird_api(info={"WIFI_MAC_ADDR": "macaddr"})
|
||||||
type(doorbirdapi).doorbell_state = AsyncMock(side_effect=doorbell_state_side_effect)
|
type(doorbirdapi).doorbell_state = AsyncMock(side_effect=doorbell_state_side_effect)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
@ -278,7 +246,7 @@ async def test_form_user_cannot_connect(hass: HomeAssistant) -> None:
|
|||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
|
|
||||||
doorbirdapi = _get_mock_doorbirdapi_side_effects(info=OSError)
|
doorbirdapi = get_mock_doorbird_api(info_side_effect=OSError)
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.doorbird.config_flow.DoorBird",
|
"homeassistant.components.doorbird.config_flow.DoorBird",
|
||||||
return_value=doorbirdapi,
|
return_value=doorbirdapi,
|
||||||
@ -298,10 +266,8 @@ async def test_form_user_invalid_auth(hass: HomeAssistant) -> None:
|
|||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
|
|
||||||
mock_error = aiohttp.ClientResponseError(
|
mock_error = mock_unauthorized_exception()
|
||||||
request_info=Mock(), history=Mock(), status=401
|
doorbirdapi = get_mock_doorbird_api(info_side_effect=mock_error)
|
||||||
)
|
|
||||||
doorbirdapi = _get_mock_doorbirdapi_side_effects(info=mock_error)
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.doorbird.config_flow.DoorBird",
|
"homeassistant.components.doorbird.config_flow.DoorBird",
|
||||||
return_value=doorbirdapi,
|
return_value=doorbirdapi,
|
||||||
@ -360,10 +326,8 @@ async def test_reauth(hass: HomeAssistant) -> None:
|
|||||||
assert len(flows) == 1
|
assert len(flows) == 1
|
||||||
flow = flows[0]
|
flow = flows[0]
|
||||||
|
|
||||||
mock_error = aiohttp.ClientResponseError(
|
mock_error = mock_unauthorized_exception()
|
||||||
request_info=Mock(), history=Mock(), status=401
|
doorbirdapi = get_mock_doorbird_api(info_side_effect=mock_error)
|
||||||
)
|
|
||||||
doorbirdapi = _get_mock_doorbirdapi_side_effects(info=mock_error)
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.doorbird.config_flow.DoorBird",
|
"homeassistant.components.doorbird.config_flow.DoorBird",
|
||||||
return_value=doorbirdapi,
|
return_value=doorbirdapi,
|
||||||
@ -379,7 +343,7 @@ async def test_reauth(hass: HomeAssistant) -> None:
|
|||||||
assert result2["type"] is FlowResultType.FORM
|
assert result2["type"] is FlowResultType.FORM
|
||||||
assert result2["errors"] == {"base": "invalid_auth"}
|
assert result2["errors"] == {"base": "invalid_auth"}
|
||||||
|
|
||||||
doorbirdapi = _get_mock_doorbirdapi_return_values(info={"WIFI_MAC_ADDR": "macaddr"})
|
doorbirdapi = get_mock_doorbird_api(info={"WIFI_MAC_ADDR": "macaddr"})
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.doorbird.config_flow.DoorBird",
|
"homeassistant.components.doorbird.config_flow.DoorBird",
|
||||||
|
35
tests/components/doorbird/test_init.py
Normal file
35
tests/components/doorbird/test_init.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
"""Test DoorBird init."""
|
||||||
|
|
||||||
|
from collections.abc import Callable, Coroutine
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from homeassistant.components.doorbird.const import DOMAIN
|
||||||
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from . import mock_unauthorized_exception
|
||||||
|
from .conftest import MockDoorbirdEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_basic_setup(
|
||||||
|
doorbird_mocker: Callable[[], Coroutine[Any, Any, MockDoorbirdEntry]],
|
||||||
|
) -> None:
|
||||||
|
"""Test basic setup."""
|
||||||
|
doorbird_entry = await doorbird_mocker()
|
||||||
|
entry = doorbird_entry.entry
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
|
||||||
|
async def test_auth_fails(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
doorbird_mocker: Callable[[], Coroutine[Any, Any, MockDoorbirdEntry]],
|
||||||
|
) -> None:
|
||||||
|
"""Test basic setup with an auth failure."""
|
||||||
|
doorbird_entry = await doorbird_mocker(
|
||||||
|
info_side_effect=mock_unauthorized_exception()
|
||||||
|
)
|
||||||
|
entry = doorbird_entry.entry
|
||||||
|
assert entry.state is ConfigEntryState.SETUP_ERROR
|
||||||
|
flows = hass.config_entries.flow.async_progress(DOMAIN)
|
||||||
|
assert len(flows) == 1
|
||||||
|
assert flows[0]["step_id"] == "reauth_confirm"
|
Loading…
x
Reference in New Issue
Block a user