2019-05-21 22:36:26 +00:00
""" Test Zeroconf component setup process. """
2021-01-01 21:31:56 +00:00
from unittest . mock import patch
2021-05-18 03:51:05 +00:00
from zeroconf import InterfaceChoice , IPVersion , ServiceInfo , ServiceStateChange
2019-05-21 22:36:26 +00:00
2019-12-09 17:54:54 +00:00
from homeassistant . components import zeroconf
2020-07-15 18:26:40 +00:00
from homeassistant . components . zeroconf import CONF_DEFAULT_INTERFACE , CONF_IPV6
2020-09-13 23:06:19 +00:00
from homeassistant . const import (
EVENT_HOMEASSISTANT_START ,
EVENT_HOMEASSISTANT_STARTED ,
EVENT_HOMEASSISTANT_STOP ,
)
2019-05-29 18:19:50 +00:00
from homeassistant . generated import zeroconf as zc_gen
2019-05-21 22:36:26 +00:00
from homeassistant . setup import async_setup_component
2020-04-18 00:36:46 +00:00
NON_UTF8_VALUE = b " ABCDEF \x8a "
NON_ASCII_KEY = b " non-ascii-key \x8a "
PROPERTIES = {
b " macaddress " : b " ABCDEF012345 " ,
b " non-utf8-value " : NON_UTF8_VALUE ,
NON_ASCII_KEY : None ,
}
2020-05-14 16:58:40 +00:00
HOMEKIT_STATUS_UNPAIRED = b " 1 "
HOMEKIT_STATUS_PAIRED = b " 0 "
2020-05-12 19:59:29 +00:00
2021-04-22 05:10:34 +00:00
_ROUTE_NO_LOOPBACK = (
{
" attrs " : [
( " RTA_TABLE " , 254 ) ,
( " RTA_DST " , " 224.0.0.251 " ) ,
( " RTA_OIF " , 4 ) ,
( " RTA_PREFSRC " , " 192.168.1.5 " ) ,
] ,
} ,
)
_ROUTE_LOOPBACK = (
{
" attrs " : [
( " RTA_TABLE " , 254 ) ,
( " RTA_DST " , " 224.0.0.251 " ) ,
( " RTA_OIF " , 4 ) ,
( " RTA_PREFSRC " , " 127.0.0.1 " ) ,
] ,
} ,
)
2019-05-21 22:36:26 +00:00
2021-01-07 18:44:34 +00:00
def service_update_mock ( zeroconf , services , handlers , * , limit_service = None ) :
2019-05-21 22:36:26 +00:00
""" Call service update handler. """
2020-05-23 16:05:41 +00:00
for service in services :
2021-01-07 18:44:34 +00:00
if limit_service is not None and service != limit_service :
continue
2021-04-28 09:47:36 +00:00
handlers [ 0 ] ( zeroconf , service , f " _name. { service } " , ServiceStateChange . Added )
2019-05-21 22:36:26 +00:00
2019-05-29 21:20:06 +00:00
def get_service_info_mock ( service_type , name ) :
2019-05-21 22:36:26 +00:00
""" Return service info for get_service_info. """
return ServiceInfo (
2019-07-31 19:25:30 +00:00
service_type ,
name ,
2020-06-05 21:33:26 +00:00
addresses = [ b " \n \x00 \x00 \x14 " ] ,
2019-07-31 19:25:30 +00:00
port = 80 ,
weight = 0 ,
priority = 0 ,
server = " name.local. " ,
2020-04-18 00:36:46 +00:00
properties = PROPERTIES ,
2019-07-31 19:25:30 +00:00
)
2019-05-21 22:36:26 +00:00
2020-07-23 06:21:57 +00:00
def get_service_info_mock_without_an_address ( service_type , name ) :
""" Return service info for get_service_info without any addresses. """
return ServiceInfo (
service_type ,
name ,
addresses = [ ] ,
port = 80 ,
weight = 0 ,
priority = 0 ,
server = " name.local. " ,
properties = PROPERTIES ,
)
2020-05-12 19:59:29 +00:00
def get_homekit_info_mock ( model , pairing_status ) :
""" Return homekit info for get_service_info for an homekit device. """
2019-07-31 19:25:30 +00:00
2019-06-08 05:59:51 +00:00
def mock_homekit_info ( service_type , name ) :
return ServiceInfo (
2019-07-31 19:25:30 +00:00
service_type ,
name ,
2020-06-05 21:33:26 +00:00
addresses = [ b " \n \x00 \x00 \x14 " ] ,
2019-07-31 19:25:30 +00:00
port = 80 ,
weight = 0 ,
priority = 0 ,
server = " name.local. " ,
2020-05-12 19:59:29 +00:00
properties = { b " md " : model . encode ( ) , b " sf " : pairing_status } ,
2019-07-31 19:25:30 +00:00
)
2019-06-08 05:59:51 +00:00
return mock_homekit_info
2019-05-21 22:36:26 +00:00
2020-09-11 10:19:21 +00:00
def get_zeroconf_info_mock ( macaddress ) :
""" Return info for get_service_info for an zeroconf device. """
def mock_zc_info ( service_type , name ) :
return ServiceInfo (
service_type ,
name ,
addresses = [ b " \n \x00 \x00 \x14 " ] ,
port = 80 ,
weight = 0 ,
priority = 0 ,
server = " name.local. " ,
properties = { b " macaddress " : macaddress . encode ( ) } ,
)
return mock_zc_info
2021-04-08 19:03:10 +00:00
def get_zeroconf_info_mock_manufacturer ( manufacturer ) :
""" Return info for get_service_info for an zeroconf device. """
def mock_zc_info ( service_type , name ) :
return ServiceInfo (
service_type ,
name ,
addresses = [ b " \n \x00 \x00 \x14 " ] ,
port = 80 ,
weight = 0 ,
priority = 0 ,
server = " name.local. " ,
properties = { b " manufacturer " : manufacturer . encode ( ) } ,
)
return mock_zc_info
2019-05-31 18:58:48 +00:00
async def test_setup ( hass , mock_zeroconf ) :
""" Test configured options for a device are loaded via config entry. """
2020-04-15 01:46:41 +00:00
with patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
2020-05-11 23:30:15 +00:00
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_service_info_mock ,
) :
2019-07-31 19:25:30 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
2020-07-22 00:18:43 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2019-05-21 22:36:26 +00:00
2020-05-23 16:05:41 +00:00
assert len ( mock_service_browser . mock_calls ) == 1
2020-03-23 09:14:21 +00:00
expected_flow_calls = 0
for matching_components in zc_gen . ZEROCONF . values ( ) :
2020-09-11 10:19:21 +00:00
domains = set ( )
for component in matching_components :
if len ( component ) == 1 :
domains . add ( component [ " domain " ] )
expected_flow_calls + = len ( domains )
2020-04-24 22:09:45 +00:00
assert len ( mock_config_flow . mock_calls ) == expected_flow_calls
2019-05-31 18:58:48 +00:00
2020-05-11 05:51:23 +00:00
# Test instance is set.
assert " zeroconf " in hass . data
assert await hass . components . zeroconf . async_get_instance ( ) is mock_zeroconf
2019-05-31 18:58:48 +00:00
2020-08-21 12:31:17 +00:00
async def test_setup_with_overly_long_url_and_name ( hass , mock_zeroconf , caplog ) :
""" Test we still setup with long urls and names. """
with patch . object ( hass . config_entries . flow , " async_init " ) , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
2020-09-13 23:06:19 +00:00
) , patch (
2020-08-21 12:31:17 +00:00
" homeassistant.components.zeroconf.get_url " ,
return_value = " https://this.url.is.way.too.long/very/deep/path/that/will/make/us/go/over/the/maximum/string/length/and/would/cause/zeroconf/to/fail/to/startup/because/the/key/and/value/can/only/be/255/bytes/and/this/string/is/a/bit/longer/than/the/maximum/length/that/we/allow/for/a/value " ,
) , patch . object (
hass . config ,
" location_name " ,
" \u00dc BER \u00dc ber German Umlaut long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string " ,
2021-05-18 03:51:05 +00:00
) , patch (
" homeassistant.components.zeroconf.ServiceInfo.request " ,
2020-08-21 12:31:17 +00:00
) :
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
2020-09-13 23:06:19 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_START )
2020-08-21 12:31:17 +00:00
await hass . async_block_till_done ( )
assert " https://this.url.is.way.too.long " in caplog . text
assert " German Umlaut " in caplog . text
2020-05-06 14:16:59 +00:00
async def test_setup_with_default_interface ( hass , mock_zeroconf ) :
""" Test default interface config. """
with patch . object ( hass . config_entries . flow , " async_init " ) , patch . object (
2020-05-11 23:30:15 +00:00
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
2021-05-18 03:51:05 +00:00
) , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_service_info_mock ,
2020-05-06 14:16:59 +00:00
) :
assert await async_setup_component (
hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { CONF_DEFAULT_INTERFACE : True } }
)
2020-07-22 00:18:43 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2020-05-06 14:16:59 +00:00
assert mock_zeroconf . called_with ( interface_choice = InterfaceChoice . Default )
2020-05-10 19:30:54 +00:00
async def test_setup_without_default_interface ( hass , mock_zeroconf ) :
""" Test without default interface config. """
with patch . object ( hass . config_entries . flow , " async_init " ) , patch . object (
2020-05-11 23:30:15 +00:00
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
2021-05-18 03:51:05 +00:00
) , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_service_info_mock ,
2020-05-10 19:30:54 +00:00
) :
assert await async_setup_component (
hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { CONF_DEFAULT_INTERFACE : False } }
)
assert mock_zeroconf . called_with ( )
2020-07-15 18:26:40 +00:00
async def test_setup_without_ipv6 ( hass , mock_zeroconf ) :
""" Test without ipv6. """
with patch . object ( hass . config_entries . flow , " async_init " ) , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
2021-05-18 03:51:05 +00:00
) , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_service_info_mock ,
2020-07-15 18:26:40 +00:00
) :
assert await async_setup_component (
hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { CONF_IPV6 : False } }
)
2020-07-22 00:18:43 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2020-07-15 18:26:40 +00:00
assert mock_zeroconf . called_with ( ip_version = IPVersion . V4Only )
async def test_setup_with_ipv6 ( hass , mock_zeroconf ) :
""" Test without ipv6. """
with patch . object ( hass . config_entries . flow , " async_init " ) , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
2021-05-18 03:51:05 +00:00
) , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_service_info_mock ,
2020-07-15 18:26:40 +00:00
) :
assert await async_setup_component (
hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { CONF_IPV6 : True } }
)
2020-07-22 00:18:43 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2020-07-15 18:26:40 +00:00
assert mock_zeroconf . called_with ( )
async def test_setup_with_ipv6_default ( hass , mock_zeroconf ) :
""" Test without ipv6 as default. """
with patch . object ( hass . config_entries . flow , " async_init " ) , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
2021-05-18 03:51:05 +00:00
) , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_service_info_mock ,
2020-07-15 18:26:40 +00:00
) :
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
2020-07-22 00:18:43 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2020-07-15 18:26:40 +00:00
assert mock_zeroconf . called_with ( )
2021-04-08 19:03:10 +00:00
async def test_zeroconf_match_macaddress ( hass , mock_zeroconf ) :
2020-09-11 10:19:21 +00:00
""" Test configured options for a device are loaded via config entry. """
def http_only_service_update_mock ( zeroconf , services , handlers ) :
""" Call service update handler. """
handlers [ 0 ] (
zeroconf ,
" _http._tcp.local. " ,
2020-12-31 00:06:26 +00:00
" Shelly108._http._tcp.local. " ,
2020-09-11 10:19:21 +00:00
ServiceStateChange . Added ,
)
with patch . dict (
zc_gen . ZEROCONF ,
2020-12-31 00:06:26 +00:00
{
" _http._tcp.local. " : [
{ " domain " : " shelly " , " name " : " shelly* " , " macaddress " : " FFAADD* " }
]
} ,
2020-09-11 10:19:21 +00:00
clear = True ,
) , patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = http_only_service_update_mock
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_zeroconf_info_mock ( " FFAADDCC11DD " ) ,
) :
2020-09-11 10:19:21 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
assert len ( mock_service_browser . mock_calls ) == 1
assert len ( mock_config_flow . mock_calls ) == 1
assert mock_config_flow . mock_calls [ 0 ] [ 1 ] [ 0 ] == " shelly "
2021-04-08 19:03:10 +00:00
async def test_zeroconf_match_manufacturer ( hass , mock_zeroconf ) :
""" Test configured options for a device are loaded via config entry. """
def http_only_service_update_mock ( zeroconf , services , handlers ) :
""" Call service update handler. """
handlers [ 0 ] (
zeroconf ,
" _airplay._tcp.local. " ,
" s1000._airplay._tcp.local. " ,
ServiceStateChange . Added ,
)
with patch . dict (
zc_gen . ZEROCONF ,
{ " _airplay._tcp.local. " : [ { " domain " : " samsungtv " , " manufacturer " : " samsung* " } ] } ,
clear = True ,
) , patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = http_only_service_update_mock
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_zeroconf_info_mock_manufacturer ( " Samsung Electronics " ) ,
) :
2021-04-08 19:03:10 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
assert len ( mock_service_browser . mock_calls ) == 1
assert len ( mock_config_flow . mock_calls ) == 1
assert mock_config_flow . mock_calls [ 0 ] [ 1 ] [ 0 ] == " samsungtv "
2021-05-12 04:24:42 +00:00
async def test_zeroconf_match_manufacturer_not_present ( hass , mock_zeroconf ) :
""" Test matchers reject when a property is missing. """
def http_only_service_update_mock ( zeroconf , services , handlers ) :
""" Call service update handler. """
handlers [ 0 ] (
zeroconf ,
" _airplay._tcp.local. " ,
" s1000._airplay._tcp.local. " ,
ServiceStateChange . Added ,
)
with patch . dict (
zc_gen . ZEROCONF ,
{ " _airplay._tcp.local. " : [ { " domain " : " samsungtv " , " manufacturer " : " samsung* " } ] } ,
clear = True ,
) , patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = http_only_service_update_mock
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_zeroconf_info_mock ( " aabbccddeeff " ) ,
) :
2021-05-12 04:24:42 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
assert len ( mock_service_browser . mock_calls ) == 1
assert len ( mock_config_flow . mock_calls ) == 0
2020-09-11 10:19:21 +00:00
async def test_zeroconf_no_match ( hass , mock_zeroconf ) :
""" Test configured options for a device are loaded via config entry. """
def http_only_service_update_mock ( zeroconf , services , handlers ) :
""" Call service update handler. """
handlers [ 0 ] (
zeroconf ,
" _http._tcp.local. " ,
" somethingelse._http._tcp.local. " ,
ServiceStateChange . Added ,
)
with patch . dict (
zc_gen . ZEROCONF ,
{ " _http._tcp.local. " : [ { " domain " : " shelly " , " name " : " shelly* " } ] } ,
clear = True ,
) , patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = http_only_service_update_mock
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_zeroconf_info_mock ( " FFAADDCC11DD " ) ,
) :
2020-09-11 10:19:21 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
2021-04-08 19:03:10 +00:00
await hass . async_block_till_done ( )
assert len ( mock_service_browser . mock_calls ) == 1
assert len ( mock_config_flow . mock_calls ) == 0
async def test_zeroconf_no_match_manufacturer ( hass , mock_zeroconf ) :
""" Test configured options for a device are loaded via config entry. """
def http_only_service_update_mock ( zeroconf , services , handlers ) :
""" Call service update handler. """
handlers [ 0 ] (
zeroconf ,
" _airplay._tcp.local. " ,
" s1000._airplay._tcp.local. " ,
ServiceStateChange . Added ,
)
with patch . dict (
zc_gen . ZEROCONF ,
{ " _airplay._tcp.local. " : [ { " domain " : " samsungtv " , " manufacturer " : " samsung* " } ] } ,
clear = True ,
) , patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = http_only_service_update_mock
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_zeroconf_info_mock_manufacturer ( " Not Samsung Electronics " ) ,
) :
2021-04-08 19:03:10 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
2020-09-11 10:19:21 +00:00
await hass . async_block_till_done ( )
assert len ( mock_service_browser . mock_calls ) == 1
assert len ( mock_config_flow . mock_calls ) == 0
2020-03-26 22:15:35 +00:00
async def test_homekit_match_partial_space ( hass , mock_zeroconf ) :
2019-05-31 18:58:48 +00:00
""" Test configured options for a device are loaded via config entry. """
with patch . dict (
2020-09-11 10:19:21 +00:00
zc_gen . ZEROCONF ,
2021-01-07 18:44:34 +00:00
{ " _hap._tcp.local. " : [ { " domain " : " homekit_controller " } ] } ,
2020-09-11 10:19:21 +00:00
clear = True ,
2020-04-15 01:46:41 +00:00
) , patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
2021-01-07 18:44:34 +00:00
zeroconf ,
" HaServiceBrowser " ,
side_effect = lambda * args , * * kwargs : service_update_mock (
* args , * * kwargs , limit_service = " _hap._tcp.local. "
) ,
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_homekit_info_mock ( " LIFX bulb " , HOMEKIT_STATUS_UNPAIRED ) ,
) :
2019-07-31 19:25:30 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
2020-07-22 00:18:43 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2019-05-31 18:58:48 +00:00
assert len ( mock_service_browser . mock_calls ) == 1
2020-04-24 22:09:45 +00:00
assert len ( mock_config_flow . mock_calls ) == 1
2019-07-31 19:25:30 +00:00
assert mock_config_flow . mock_calls [ 0 ] [ 1 ] [ 0 ] == " lifx "
2019-06-08 05:59:51 +00:00
2020-03-26 22:15:35 +00:00
async def test_homekit_match_partial_dash ( hass , mock_zeroconf ) :
""" Test configured options for a device are loaded via config entry. """
with patch . dict (
2020-09-11 10:19:21 +00:00
zc_gen . ZEROCONF ,
2021-01-07 18:44:34 +00:00
{ " _hap._udp.local. " : [ { " domain " : " homekit_controller " } ] } ,
2020-09-11 10:19:21 +00:00
clear = True ,
2020-04-15 01:46:41 +00:00
) , patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
2021-01-07 18:44:34 +00:00
zeroconf ,
" HaServiceBrowser " ,
side_effect = lambda * args , * * kwargs : service_update_mock (
* args , * * kwargs , limit_service = " _hap._udp.local. "
) ,
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_homekit_info_mock ( " Rachio-fa46ba " , HOMEKIT_STATUS_UNPAIRED ) ,
) :
2020-03-26 22:15:35 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
2020-07-22 00:18:43 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2020-03-26 22:15:35 +00:00
assert len ( mock_service_browser . mock_calls ) == 1
2020-04-24 22:09:45 +00:00
assert len ( mock_config_flow . mock_calls ) == 1
2020-03-26 22:15:35 +00:00
assert mock_config_flow . mock_calls [ 0 ] [ 1 ] [ 0 ] == " rachio "
2019-06-08 05:59:51 +00:00
async def test_homekit_match_full ( hass , mock_zeroconf ) :
""" Test configured options for a device are loaded via config entry. """
with patch . dict (
2020-09-11 10:19:21 +00:00
zc_gen . ZEROCONF ,
2021-01-07 18:44:34 +00:00
{ " _hap._udp.local. " : [ { " domain " : " homekit_controller " } ] } ,
2020-09-11 10:19:21 +00:00
clear = True ,
2020-04-15 01:46:41 +00:00
) , patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
2021-01-07 18:44:34 +00:00
zeroconf ,
" HaServiceBrowser " ,
side_effect = lambda * args , * * kwargs : service_update_mock (
* args , * * kwargs , limit_service = " _hap._udp.local. "
) ,
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_homekit_info_mock ( " BSB002 " , HOMEKIT_STATUS_UNPAIRED ) ,
) :
2019-07-31 19:25:30 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
2020-07-22 00:18:43 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2019-06-08 05:59:51 +00:00
assert len ( mock_service_browser . mock_calls ) == 1
2020-04-24 22:09:45 +00:00
assert len ( mock_config_flow . mock_calls ) == 1
2019-07-31 19:25:30 +00:00
assert mock_config_flow . mock_calls [ 0 ] [ 1 ] [ 0 ] == " hue "
2020-01-22 05:24:59 +00:00
2020-05-12 19:59:29 +00:00
async def test_homekit_already_paired ( hass , mock_zeroconf ) :
""" Test that an already paired device is sent to homekit_controller. """
with patch . dict (
2020-09-11 10:19:21 +00:00
zc_gen . ZEROCONF ,
2021-01-07 18:44:34 +00:00
{ " _hap._tcp.local. " : [ { " domain " : " homekit_controller " } ] } ,
2020-09-11 10:19:21 +00:00
clear = True ,
2020-05-12 19:59:29 +00:00
) , patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
2021-01-07 18:44:34 +00:00
zeroconf ,
" HaServiceBrowser " ,
side_effect = lambda * args , * * kwargs : service_update_mock (
* args , * * kwargs , limit_service = " _hap._tcp.local. "
) ,
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_homekit_info_mock ( " tado " , HOMEKIT_STATUS_PAIRED ) ,
) :
2020-05-12 19:59:29 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
2020-07-22 00:18:43 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2020-05-12 19:59:29 +00:00
assert len ( mock_service_browser . mock_calls ) == 1
assert len ( mock_config_flow . mock_calls ) == 2
assert mock_config_flow . mock_calls [ 0 ] [ 1 ] [ 0 ] == " tado "
assert mock_config_flow . mock_calls [ 1 ] [ 1 ] [ 0 ] == " homekit_controller "
async def test_homekit_invalid_paring_status ( hass , mock_zeroconf ) :
""" Test that missing paring data is not sent to homekit_controller. """
with patch . dict (
2020-09-11 10:19:21 +00:00
zc_gen . ZEROCONF ,
2021-01-07 18:44:34 +00:00
{ " _hap._tcp.local. " : [ { " domain " : " homekit_controller " } ] } ,
2020-09-11 10:19:21 +00:00
clear = True ,
2020-05-12 19:59:29 +00:00
) , patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
2021-01-07 18:44:34 +00:00
zeroconf ,
" HaServiceBrowser " ,
side_effect = lambda * args , * * kwargs : service_update_mock (
* args , * * kwargs , limit_service = " _hap._tcp.local. "
) ,
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_homekit_info_mock ( " tado " , b " invalid " ) ,
) :
2020-05-12 19:59:29 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
2020-07-22 00:18:43 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2020-05-12 19:59:29 +00:00
assert len ( mock_service_browser . mock_calls ) == 1
assert len ( mock_config_flow . mock_calls ) == 1
assert mock_config_flow . mock_calls [ 0 ] [ 1 ] [ 0 ] == " tado "
2020-07-14 00:45:05 +00:00
async def test_homekit_not_paired ( hass , mock_zeroconf ) :
""" Test that an not paired device is sent to homekit_controller. """
with patch . dict (
2020-09-11 10:19:21 +00:00
zc_gen . ZEROCONF ,
2021-01-07 18:44:34 +00:00
{ " _hap._tcp.local. " : [ { " domain " : " homekit_controller " } ] } ,
2020-09-11 10:19:21 +00:00
clear = True ,
2020-07-14 00:45:05 +00:00
) , patch . object (
hass . config_entries . flow , " async_init "
) as mock_config_flow , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
2021-05-18 03:51:05 +00:00
) as mock_service_browser , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_homekit_info_mock (
2020-07-14 00:45:05 +00:00
" this_will_not_match_any_integration " , HOMEKIT_STATUS_UNPAIRED
2021-05-18 03:51:05 +00:00
) ,
) :
2020-07-14 00:45:05 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
2020-07-22 00:18:43 +00:00
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2020-07-14 00:45:05 +00:00
assert len ( mock_service_browser . mock_calls ) == 1
assert len ( mock_config_flow . mock_calls ) == 1
assert mock_config_flow . mock_calls [ 0 ] [ 1 ] [ 0 ] == " homekit_controller "
2020-01-22 05:24:59 +00:00
async def test_info_from_service_non_utf8 ( hass ) :
2020-04-18 00:36:46 +00:00
""" Test info_from_service handles non UTF-8 property keys and values correctly. """
2020-01-22 05:24:59 +00:00
service_type = " _test._tcp.local. "
info = zeroconf . info_from_service (
get_service_info_mock ( service_type , f " test. { service_type } " )
)
raw_info = info [ " properties " ] . pop ( " _raw " , False )
assert raw_info
2020-04-18 00:36:46 +00:00
assert len ( raw_info ) == len ( PROPERTIES ) - 1
assert NON_ASCII_KEY not in raw_info
2020-01-22 05:24:59 +00:00
assert len ( info [ " properties " ] ) < = len ( raw_info )
assert " non-utf8-value " not in info [ " properties " ]
2020-04-18 00:36:46 +00:00
assert raw_info [ " non-utf8-value " ] is NON_UTF8_VALUE
2020-05-11 05:51:23 +00:00
2020-07-23 06:21:57 +00:00
async def test_info_from_service_with_addresses ( hass ) :
""" Test info_from_service does not throw when there are no addresses. """
service_type = " _test._tcp.local. "
info = zeroconf . info_from_service (
get_service_info_mock_without_an_address ( service_type , f " test. { service_type } " )
)
assert info is None
2020-05-11 05:51:23 +00:00
async def test_get_instance ( hass , mock_zeroconf ) :
""" Test we get an instance. """
2020-09-13 23:06:19 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
2020-05-11 05:51:23 +00:00
assert await hass . components . zeroconf . async_get_instance ( ) is mock_zeroconf
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STOP )
await hass . async_block_till_done ( )
assert len ( mock_zeroconf . ha_close . mock_calls ) == 1
2021-03-09 20:14:00 +00:00
async def test_removed_ignored ( hass , mock_zeroconf ) :
""" Test we remove it when a zeroconf entry is removed. """
def service_update_mock ( zeroconf , services , handlers ) :
""" Call service update handler. """
handlers [ 0 ] (
2021-05-18 03:51:05 +00:00
zeroconf ,
" _service.added.local. " ,
" name._service.added.local. " ,
ServiceStateChange . Added ,
2021-03-09 20:14:00 +00:00
)
handlers [ 0 ] (
zeroconf ,
2021-05-18 03:51:05 +00:00
" _service.updated.local. " ,
" name._service.updated.local. " ,
2021-03-09 20:14:00 +00:00
ServiceStateChange . Updated ,
)
handlers [ 0 ] (
zeroconf ,
2021-05-18 03:51:05 +00:00
" _service.removed.local. " ,
" name._service.removed.local. " ,
2021-03-09 20:14:00 +00:00
ServiceStateChange . Removed ,
)
2021-05-18 03:51:05 +00:00
with patch . object (
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
) , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_service_info_mock ,
) as mock_service_info :
2021-03-09 20:14:00 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
2021-05-18 03:51:05 +00:00
assert len ( mock_service_info . mock_calls ) == 2
import pprint
pprint . pprint ( mock_service_info . mock_calls [ 0 ] [ 1 ] )
assert mock_service_info . mock_calls [ 0 ] [ 1 ] [ 0 ] == " _service.added.local. "
assert mock_service_info . mock_calls [ 1 ] [ 1 ] [ 0 ] == " _service.updated.local. "
2021-04-22 05:10:34 +00:00
async def test_async_detect_interfaces_setting_non_loopback_route ( hass , mock_zeroconf ) :
""" Test without default interface config and the route returns a non-loopback address. """
with patch . object ( hass . config_entries . flow , " async_init " ) , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
) , patch (
" homeassistant.components.zeroconf.IPRoute.route " ,
return_value = _ROUTE_NO_LOOPBACK ,
2021-05-18 03:51:05 +00:00
) , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_service_info_mock ,
2021-04-22 05:10:34 +00:00
) :
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
assert mock_zeroconf . called_with ( interface_choice = InterfaceChoice . Default )
async def test_async_detect_interfaces_setting_loopback_route ( hass , mock_zeroconf ) :
""" Test without default interface config and the route returns a loopback address. """
with patch . object ( hass . config_entries . flow , " async_init " ) , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
) , patch (
" homeassistant.components.zeroconf.IPRoute.route " , return_value = _ROUTE_LOOPBACK
2021-05-18 03:51:05 +00:00
) , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_service_info_mock ,
2021-04-22 05:10:34 +00:00
) :
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
assert mock_zeroconf . called_with ( interface_choice = InterfaceChoice . All )
async def test_async_detect_interfaces_setting_empty_route ( hass , mock_zeroconf ) :
""" Test without default interface config and the route returns nothing. """
with patch . object ( hass . config_entries . flow , " async_init " ) , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
2021-05-18 03:51:05 +00:00
) , patch ( " homeassistant.components.zeroconf.IPRoute.route " , return_value = [ ] ) , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_service_info_mock ,
) :
2021-04-22 05:10:34 +00:00
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
assert mock_zeroconf . called_with ( interface_choice = InterfaceChoice . All )
async def test_async_detect_interfaces_setting_exception ( hass , mock_zeroconf ) :
""" Test without default interface config and the route throws an exception. """
with patch . object ( hass . config_entries . flow , " async_init " ) , patch . object (
zeroconf , " HaServiceBrowser " , side_effect = service_update_mock
) , patch (
" homeassistant.components.zeroconf.IPRoute.route " , side_effect = AttributeError
2021-05-18 03:51:05 +00:00
) , patch (
" homeassistant.components.zeroconf.ServiceInfo " ,
side_effect = get_service_info_mock ,
2021-04-22 05:10:34 +00:00
) :
assert await async_setup_component ( hass , zeroconf . DOMAIN , { zeroconf . DOMAIN : { } } )
hass . bus . async_fire ( EVENT_HOMEASSISTANT_STARTED )
await hass . async_block_till_done ( )
assert mock_zeroconf . called_with ( interface_choice = InterfaceChoice . All )