From 253065296503cc3f42887077fd35d3fe6a1fdfdb Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Fri, 26 Aug 2016 23:42:57 +0200 Subject: [PATCH 1/3] Voluptuous for AsusWRT --- .../components/device_tracker/asuswrt.py | 43 +++++---- .../components/device_tracker/test_asuswrt.py | 92 ++++++++++++------- 2 files changed, 88 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/device_tracker/asuswrt.py b/homeassistant/components/device_tracker/asuswrt.py index ec1d073c436167..0d37d541d25f5f 100644 --- a/homeassistant/components/device_tracker/asuswrt.py +++ b/homeassistant/components/device_tracker/asuswrt.py @@ -12,14 +12,35 @@ from collections import namedtuple from datetime import timedelta +import voluptuous as vol + from homeassistant.components.device_tracker import DOMAIN from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME -from homeassistant.helpers import validate_config from homeassistant.util import Throttle +import homeassistant.helpers.config_validation as cv +from homeassistant.components.device_tracker import PLATFORM_SCHEMA # Return cached results if last scan was less then this time ago. MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) +CONF_PROTOCOL = 'protocol' +CONF_MODE = 'mode' +CONF_SSH_KEY = 'ssh_key' +CONF_PUB_KEY = 'pub_key' + +PLATFORM_SCHEMA = vol.All( + cv.has_at_least_one_key(CONF_PASSWORD, CONF_PUB_KEY, CONF_SSH_KEY), + PLATFORM_SCHEMA.extend({ + vol.Required(CONF_HOST): cv.string, + vol.Required(CONF_USERNAME): cv.string, + vol.Optional(CONF_PASSWORD): cv.string, + vol.Optional(CONF_PROTOCOL, default='ssh'): vol.Schema(['ssh', 'telnet']), + vol.Optional(CONF_MODE, default='router'): vol.Schema(['router', 'ap']), + vol.Optional(CONF_SSH_KEY): cv.isfile, + vol.Optional(CONF_PUB_KEY): cv.isfile + })) + + _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ['pexpect==4.0.1'] @@ -57,16 +78,6 @@ # pylint: disable=unused-argument def get_scanner(hass, config): """Validate the configuration and return an ASUS-WRT scanner.""" - if not validate_config(config, - {DOMAIN: [CONF_HOST, CONF_USERNAME]}, - _LOGGER): - return None - elif CONF_PASSWORD not in config[DOMAIN] and \ - 'ssh_key' not in config[DOMAIN] and \ - 'pub_key' not in config[DOMAIN]: - _LOGGER.error('Either a private key or password must be provided') - return None - scanner = AsusWrtDeviceScanner(config[DOMAIN]) return scanner if scanner.success_init else None @@ -83,11 +94,11 @@ class AsusWrtDeviceScanner(object): def __init__(self, config): """Initialize the scanner.""" self.host = config[CONF_HOST] - self.username = str(config[CONF_USERNAME]) - self.password = str(config.get(CONF_PASSWORD, '')) - self.ssh_key = str(config.get('ssh_key', config.get('pub_key', ''))) - self.protocol = config.get('protocol') - self.mode = config.get('mode') + self.username = config[CONF_USERNAME] + self.password = config.get(CONF_PASSWORD, '') + self.ssh_key = config.get('ssh_key', config.get('pub_key', '')) + self.protocol = config[CONF_PROTOCOL] + self.mode = config[CONF_MODE] self.lock = threading.Lock() diff --git a/tests/components/device_tracker/test_asuswrt.py b/tests/components/device_tracker/test_asuswrt.py index 241e4a65a0f295..0e567db9e002aa 100644 --- a/tests/components/device_tracker/test_asuswrt.py +++ b/tests/components/device_tracker/test_asuswrt.py @@ -1,14 +1,34 @@ """The tests for the ASUSWRT device tracker platform.""" - +# pylint: disable=invalid-name import os import unittest from unittest import mock +import voluptuous as vol + +from homeassistant.bootstrap import _setup_component from homeassistant.components import device_tracker +from homeassistant.components.device_tracker.asuswrt import ( + CONF_PROTOCOL, CONF_MODE, CONF_PUB_KEY, PLATFORM_SCHEMA, DOMAIN) from homeassistant.const import (CONF_PLATFORM, CONF_PASSWORD, CONF_USERNAME, CONF_HOST) -from tests.common import get_test_home_assistant +from tests.common import get_test_home_assistant, get_test_config_dir + +FAKEFILE = None + + +def setUpModule(): + """Setup the test module.""" + global FAKEFILE + FAKEFILE = get_test_config_dir('fake_file') + with open(FAKEFILE, 'w') as out: + out.write(' ') + + +def tearDownModule(): + """Tear down the module.""" + os.remove(FAKEFILE) class TestComponentsDeviceTrackerASUSWRT(unittest.TestCase): @@ -17,6 +37,7 @@ class TestComponentsDeviceTrackerASUSWRT(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() + self.hass.config.components = ['zone'] def tearDown(self): # pylint: disable=invalid-name """Stop everything that was started.""" @@ -27,8 +48,8 @@ def tearDown(self): # pylint: disable=invalid-name def test_password_or_pub_key_required(self): """Test creating an AsusWRT scanner without a pass or pubkey.""" - self.assertIsNone(device_tracker.asuswrt.get_scanner( - self.hass, {device_tracker.DOMAIN: { + self.assertFalse(_setup_component( + self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'asuswrt', CONF_HOST: 'fake_host', CONF_USERNAME: 'fake_user' @@ -40,16 +61,17 @@ def test_password_or_pub_key_required(self): def test_get_scanner_with_password_no_pubkey(self, asuswrt_mock): """Test creating an AsusWRT scanner with a password and no pubkey.""" conf_dict = { - device_tracker.DOMAIN: { + DOMAIN: { CONF_PLATFORM: 'asuswrt', CONF_HOST: 'fake_host', CONF_USERNAME: 'fake_user', CONF_PASSWORD: 'fake_pass' } } - self.assertIsNotNone(device_tracker.asuswrt.get_scanner( - self.hass, conf_dict)) - asuswrt_mock.assert_called_once_with(conf_dict[device_tracker.DOMAIN]) + self.assertIsNotNone(_setup_component(self.hass, DOMAIN, conf_dict)) + conf_dict[DOMAIN][CONF_MODE] = 'router' + conf_dict[DOMAIN][CONF_PROTOCOL] = 'ssh' + asuswrt_mock.assert_called_once_with(conf_dict[DOMAIN]) @mock.patch( 'homeassistant.components.device_tracker.asuswrt.AsusWrtDeviceScanner', @@ -61,12 +83,15 @@ def test_get_scanner_with_pubkey_no_password(self, asuswrt_mock): CONF_PLATFORM: 'asuswrt', CONF_HOST: 'fake_host', CONF_USERNAME: 'fake_user', - 'pub_key': '/fake_path' + CONF_PUB_KEY: FAKEFILE } } - self.assertIsNotNone(device_tracker.asuswrt.get_scanner( - self.hass, conf_dict)) - asuswrt_mock.assert_called_once_with(conf_dict[device_tracker.DOMAIN]) + + self.assertIsNotNone(_setup_component(self.hass, DOMAIN, conf_dict)) + + conf_dict[DOMAIN][CONF_MODE] = 'router' + conf_dict[DOMAIN][CONF_PROTOCOL] = 'ssh' + asuswrt_mock.assert_called_once_with(conf_dict[DOMAIN]) def test_ssh_login_with_pub_key(self): """Test that login is done with pub_key when configured to.""" @@ -74,12 +99,12 @@ def test_ssh_login_with_pub_key(self): ssh_mock = mock.patch('pexpect.pxssh.pxssh', return_value=ssh) ssh_mock.start() self.addCleanup(ssh_mock.stop) - conf_dict = { - CONF_PLATFORM: 'asuswrt', - CONF_HOST: 'fake_host', - CONF_USERNAME: 'fake_user', - 'pub_key': '/fake_path' - } + conf_dict = PLATFORM_SCHEMA({ + CONF_PLATFORM: 'asuswrt', + CONF_HOST: 'fake_host', + CONF_USERNAME: 'fake_user', + CONF_PUB_KEY: FAKEFILE + }) update_mock = mock.patch( 'homeassistant.components.device_tracker.asuswrt.' 'AsusWrtDeviceScanner.get_asuswrt_data') @@ -88,7 +113,7 @@ def test_ssh_login_with_pub_key(self): asuswrt = device_tracker.asuswrt.AsusWrtDeviceScanner(conf_dict) asuswrt.ssh_connection() ssh.login.assert_called_once_with('fake_host', 'fake_user', - ssh_key='/fake_path') + ssh_key=FAKEFILE) def test_ssh_login_with_password(self): """Test that login is done with password when configured to.""" @@ -96,12 +121,12 @@ def test_ssh_login_with_password(self): ssh_mock = mock.patch('pexpect.pxssh.pxssh', return_value=ssh) ssh_mock.start() self.addCleanup(ssh_mock.stop) - conf_dict = { - CONF_PLATFORM: 'asuswrt', - CONF_HOST: 'fake_host', - CONF_USERNAME: 'fake_user', - CONF_PASSWORD: 'fake_pass' - } + conf_dict = PLATFORM_SCHEMA({ + CONF_PLATFORM: 'asuswrt', + CONF_HOST: 'fake_host', + CONF_USERNAME: 'fake_user', + CONF_PASSWORD: 'fake_pass' + }) update_mock = mock.patch( 'homeassistant.components.device_tracker.asuswrt.' 'AsusWrtDeviceScanner.get_asuswrt_data') @@ -118,17 +143,22 @@ def test_ssh_login_without_password_or_pubkey(self): ssh_mock = mock.patch('pexpect.pxssh.pxssh', return_value=ssh) ssh_mock.start() self.addCleanup(ssh_mock.stop) + conf_dict = { - CONF_PLATFORM: 'asuswrt', - CONF_HOST: 'fake_host', - CONF_USERNAME: 'fake_user', + CONF_PLATFORM: 'asuswrt', + CONF_HOST: 'fake_host', + CONF_USERNAME: 'fake_user', } + + with self.assertRaises(vol.Invalid): + conf_dict = PLATFORM_SCHEMA(conf_dict) + update_mock = mock.patch( 'homeassistant.components.device_tracker.asuswrt.' 'AsusWrtDeviceScanner.get_asuswrt_data') update_mock.start() self.addCleanup(update_mock.stop) - asuswrt = device_tracker.asuswrt.AsusWrtDeviceScanner(conf_dict) - result = asuswrt.ssh_connection() + + self.assertFalse(_setup_component(self.hass, DOMAIN, + {DOMAIN: conf_dict})) ssh.login.assert_not_called() - self.assertIsNone(result) From 6a94a333c7e62c4f2173f4c7be4147ef5637ab16 Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Sat, 27 Aug 2016 00:11:12 +0200 Subject: [PATCH 2/3] Long line --- homeassistant/components/device_tracker/asuswrt.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/device_tracker/asuswrt.py b/homeassistant/components/device_tracker/asuswrt.py index 0d37d541d25f5f..4c8c36c98f329b 100644 --- a/homeassistant/components/device_tracker/asuswrt.py +++ b/homeassistant/components/device_tracker/asuswrt.py @@ -34,8 +34,10 @@ vol.Required(CONF_HOST): cv.string, vol.Required(CONF_USERNAME): cv.string, vol.Optional(CONF_PASSWORD): cv.string, - vol.Optional(CONF_PROTOCOL, default='ssh'): vol.Schema(['ssh', 'telnet']), - vol.Optional(CONF_MODE, default='router'): vol.Schema(['router', 'ap']), + vol.Optional(CONF_PROTOCOL, default='ssh'): + vol.Schema(['ssh', 'telnet']), + vol.Optional(CONF_MODE, default='router'): + vol.Schema(['router', 'ap']), vol.Optional(CONF_SSH_KEY): cv.isfile, vol.Optional(CONF_PUB_KEY): cv.isfile })) From 9efd06ce30b77adbac873930bd530b3cb496c5d0 Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Sat, 27 Aug 2016 20:38:56 +0200 Subject: [PATCH 3/3] Combine, invalid-name --- .../components/device_tracker/asuswrt.py | 3 +-- .../components/device_tracker/test_asuswrt.py | 22 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/device_tracker/asuswrt.py b/homeassistant/components/device_tracker/asuswrt.py index 4c8c36c98f329b..37e8cb0b2e7779 100644 --- a/homeassistant/components/device_tracker/asuswrt.py +++ b/homeassistant/components/device_tracker/asuswrt.py @@ -14,11 +14,10 @@ import voluptuous as vol -from homeassistant.components.device_tracker import DOMAIN +from homeassistant.components.device_tracker import DOMAIN, PLATFORM_SCHEMA from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -from homeassistant.components.device_tracker import PLATFORM_SCHEMA # Return cached results if last scan was less then this time ago. MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) diff --git a/tests/components/device_tracker/test_asuswrt.py b/tests/components/device_tracker/test_asuswrt.py index 0e567db9e002aa..fc03426a7a1220 100644 --- a/tests/components/device_tracker/test_asuswrt.py +++ b/tests/components/device_tracker/test_asuswrt.py @@ -1,5 +1,4 @@ """The tests for the ASUSWRT device tracker platform.""" -# pylint: disable=invalid-name import os import unittest from unittest import mock @@ -18,7 +17,7 @@ FAKEFILE = None -def setUpModule(): +def setup_module(): """Setup the test module.""" global FAKEFILE FAKEFILE = get_test_config_dir('fake_file') @@ -26,27 +25,29 @@ def setUpModule(): out.write(' ') -def tearDownModule(): +def teardown_module(): """Tear down the module.""" os.remove(FAKEFILE) class TestComponentsDeviceTrackerASUSWRT(unittest.TestCase): """Tests for the ASUSWRT device tracker platform.""" + hass = None - def setUp(self): # pylint: disable=invalid-name + def setup_method(self, _): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.components = ['zone'] - def tearDown(self): # pylint: disable=invalid-name + def teardown_method(self, _): """Stop everything that was started.""" try: os.remove(self.hass.config.path(device_tracker.YAML_DEVICES)) except FileNotFoundError: pass - def test_password_or_pub_key_required(self): + def test_password_or_pub_key_required(self): \ + # pylint: disable=invalid-name """Test creating an AsusWRT scanner without a pass or pubkey.""" self.assertFalse(_setup_component( self.hass, DOMAIN, {DOMAIN: { @@ -58,7 +59,8 @@ def test_password_or_pub_key_required(self): @mock.patch( 'homeassistant.components.device_tracker.asuswrt.AsusWrtDeviceScanner', return_value=mock.MagicMock()) - def test_get_scanner_with_password_no_pubkey(self, asuswrt_mock): + def test_get_scanner_with_password_no_pubkey(self, asuswrt_mock): \ + # pylint: disable=invalid-name """Test creating an AsusWRT scanner with a password and no pubkey.""" conf_dict = { DOMAIN: { @@ -76,7 +78,8 @@ def test_get_scanner_with_password_no_pubkey(self, asuswrt_mock): @mock.patch( 'homeassistant.components.device_tracker.asuswrt.AsusWrtDeviceScanner', return_value=mock.MagicMock()) - def test_get_scanner_with_pubkey_no_password(self, asuswrt_mock): + def test_get_scanner_with_pubkey_no_password(self, asuswrt_mock): \ + # pylint: disable=invalid-name """Test creating an AsusWRT scanner with a pubkey and no password.""" conf_dict = { device_tracker.DOMAIN: { @@ -137,7 +140,8 @@ def test_ssh_login_with_password(self): ssh.login.assert_called_once_with('fake_host', 'fake_user', 'fake_pass') - def test_ssh_login_without_password_or_pubkey(self): + def test_ssh_login_without_password_or_pubkey(self): \ + # pylint: disable=invalid-name """Test that login is not called without password or pub_key.""" ssh = mock.MagicMock() ssh_mock = mock.patch('pexpect.pxssh.pxssh', return_value=ssh)