mirror of
https://github.com/home-assistant/core.git
synced 2026-09-03 02:34:57 -05:00
Add network and callback support to SSDP (#51019)
Co-authored-by: Ruslan Sayfutdinov <ruslan@sayfutdinov.com> Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
co-authored by
Ruslan Sayfutdinov
Paulus Schoutsen
parent
02cbb2025e
commit
fb50cf9840
+472
-157
@@ -1,42 +1,70 @@
|
||||
"""Test the SSDP integration."""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
from ipaddress import IPv4Address, IPv6Address
|
||||
from unittest.mock import patch
|
||||
|
||||
import aiohttp
|
||||
from async_upnp_client.search import SSDPListener
|
||||
from async_upnp_client.utils import CaseInsensitiveDict
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import ssdp
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import CoreState, callback
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import async_fire_time_changed, mock_coro
|
||||
|
||||
|
||||
async def test_scan_match_st(hass, caplog):
|
||||
"""Test matching based on ST."""
|
||||
scanner = ssdp.Scanner(hass, {"mock-domain": [{"st": "mock-st"}]})
|
||||
def _patched_ssdp_listener(info, *args, **kwargs):
|
||||
listener = SSDPListener(*args, **kwargs)
|
||||
|
||||
async def _mock_async_scan(*args, async_callback=None, **kwargs):
|
||||
await async_callback(
|
||||
{
|
||||
"st": "mock-st",
|
||||
"location": None,
|
||||
"usn": "mock-usn",
|
||||
"server": "mock-server",
|
||||
"ext": "",
|
||||
}
|
||||
async def _async_callback(*_):
|
||||
await listener.async_callback(info)
|
||||
|
||||
listener.async_start = _async_callback
|
||||
return listener
|
||||
|
||||
|
||||
async def _async_run_mocked_scan(hass, mock_ssdp_response, mock_get_ssdp):
|
||||
def _generate_fake_ssdp_listener(*args, **kwargs):
|
||||
return _patched_ssdp_listener(
|
||||
mock_ssdp_response,
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ssdp.async_search",
|
||||
side_effect=_mock_async_scan,
|
||||
"homeassistant.components.ssdp.async_get_ssdp",
|
||||
return_value=mock_get_ssdp,
|
||||
), patch(
|
||||
"homeassistant.components.ssdp.SSDPListener",
|
||||
new=_generate_fake_ssdp_listener,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init", return_value=mock_coro()
|
||||
) as mock_init:
|
||||
await scanner.async_scan(None)
|
||||
assert await async_setup_component(hass, ssdp.DOMAIN, {ssdp.DOMAIN: {}})
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return mock_init
|
||||
|
||||
|
||||
async def test_scan_match_st(hass, caplog):
|
||||
"""Test matching based on ST."""
|
||||
mock_ssdp_response = {
|
||||
"st": "mock-st",
|
||||
"location": None,
|
||||
"usn": "mock-usn",
|
||||
"server": "mock-server",
|
||||
"ext": "",
|
||||
}
|
||||
mock_get_ssdp = {"mock-domain": [{"st": "mock-st"}]}
|
||||
mock_init = await _async_run_mocked_scan(hass, mock_ssdp_response, mock_get_ssdp)
|
||||
|
||||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
@@ -53,6 +81,19 @@ async def test_scan_match_st(hass, caplog):
|
||||
assert "Failed to fetch ssdp data" not in caplog.text
|
||||
|
||||
|
||||
async def test_partial_response(hass, caplog):
|
||||
"""Test location and st missing."""
|
||||
mock_ssdp_response = {
|
||||
"usn": "mock-usn",
|
||||
"server": "mock-server",
|
||||
"ext": "",
|
||||
}
|
||||
mock_get_ssdp = {"mock-domain": [{"st": "mock-st"}]}
|
||||
mock_init = await _async_run_mocked_scan(hass, mock_ssdp_response, mock_get_ssdp)
|
||||
|
||||
assert len(mock_init.mock_calls) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"key", (ssdp.ATTR_UPNP_MANUFACTURER, ssdp.ATTR_UPNP_DEVICE_TYPE)
|
||||
)
|
||||
@@ -68,25 +109,12 @@ async def test_scan_match_upnp_devicedesc(hass, aioclient_mock, key):
|
||||
</root>
|
||||
""",
|
||||
)
|
||||
scanner = ssdp.Scanner(hass, {"mock-domain": [{key: "Paulus"}]})
|
||||
|
||||
async def _mock_async_scan(*args, async_callback=None, **kwargs):
|
||||
for _ in range(5):
|
||||
await async_callback(
|
||||
{
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ssdp.async_search",
|
||||
side_effect=_mock_async_scan,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init", return_value=mock_coro()
|
||||
) as mock_init:
|
||||
await scanner.async_scan(None)
|
||||
|
||||
mock_get_ssdp = {"mock-domain": [{key: "Paulus"}]}
|
||||
mock_ssdp_response = {
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
}
|
||||
mock_init = await _async_run_mocked_scan(hass, mock_ssdp_response, mock_get_ssdp)
|
||||
# If we get duplicate respones, ensure we only look it up once
|
||||
assert len(aioclient_mock.mock_calls) == 1
|
||||
assert len(mock_init.mock_calls) == 1
|
||||
@@ -108,33 +136,19 @@ async def test_scan_not_all_present(hass, aioclient_mock):
|
||||
</root>
|
||||
""",
|
||||
)
|
||||
scanner = ssdp.Scanner(
|
||||
hass,
|
||||
{
|
||||
"mock-domain": [
|
||||
{
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus",
|
||||
ssdp.ATTR_UPNP_MANUFACTURER: "Paulus",
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
async def _mock_async_scan(*args, async_callback=None, **kwargs):
|
||||
await async_callback(
|
||||
mock_ssdp_response = {
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
}
|
||||
mock_get_ssdp = {
|
||||
"mock-domain": [
|
||||
{
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus",
|
||||
ssdp.ATTR_UPNP_MANUFACTURER: "Paulus",
|
||||
}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ssdp.async_search",
|
||||
side_effect=_mock_async_scan,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init", return_value=mock_coro()
|
||||
) as mock_init:
|
||||
await scanner.async_scan(None)
|
||||
]
|
||||
}
|
||||
mock_init = await _async_run_mocked_scan(hass, mock_ssdp_response, mock_get_ssdp)
|
||||
|
||||
assert not mock_init.mock_calls
|
||||
|
||||
@@ -152,33 +166,19 @@ async def test_scan_not_all_match(hass, aioclient_mock):
|
||||
</root>
|
||||
""",
|
||||
)
|
||||
scanner = ssdp.Scanner(
|
||||
hass,
|
||||
{
|
||||
"mock-domain": [
|
||||
{
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus",
|
||||
ssdp.ATTR_UPNP_MANUFACTURER: "Not-Paulus",
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
async def _mock_async_scan(*args, async_callback=None, **kwargs):
|
||||
await async_callback(
|
||||
mock_ssdp_response = {
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
}
|
||||
mock_get_ssdp = {
|
||||
"mock-domain": [
|
||||
{
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus",
|
||||
ssdp.ATTR_UPNP_MANUFACTURER: "Not-Paulus",
|
||||
}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ssdp.async_search",
|
||||
side_effect=_mock_async_scan,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init", return_value=mock_coro()
|
||||
) as mock_init:
|
||||
await scanner.async_scan(None)
|
||||
]
|
||||
}
|
||||
mock_init = await _async_run_mocked_scan(hass, mock_ssdp_response, mock_get_ssdp)
|
||||
|
||||
assert not mock_init.mock_calls
|
||||
|
||||
@@ -187,21 +187,21 @@ async def test_scan_not_all_match(hass, aioclient_mock):
|
||||
async def test_scan_description_fetch_fail(hass, aioclient_mock, exc):
|
||||
"""Test failing to fetch description."""
|
||||
aioclient_mock.get("http://1.1.1.1", exc=exc)
|
||||
scanner = ssdp.Scanner(hass, {})
|
||||
|
||||
async def _mock_async_scan(*args, async_callback=None, **kwargs):
|
||||
await async_callback(
|
||||
mock_ssdp_response = {
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
}
|
||||
mock_get_ssdp = {
|
||||
"mock-domain": [
|
||||
{
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus",
|
||||
ssdp.ATTR_UPNP_MANUFACTURER: "Paulus",
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
mock_init = await _async_run_mocked_scan(hass, mock_ssdp_response, mock_get_ssdp)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ssdp.async_search",
|
||||
side_effect=_mock_async_scan,
|
||||
):
|
||||
await scanner.async_scan(None)
|
||||
assert not mock_init.mock_calls
|
||||
|
||||
|
||||
async def test_scan_description_parse_fail(hass, aioclient_mock):
|
||||
@@ -212,21 +212,22 @@ async def test_scan_description_parse_fail(hass, aioclient_mock):
|
||||
<root>INVALIDXML
|
||||
""",
|
||||
)
|
||||
scanner = ssdp.Scanner(hass, {})
|
||||
|
||||
async def _mock_async_scan(*args, async_callback=None, **kwargs):
|
||||
await async_callback(
|
||||
mock_ssdp_response = {
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
}
|
||||
mock_get_ssdp = {
|
||||
"mock-domain": [
|
||||
{
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus",
|
||||
ssdp.ATTR_UPNP_MANUFACTURER: "Paulus",
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
mock_init = await _async_run_mocked_scan(hass, mock_ssdp_response, mock_get_ssdp)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ssdp.async_search",
|
||||
side_effect=_mock_async_scan,
|
||||
):
|
||||
await scanner.async_scan(None)
|
||||
assert not mock_init.mock_calls
|
||||
|
||||
|
||||
async def test_invalid_characters(hass, aioclient_mock):
|
||||
@@ -242,32 +243,20 @@ async def test_invalid_characters(hass, aioclient_mock):
|
||||
</root>
|
||||
""",
|
||||
)
|
||||
scanner = ssdp.Scanner(
|
||||
hass,
|
||||
{
|
||||
"mock-domain": [
|
||||
{
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "ABC",
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
async def _mock_async_scan(*args, async_callback=None, **kwargs):
|
||||
await async_callback(
|
||||
mock_ssdp_response = {
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
}
|
||||
mock_get_ssdp = {
|
||||
"mock-domain": [
|
||||
{
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "ABC",
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ssdp.async_search",
|
||||
side_effect=_mock_async_scan,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init", return_value=mock_coro()
|
||||
) as mock_init:
|
||||
await scanner.async_scan(None)
|
||||
mock_init = await _async_run_mocked_scan(hass, mock_ssdp_response, mock_get_ssdp)
|
||||
|
||||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
@@ -282,8 +271,9 @@ async def test_invalid_characters(hass, aioclient_mock):
|
||||
}
|
||||
|
||||
|
||||
@patch("homeassistant.components.ssdp.async_search")
|
||||
async def test_start_stop_scanner(async_search_mock, hass):
|
||||
@patch("homeassistant.components.ssdp.SSDPListener.async_start")
|
||||
@patch("homeassistant.components.ssdp.SSDPListener.async_search")
|
||||
async def test_start_stop_scanner(async_start_mock, async_search_mock, hass):
|
||||
"""Test we start and stop the scanner."""
|
||||
assert await async_setup_component(hass, ssdp.DOMAIN, {ssdp.DOMAIN: {}})
|
||||
|
||||
@@ -291,13 +281,15 @@ async def test_start_stop_scanner(async_search_mock, hass):
|
||||
await hass.async_block_till_done()
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
|
||||
await hass.async_block_till_done()
|
||||
assert async_search_mock.call_count == 2
|
||||
assert async_start_mock.call_count == 1
|
||||
assert async_search_mock.call_count == 1
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
|
||||
await hass.async_block_till_done()
|
||||
assert async_search_mock.call_count == 2
|
||||
assert async_start_mock.call_count == 1
|
||||
assert async_search_mock.call_count == 1
|
||||
|
||||
|
||||
async def test_unexpected_exception_while_fetching(hass, aioclient_mock, caplog):
|
||||
@@ -313,34 +305,357 @@ async def test_unexpected_exception_while_fetching(hass, aioclient_mock, caplog)
|
||||
</root>
|
||||
""",
|
||||
)
|
||||
scanner = ssdp.Scanner(
|
||||
hass,
|
||||
{
|
||||
"mock-domain": [
|
||||
{
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "ABC",
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
async def _mock_async_scan(*args, async_callback=None, **kwargs):
|
||||
await async_callback(
|
||||
mock_ssdp_response = {
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
}
|
||||
mock_get_ssdp = {
|
||||
"mock-domain": [
|
||||
{
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "ABC",
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ssdp.ElementTree.fromstring", side_effect=ValueError
|
||||
), patch(
|
||||
"homeassistant.components.ssdp.async_search",
|
||||
side_effect=_mock_async_scan,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init", return_value=mock_coro()
|
||||
) as mock_init:
|
||||
await scanner.async_scan(None)
|
||||
"homeassistant.components.ssdp.descriptions.ElementTree.fromstring",
|
||||
side_effect=ValueError,
|
||||
):
|
||||
mock_init = await _async_run_mocked_scan(
|
||||
hass, mock_ssdp_response, mock_get_ssdp
|
||||
)
|
||||
|
||||
assert len(mock_init.mock_calls) == 0
|
||||
assert "Failed to fetch ssdp data from: http://1.1.1.1" in caplog.text
|
||||
|
||||
|
||||
async def test_scan_with_registered_callback(hass, aioclient_mock, caplog):
|
||||
"""Test matching based on callback."""
|
||||
aioclient_mock.get(
|
||||
"http://1.1.1.1",
|
||||
text="""
|
||||
<root>
|
||||
<device>
|
||||
<deviceType>Paulus</deviceType>
|
||||
</device>
|
||||
</root>
|
||||
""",
|
||||
)
|
||||
mock_ssdp_response = {
|
||||
"st": "mock-st",
|
||||
"location": "http://1.1.1.1",
|
||||
"usn": "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::urn:mdx-netflix-com:service:target:3",
|
||||
"server": "mock-server",
|
||||
"ext": "",
|
||||
}
|
||||
not_matching_intergration_callbacks = []
|
||||
intergration_callbacks = []
|
||||
intergration_callbacks_from_cache = []
|
||||
match_any_callbacks = []
|
||||
|
||||
@callback
|
||||
def _async_exception_callbacks(info):
|
||||
raise ValueError
|
||||
|
||||
@callback
|
||||
def _async_intergration_callbacks(info):
|
||||
intergration_callbacks.append(info)
|
||||
|
||||
@callback
|
||||
def _async_intergration_callbacks_from_cache(info):
|
||||
intergration_callbacks_from_cache.append(info)
|
||||
|
||||
@callback
|
||||
def _async_not_matching_intergration_callbacks(info):
|
||||
not_matching_intergration_callbacks.append(info)
|
||||
|
||||
@callback
|
||||
def _async_match_any_callbacks(info):
|
||||
match_any_callbacks.append(info)
|
||||
|
||||
def _generate_fake_ssdp_listener(*args, **kwargs):
|
||||
listener = SSDPListener(*args, **kwargs)
|
||||
|
||||
async def _async_callback(*_):
|
||||
await listener.async_callback(mock_ssdp_response)
|
||||
|
||||
@callback
|
||||
def _callback(*_):
|
||||
hass.async_create_task(listener.async_callback(mock_ssdp_response))
|
||||
|
||||
listener.async_start = _async_callback
|
||||
listener.async_search = _callback
|
||||
return listener
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ssdp.SSDPListener",
|
||||
new=_generate_fake_ssdp_listener,
|
||||
):
|
||||
hass.state = CoreState.stopped
|
||||
assert await async_setup_component(hass, ssdp.DOMAIN, {ssdp.DOMAIN: {}})
|
||||
await hass.async_block_till_done()
|
||||
ssdp.async_register_callback(hass, _async_exception_callbacks, {})
|
||||
ssdp.async_register_callback(
|
||||
hass,
|
||||
_async_intergration_callbacks,
|
||||
{"st": "mock-st"},
|
||||
)
|
||||
ssdp.async_register_callback(
|
||||
hass,
|
||||
_async_not_matching_intergration_callbacks,
|
||||
{"st": "not-match-mock-st"},
|
||||
)
|
||||
ssdp.async_register_callback(
|
||||
hass,
|
||||
_async_match_any_callbacks,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
|
||||
ssdp.async_register_callback(
|
||||
hass,
|
||||
_async_intergration_callbacks_from_cache,
|
||||
{"st": "mock-st"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
hass.state = CoreState.running
|
||||
await hass.async_block_till_done()
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
|
||||
await hass.async_block_till_done()
|
||||
assert hass.state == CoreState.running
|
||||
|
||||
assert len(intergration_callbacks) == 3
|
||||
assert len(intergration_callbacks_from_cache) == 3
|
||||
assert len(match_any_callbacks) == 3
|
||||
assert len(not_matching_intergration_callbacks) == 0
|
||||
assert intergration_callbacks[0] == {
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus",
|
||||
ssdp.ATTR_SSDP_EXT: "",
|
||||
ssdp.ATTR_SSDP_LOCATION: "http://1.1.1.1",
|
||||
ssdp.ATTR_SSDP_SERVER: "mock-server",
|
||||
ssdp.ATTR_SSDP_ST: "mock-st",
|
||||
ssdp.ATTR_SSDP_USN: "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::urn:mdx-netflix-com:service:target:3",
|
||||
ssdp.ATTR_UPNP_UDN: "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL",
|
||||
}
|
||||
assert "Failed to callback info" in caplog.text
|
||||
|
||||
|
||||
async def test_scan_second_hit(hass, aioclient_mock, caplog):
|
||||
"""Test matching on second scan."""
|
||||
aioclient_mock.get(
|
||||
"http://1.1.1.1",
|
||||
text="""
|
||||
<root>
|
||||
<device>
|
||||
<deviceType>Paulus</deviceType>
|
||||
</device>
|
||||
</root>
|
||||
""",
|
||||
)
|
||||
|
||||
mock_ssdp_response = CaseInsensitiveDict(
|
||||
**{
|
||||
"ST": "mock-st",
|
||||
"LOCATION": "http://1.1.1.1",
|
||||
"USN": "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::urn:mdx-netflix-com:service:target:3",
|
||||
"SERVER": "mock-server",
|
||||
"EXT": "",
|
||||
}
|
||||
)
|
||||
mock_get_ssdp = {"mock-domain": [{"st": "mock-st"}]}
|
||||
intergration_callbacks = []
|
||||
|
||||
@callback
|
||||
def _async_intergration_callbacks(info):
|
||||
intergration_callbacks.append(info)
|
||||
|
||||
def _generate_fake_ssdp_listener(*args, **kwargs):
|
||||
listener = SSDPListener(*args, **kwargs)
|
||||
|
||||
async def _async_callback(*_):
|
||||
pass
|
||||
|
||||
@callback
|
||||
def _callback(*_):
|
||||
hass.async_create_task(listener.async_callback(mock_ssdp_response))
|
||||
|
||||
listener.async_start = _async_callback
|
||||
listener.async_search = _callback
|
||||
return listener
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ssdp.async_get_ssdp",
|
||||
return_value=mock_get_ssdp,
|
||||
), patch(
|
||||
"homeassistant.components.ssdp.SSDPListener",
|
||||
new=_generate_fake_ssdp_listener,
|
||||
), patch.object(
|
||||
hass.config_entries.flow, "async_init", return_value=mock_coro()
|
||||
) as mock_init:
|
||||
assert await async_setup_component(hass, ssdp.DOMAIN, {ssdp.DOMAIN: {}})
|
||||
await hass.async_block_till_done()
|
||||
remove = ssdp.async_register_callback(
|
||||
hass,
|
||||
_async_intergration_callbacks,
|
||||
{"st": "mock-st"},
|
||||
)
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
|
||||
await hass.async_block_till_done()
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
|
||||
await hass.async_block_till_done()
|
||||
remove()
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(intergration_callbacks) == 2
|
||||
assert intergration_callbacks[0] == {
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus",
|
||||
ssdp.ATTR_SSDP_EXT: "",
|
||||
ssdp.ATTR_SSDP_LOCATION: "http://1.1.1.1",
|
||||
ssdp.ATTR_SSDP_SERVER: "mock-server",
|
||||
ssdp.ATTR_SSDP_ST: "mock-st",
|
||||
ssdp.ATTR_SSDP_USN: "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::urn:mdx-netflix-com:service:target:3",
|
||||
ssdp.ATTR_UPNP_UDN: "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL",
|
||||
}
|
||||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mock-domain"
|
||||
assert mock_init.mock_calls[0][2]["context"] == {
|
||||
"source": config_entries.SOURCE_SSDP
|
||||
}
|
||||
assert mock_init.mock_calls[0][2]["data"] == {
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus",
|
||||
ssdp.ATTR_SSDP_ST: "mock-st",
|
||||
ssdp.ATTR_SSDP_LOCATION: "http://1.1.1.1",
|
||||
ssdp.ATTR_SSDP_SERVER: "mock-server",
|
||||
ssdp.ATTR_SSDP_EXT: "",
|
||||
ssdp.ATTR_SSDP_USN: "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::urn:mdx-netflix-com:service:target:3",
|
||||
ssdp.ATTR_UPNP_UDN: "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL",
|
||||
}
|
||||
assert "Failed to fetch ssdp data" not in caplog.text
|
||||
udn_discovery_info = ssdp.async_get_discovery_info_by_st(hass, "mock-st")
|
||||
discovery_info = udn_discovery_info[0]
|
||||
assert discovery_info[ssdp.ATTR_SSDP_LOCATION] == "http://1.1.1.1"
|
||||
assert discovery_info[ssdp.ATTR_SSDP_ST] == "mock-st"
|
||||
assert (
|
||||
discovery_info[ssdp.ATTR_UPNP_UDN]
|
||||
== "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL"
|
||||
)
|
||||
assert (
|
||||
discovery_info[ssdp.ATTR_SSDP_USN]
|
||||
== "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::urn:mdx-netflix-com:service:target:3"
|
||||
)
|
||||
|
||||
st_discovery_info = ssdp.async_get_discovery_info_by_udn(
|
||||
hass, "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL"
|
||||
)
|
||||
discovery_info = st_discovery_info[0]
|
||||
assert discovery_info[ssdp.ATTR_SSDP_LOCATION] == "http://1.1.1.1"
|
||||
assert discovery_info[ssdp.ATTR_SSDP_ST] == "mock-st"
|
||||
assert (
|
||||
discovery_info[ssdp.ATTR_UPNP_UDN]
|
||||
== "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL"
|
||||
)
|
||||
assert (
|
||||
discovery_info[ssdp.ATTR_SSDP_USN]
|
||||
== "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::urn:mdx-netflix-com:service:target:3"
|
||||
)
|
||||
|
||||
discovery_info = ssdp.async_get_discovery_info_by_udn_st(
|
||||
hass, "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL", "mock-st"
|
||||
)
|
||||
assert discovery_info[ssdp.ATTR_SSDP_LOCATION] == "http://1.1.1.1"
|
||||
assert discovery_info[ssdp.ATTR_SSDP_ST] == "mock-st"
|
||||
assert (
|
||||
discovery_info[ssdp.ATTR_UPNP_UDN]
|
||||
== "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL"
|
||||
)
|
||||
assert (
|
||||
discovery_info[ssdp.ATTR_SSDP_USN]
|
||||
== "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::urn:mdx-netflix-com:service:target:3"
|
||||
)
|
||||
|
||||
assert ssdp.async_get_discovery_info_by_udn_st(hass, "wrong", "mock-st") is None
|
||||
|
||||
|
||||
_ADAPTERS_WITH_MANUAL_CONFIG = [
|
||||
{
|
||||
"auto": True,
|
||||
"default": False,
|
||||
"enabled": True,
|
||||
"ipv4": [],
|
||||
"ipv6": [
|
||||
{
|
||||
"address": "2001:db8::",
|
||||
"network_prefix": 8,
|
||||
"flowinfo": 1,
|
||||
"scope_id": 1,
|
||||
}
|
||||
],
|
||||
"name": "eth0",
|
||||
},
|
||||
{
|
||||
"auto": True,
|
||||
"default": False,
|
||||
"enabled": True,
|
||||
"ipv4": [{"address": "192.168.1.5", "network_prefix": 23}],
|
||||
"ipv6": [],
|
||||
"name": "eth1",
|
||||
},
|
||||
{
|
||||
"auto": False,
|
||||
"default": False,
|
||||
"enabled": False,
|
||||
"ipv4": [{"address": "169.254.3.2", "network_prefix": 16}],
|
||||
"ipv6": [],
|
||||
"name": "vtun0",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
async def test_async_detect_interfaces_setting_empty_route(hass):
|
||||
"""Test without default interface config and the route returns nothing."""
|
||||
mock_get_ssdp = {
|
||||
"mock-domain": [
|
||||
{
|
||||
ssdp.ATTR_UPNP_DEVICE_TYPE: "ABC",
|
||||
}
|
||||
]
|
||||
}
|
||||
create_args = []
|
||||
|
||||
def _generate_fake_ssdp_listener(*args, **kwargs):
|
||||
create_args.append([args, kwargs])
|
||||
listener = SSDPListener(*args, **kwargs)
|
||||
|
||||
async def _async_callback(*_):
|
||||
pass
|
||||
|
||||
@callback
|
||||
def _callback(*_):
|
||||
pass
|
||||
|
||||
listener.async_start = _async_callback
|
||||
listener.async_search = _callback
|
||||
return listener
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ssdp.async_get_ssdp",
|
||||
return_value=mock_get_ssdp,
|
||||
), patch(
|
||||
"homeassistant.components.ssdp.SSDPListener",
|
||||
new=_generate_fake_ssdp_listener,
|
||||
), patch(
|
||||
"homeassistant.components.ssdp.network.async_get_adapters",
|
||||
return_value=_ADAPTERS_WITH_MANUAL_CONFIG,
|
||||
):
|
||||
assert await async_setup_component(hass, ssdp.DOMAIN, {ssdp.DOMAIN: {}})
|
||||
await hass.async_block_till_done()
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert {create_args[0][1]["source_ip"], create_args[1][1]["source_ip"]} == {
|
||||
IPv4Address("192.168.1.5"),
|
||||
IPv6Address("2001:db8::"),
|
||||
}
|
||||
|
||||
@@ -364,7 +364,11 @@ async def test_discovery_requirements_ssdp(hass):
|
||||
assert len(mock_process.mock_calls) == 4
|
||||
assert mock_process.mock_calls[0][1][2] == ssdp.requirements
|
||||
# Ensure zeroconf is a dep for ssdp
|
||||
assert mock_process.mock_calls[1][1][1] == "zeroconf"
|
||||
assert {
|
||||
mock_process.mock_calls[1][1][1],
|
||||
mock_process.mock_calls[2][1][1],
|
||||
mock_process.mock_calls[3][1][1],
|
||||
} == {"network", "zeroconf", "http"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user