core/tests/components/xiaomi/test_device_tracker.py

257 lines
8.6 KiB
Python
Raw Normal View History

"""The tests for the Xiaomi router device tracker platform."""
from http import HTTPStatus
import logging
2021-01-01 21:31:56 +00:00
from unittest.mock import MagicMock, call, patch
import requests
Consolidate all platforms that have tests (#22109) * Moved climate components with tests into platform dirs. * Updated tests from climate component. * Moved binary_sensor components with tests into platform dirs. * Updated tests from binary_sensor component. * Moved calendar components with tests into platform dirs. * Updated tests from calendar component. * Moved camera components with tests into platform dirs. * Updated tests from camera component. * Moved cover components with tests into platform dirs. * Updated tests from cover component. * Moved device_tracker components with tests into platform dirs. * Updated tests from device_tracker component. * Moved fan components with tests into platform dirs. * Updated tests from fan component. * Moved geo_location components with tests into platform dirs. * Updated tests from geo_location component. * Moved image_processing components with tests into platform dirs. * Updated tests from image_processing component. * Moved light components with tests into platform dirs. * Updated tests from light component. * Moved lock components with tests into platform dirs. * Moved media_player components with tests into platform dirs. * Updated tests from media_player component. * Moved scene components with tests into platform dirs. * Moved sensor components with tests into platform dirs. * Updated tests from sensor component. * Moved switch components with tests into platform dirs. * Updated tests from sensor component. * Moved vacuum components with tests into platform dirs. * Updated tests from vacuum component. * Moved weather components with tests into platform dirs. * Fixed __init__.py files * Fixes for stuff moved as part of this branch. * Fix stuff needed to merge with balloob's branch. * Formatting issues. * Missing __init__.py files. * Fix-ups * Fixup * Regenerated requirements. * Linting errors fixed. * Fixed more broken tests. * Missing init files. * Fix broken tests. * More broken tests * There seems to be a thread race condition. I suspect the logger stuff is running in another thread, which means waiting until the aio loop is done is missing the log messages. Used sleep instead because that allows the logger thread to run. I think the api_streams sensor might not be thread safe. * Disabled tests, will remove sensor in #22147 * Updated coverage and codeowners.
2019-03-19 06:07:39 +00:00
from homeassistant.components.device_tracker import DOMAIN
import homeassistant.components.xiaomi.device_tracker as xiaomi
from homeassistant.components.xiaomi.device_tracker import get_scanner
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PLATFORM, CONF_USERNAME
_LOGGER = logging.getLogger(__name__)
2019-07-31 19:25:30 +00:00
INVALID_USERNAME = "bob"
TOKEN_TIMEOUT_USERNAME = "tok"
URL_AUTHORIZE = "http://192.168.0.1/cgi-bin/luci/api/xqsystem/login"
URL_LIST_END = "api/misystem/devicelist"
FIRST_CALL = True
def mocked_requests(*args, **kwargs):
"""Mock requests.get invocations."""
2019-07-31 19:25:30 +00:00
class MockResponse:
"""Class to represent a mocked response."""
def __init__(self, json_data, status_code):
"""Initialize the mock response class."""
self.json_data = json_data
self.status_code = status_code
def json(self):
"""Return the json of the response."""
return self.json_data
@property
def content(self):
"""Return the content of the response."""
return self.json()
def raise_for_status(self):
"""Raise an HTTPError if status is not OK."""
if self.status_code != HTTPStatus.OK:
raise requests.HTTPError(self.status_code)
2019-07-31 19:25:30 +00:00
data = kwargs.get("data")
global FIRST_CALL
2019-07-31 19:25:30 +00:00
if data and data.get("username", None) == INVALID_USERNAME:
# deliver an invalid token
2019-07-31 19:25:30 +00:00
return MockResponse({"code": "401", "msg": "Invalid token"}, 200)
if data and data.get("username", None) == TOKEN_TIMEOUT_USERNAME:
# deliver an expired token
2019-07-31 19:25:30 +00:00
return MockResponse(
{
"url": "/cgi-bin/luci/;stok=ef5860/web/home",
"token": "timedOut",
"code": "0",
},
200,
)
if str(args[0]).startswith(URL_AUTHORIZE):
# deliver an authorized token
2019-07-31 19:25:30 +00:00
return MockResponse(
{
"url": "/cgi-bin/luci/;stok=ef5860/web/home",
"token": "ef5860",
"code": "0",
},
200,
)
2020-04-07 21:14:28 +00:00
if str(args[0]).endswith(f"timedOut/{URL_LIST_END}") and FIRST_CALL is True:
FIRST_CALL = False
# deliver an error when called with expired token
2019-07-31 19:25:30 +00:00
return MockResponse({"code": "401", "msg": "Invalid token"}, 200)
if str(args[0]).endswith(URL_LIST_END):
# deliver the device list
2019-07-31 19:25:30 +00:00
return MockResponse(
{
"mac": "1C:98:EC:0E:D5:A4",
"list": [
{
"mac": "23:83:BF:F6:38:A0",
"oname": "12255ff",
"isap": 0,
"parent": "",
"authority": {"wan": 1, "pridisk": 0, "admin": 1, "lan": 0},
"push": 0,
"online": 1,
"name": "Device1",
"times": 0,
"ip": [
{
"downspeed": "0",
"online": "496957",
"active": 1,
"upspeed": "0",
"ip": "192.168.0.25",
}
],
"statistics": {
"downspeed": "0",
"online": "496957",
"upspeed": "0",
2019-07-31 19:25:30 +00:00
},
"icon": "",
"type": 1,
},
2019-07-31 19:25:30 +00:00
{
"mac": "1D:98:EC:5E:D5:A6",
"oname": "CdddFG58",
"isap": 0,
"parent": "",
"authority": {"wan": 1, "pridisk": 0, "admin": 1, "lan": 0},
"push": 0,
"online": 1,
"name": "Device2",
"times": 0,
"ip": [
{
"downspeed": "0",
"online": "347325",
"active": 1,
"upspeed": "0",
"ip": "192.168.0.3",
}
],
"statistics": {
"downspeed": "0",
"online": "347325",
"upspeed": "0",
2019-07-31 19:25:30 +00:00
},
"icon": "",
"type": 0,
},
2019-07-31 19:25:30 +00:00
],
"code": 0,
},
200,
)
_LOGGER.debug("UNKNOWN ROUTE")
@patch(
2019-07-31 19:25:30 +00:00
"homeassistant.components.xiaomi.device_tracker.XiaomiDeviceScanner",
return_value=MagicMock(),
2019-07-31 19:25:30 +00:00
)
async def test_config(xiaomi_mock, hass):
"""Testing minimal configuration."""
config = {
2019-07-31 19:25:30 +00:00
DOMAIN: xiaomi.PLATFORM_SCHEMA(
{
CONF_PLATFORM: xiaomi.DOMAIN,
CONF_HOST: "192.168.0.1",
CONF_PASSWORD: "passwordTest",
}
)
}
xiaomi.get_scanner(hass, config)
assert xiaomi_mock.call_count == 1
assert xiaomi_mock.call_args == call(config[DOMAIN])
call_arg = xiaomi_mock.call_args[0][0]
2019-07-31 19:25:30 +00:00
assert call_arg["username"] == "admin"
assert call_arg["password"] == "passwordTest"
assert call_arg["host"] == "192.168.0.1"
assert call_arg["platform"] == "device_tracker"
@patch(
2019-07-31 19:25:30 +00:00
"homeassistant.components.xiaomi.device_tracker.XiaomiDeviceScanner",
return_value=MagicMock(),
2019-07-31 19:25:30 +00:00
)
async def test_config_full(xiaomi_mock, hass):
"""Testing full configuration."""
config = {
2019-07-31 19:25:30 +00:00
DOMAIN: xiaomi.PLATFORM_SCHEMA(
{
CONF_PLATFORM: xiaomi.DOMAIN,
CONF_HOST: "192.168.0.1",
CONF_USERNAME: "alternativeAdminName",
CONF_PASSWORD: "passwordTest",
}
)
}
xiaomi.get_scanner(hass, config)
assert xiaomi_mock.call_count == 1
assert xiaomi_mock.call_args == call(config[DOMAIN])
call_arg = xiaomi_mock.call_args[0][0]
2019-07-31 19:25:30 +00:00
assert call_arg["username"] == "alternativeAdminName"
assert call_arg["password"] == "passwordTest"
assert call_arg["host"] == "192.168.0.1"
assert call_arg["platform"] == "device_tracker"
2019-07-31 19:25:30 +00:00
@patch("requests.get", side_effect=mocked_requests)
@patch("requests.post", side_effect=mocked_requests)
async def test_invalid_credential(mock_get, mock_post, hass):
"""Testing invalid credential handling."""
config = {
2019-07-31 19:25:30 +00:00
DOMAIN: xiaomi.PLATFORM_SCHEMA(
{
CONF_PLATFORM: xiaomi.DOMAIN,
CONF_HOST: "192.168.0.1",
CONF_USERNAME: INVALID_USERNAME,
CONF_PASSWORD: "passwordTest",
}
)
}
assert get_scanner(hass, config) is None
2019-07-31 19:25:30 +00:00
@patch("requests.get", side_effect=mocked_requests)
@patch("requests.post", side_effect=mocked_requests)
async def test_valid_credential(mock_get, mock_post, hass):
"""Testing valid refresh."""
config = {
2019-07-31 19:25:30 +00:00
DOMAIN: xiaomi.PLATFORM_SCHEMA(
{
CONF_PLATFORM: xiaomi.DOMAIN,
CONF_HOST: "192.168.0.1",
CONF_USERNAME: "admin",
CONF_PASSWORD: "passwordTest",
}
)
}
scanner = get_scanner(hass, config)
assert scanner is not None
assert len(scanner.scan_devices()) == 2
assert scanner.get_device_name("23:83:BF:F6:38:A0") == "Device1"
assert scanner.get_device_name("1D:98:EC:5E:D5:A6") == "Device2"
2019-07-31 19:25:30 +00:00
@patch("requests.get", side_effect=mocked_requests)
@patch("requests.post", side_effect=mocked_requests)
async def test_token_timed_out(mock_get, mock_post, hass):
"""Testing refresh with a timed out token.
New token is requested and list is downloaded a second time.
"""
config = {
2019-07-31 19:25:30 +00:00
DOMAIN: xiaomi.PLATFORM_SCHEMA(
{
CONF_PLATFORM: xiaomi.DOMAIN,
CONF_HOST: "192.168.0.1",
CONF_USERNAME: TOKEN_TIMEOUT_USERNAME,
CONF_PASSWORD: "passwordTest",
}
)
}
scanner = get_scanner(hass, config)
assert scanner is not None
assert len(scanner.scan_devices()) == 2
assert scanner.get_device_name("23:83:BF:F6:38:A0") == "Device1"
assert scanner.get_device_name("1D:98:EC:5E:D5:A6") == "Device2"