core/tests/components/device_tracker/test_xiaomi.py

266 lines
9.6 KiB
Python
Raw Normal View History

"""The tests for the Xiaomi router device tracker platform."""
import logging
import unittest
from unittest import mock
from unittest.mock import patch
import requests
from homeassistant.components.device_tracker import DOMAIN, xiaomi as xiaomi
from homeassistant.components.device_tracker.xiaomi import get_scanner
from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD,
CONF_PLATFORM)
from tests.common import get_test_home_assistant
_LOGGER = logging.getLogger(__name__)
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."""
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 200."""
if self.status_code != 200:
raise requests.HTTPError(self.status_code)
data = kwargs.get('data')
global FIRST_CALL
if data and data.get('username', None) == INVALID_USERNAME:
# deliver an invalid token
return MockResponse({
"code": "401",
"msg": "Invalid token"
}, 200)
elif data and data.get('username', None) == TOKEN_TIMEOUT_USERNAME:
# deliver an expired token
return MockResponse({
"url": "/cgi-bin/luci/;stok=ef5860/web/home",
"token": "timedOut",
"code": "0"
}, 200)
elif str(args[0]).startswith(URL_AUTHORIZE):
# deliver an authorized token
return MockResponse({
"url": "/cgi-bin/luci/;stok=ef5860/web/home",
"token": "ef5860",
"code": "0"
}, 200)
elif str(args[0]).endswith("timedOut/" + URL_LIST_END) \
and FIRST_CALL is True:
FIRST_CALL = False
# deliver an error when called with expired token
return MockResponse({
"code": "401",
"msg": "Invalid token"
}, 200)
elif str(args[0]).endswith(URL_LIST_END):
# deliver the device list
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"
},
"icon": "",
"type": 1
},
{
"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"
},
"icon": "",
"type": 0
},
],
"code": 0
}, 200)
else:
_LOGGER.debug('UNKNOWN ROUTE')
class TestXiaomiDeviceScanner(unittest.TestCase):
"""Xiaomi device scanner test class."""
def setUp(self):
"""Initialize values for this testcase class."""
self.hass = get_test_home_assistant()
def tearDown(self):
"""Stop everything that was started."""
self.hass.stop()
@mock.patch(
'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner',
return_value=mock.MagicMock())
def test_config(self, xiaomi_mock):
"""Testing minimal configuration."""
config = {
DOMAIN: xiaomi.PLATFORM_SCHEMA({
CONF_PLATFORM: xiaomi.DOMAIN,
CONF_HOST: '192.168.0.1',
CONF_PASSWORD: 'passwordTest'
})
}
xiaomi.get_scanner(self.hass, config)
self.assertEqual(xiaomi_mock.call_count, 1)
self.assertEqual(xiaomi_mock.call_args, mock.call(config[DOMAIN]))
call_arg = xiaomi_mock.call_args[0][0]
self.assertEqual(call_arg['username'], 'admin')
self.assertEqual(call_arg['password'], 'passwordTest')
self.assertEqual(call_arg['host'], '192.168.0.1')
self.assertEqual(call_arg['platform'], 'device_tracker')
@mock.patch(
'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner',
return_value=mock.MagicMock())
def test_config_full(self, xiaomi_mock):
"""Testing full configuration."""
config = {
DOMAIN: xiaomi.PLATFORM_SCHEMA({
CONF_PLATFORM: xiaomi.DOMAIN,
CONF_HOST: '192.168.0.1',
CONF_USERNAME: 'alternativeAdminName',
CONF_PASSWORD: 'passwordTest'
})
}
xiaomi.get_scanner(self.hass, config)
self.assertEqual(xiaomi_mock.call_count, 1)
self.assertEqual(xiaomi_mock.call_args, mock.call(config[DOMAIN]))
call_arg = xiaomi_mock.call_args[0][0]
self.assertEqual(call_arg['username'], 'alternativeAdminName')
self.assertEqual(call_arg['password'], 'passwordTest')
self.assertEqual(call_arg['host'], '192.168.0.1')
self.assertEqual(call_arg['platform'], 'device_tracker')
@patch('requests.get', side_effect=mocked_requests)
@patch('requests.post', side_effect=mocked_requests)
def test_invalid_credential(self, mock_get, mock_post):
""""Testing invalid credential handling."""
config = {
DOMAIN: xiaomi.PLATFORM_SCHEMA({
CONF_PLATFORM: xiaomi.DOMAIN,
CONF_HOST: '192.168.0.1',
CONF_USERNAME: INVALID_USERNAME,
CONF_PASSWORD: 'passwordTest'
})
}
self.assertIsNone(get_scanner(self.hass, config))
@patch('requests.get', side_effect=mocked_requests)
@patch('requests.post', side_effect=mocked_requests)
def test_valid_credential(self, mock_get, mock_post):
""""Testing valid refresh."""
config = {
DOMAIN: xiaomi.PLATFORM_SCHEMA({
CONF_PLATFORM: xiaomi.DOMAIN,
CONF_HOST: '192.168.0.1',
CONF_USERNAME: 'admin',
CONF_PASSWORD: 'passwordTest'
})
}
scanner = get_scanner(self.hass, config)
self.assertIsNotNone(scanner)
self.assertEqual(2, len(scanner.scan_devices()))
self.assertEqual("Device1",
scanner.get_device_name("23:83:BF:F6:38:A0"))
self.assertEqual("Device2",
scanner.get_device_name("1D:98:EC:5E:D5:A6"))
@patch('requests.get', side_effect=mocked_requests)
@patch('requests.post', side_effect=mocked_requests)
def test_token_timed_out(self, mock_get, mock_post):
""""Testing refresh with a timed out token.
New token is requested and list is downloaded a second time.
"""
config = {
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(self.hass, config)
self.assertIsNotNone(scanner)
self.assertEqual(2, len(scanner.scan_devices()))
self.assertEqual("Device1",
scanner.get_device_name("23:83:BF:F6:38:A0"))
self.assertEqual("Device2",
scanner.get_device_name("1D:98:EC:5E:D5:A6"))