From d369d70ca50eb6b41069413e5f2d17f1fbf1b315 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 19 May 2017 07:37:39 -0700 Subject: [PATCH] Fix tests (#7659) * Remove global hass * Http.auth test no longer spin up server * Remove server usage from http.ban test * Remove setupModule from test device_sun_light_trigger * Update common.py --- .../components/device_tracker/__init__.py | 24 +- tests/components/http/test_auth.py | 214 +++--- tests/components/http/test_ban.py | 150 ++--- tests/components/test_api.py | 616 +++++++++--------- .../test_device_sun_light_trigger.py | 53 +- tests/components/test_frontend.py | 6 +- 6 files changed, 499 insertions(+), 564 deletions(-) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 6582ba3f57e..8770aaafaa8 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -150,14 +150,14 @@ def async_setup(hass: HomeAssistantType, config: ConfigType): scanner = yield from platform.async_get_scanner( hass, {DOMAIN: p_config}) elif hasattr(platform, 'get_scanner'): - scanner = yield from hass.loop.run_in_executor( - None, platform.get_scanner, hass, {DOMAIN: p_config}) + scanner = yield from hass.async_add_job( + platform.get_scanner, hass, {DOMAIN: p_config}) elif hasattr(platform, 'async_setup_scanner'): setup = yield from platform.async_setup_scanner( hass, p_config, tracker.async_see, disc_info) elif hasattr(platform, 'setup_scanner'): - setup = yield from hass.loop.run_in_executor( - None, platform.setup_scanner, hass, p_config, tracker.see, + setup = yield from hass.async_add_job( + platform.setup_scanner, hass, p_config, tracker.see, disc_info) else: raise HomeAssistantError("Invalid device_tracker platform.") @@ -209,8 +209,8 @@ def async_setup(hass: HomeAssistantType, config: ConfigType): ATTR_GPS, ATTR_GPS_ACCURACY, ATTR_BATTERY, ATTR_ATTRIBUTES)} yield from tracker.async_see(**args) - descriptions = yield from hass.loop.run_in_executor( - None, load_yaml_config_file, + descriptions = yield from hass.async_add_job( + load_yaml_config_file, os.path.join(os.path.dirname(__file__), 'services.yaml') ) hass.services.async_register( @@ -322,8 +322,8 @@ class DeviceTracker(object): This method is a coroutine. """ with (yield from self._is_updating): - yield from self.hass.loop.run_in_executor( - None, update_config, self.hass.config.path(YAML_DEVICES), + yield from self.hass.async_add_job( + update_config, self.hass.config.path(YAML_DEVICES), dev_id, device) @asyncio.coroutine @@ -608,7 +608,7 @@ class DeviceScanner(object): This method must be run in the event loop and returns a coroutine. """ - return self.hass.loop.run_in_executor(None, self.scan_devices) + return self.hass.async_add_job(self.scan_devices) def get_device_name(self, mac: str) -> str: """Get device name from mac.""" @@ -619,7 +619,7 @@ class DeviceScanner(object): This method must be run in the event loop and returns a coroutine. """ - return self.hass.loop.run_in_executor(None, self.get_device_name, mac) + return self.hass.async_add_job(self.get_device_name, mac) def load_config(path: str, hass: HomeAssistantType, consider_home: timedelta): @@ -650,8 +650,8 @@ def async_load_config(path: str, hass: HomeAssistantType, try: result = [] try: - devices = yield from hass.loop.run_in_executor( - None, load_yaml_config_file, path) + devices = yield from hass.async_add_job( + load_yaml_config_file, path) except HomeAssistantError as err: _LOGGER.error("Unable to load %s: %s", path, str(err)) return [] diff --git a/tests/components/http/test_auth.py b/tests/components/http/test_auth.py index 729e6f22be6..5db42b01371 100644 --- a/tests/components/http/test_auth.py +++ b/tests/components/http/test_auth.py @@ -1,26 +1,19 @@ """The tests for the Home Assistant HTTP component.""" # pylint: disable=protected-access -import logging +import asyncio from ipaddress import ip_address, ip_network from unittest.mock import patch -import requests +import pytest -from homeassistant import setup, const +from homeassistant import const +from homeassistant.setup import async_setup_component import homeassistant.components.http as http from homeassistant.components.http.const import ( KEY_TRUSTED_NETWORKS, KEY_USE_X_FORWARDED_FOR, HTTP_HEADER_X_FORWARDED_FOR) -from tests.common import get_test_instance_port, get_test_home_assistant - API_PASSWORD = 'test1234' -SERVER_PORT = get_test_instance_port() -HTTP_BASE = '127.0.0.1:{}'.format(SERVER_PORT) -HTTP_BASE_URL = 'http://{}'.format(HTTP_BASE) -HA_HEADERS = { - const.HTTP_HEADER_HA_AUTH: API_PASSWORD, - const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON, -} + # Don't add 127.0.0.1/::1 as trusted, as it may interfere with other test cases TRUSTED_NETWORKS = ['192.0.2.0/24', '2001:DB8:ABCD::/48', '100.64.0.1', 'FD01:DB8::1'] @@ -28,142 +21,131 @@ TRUSTED_ADDRESSES = ['100.64.0.1', '192.0.2.100', 'FD01:DB8::1', '2001:DB8:ABCD::1'] UNTRUSTED_ADDRESSES = ['198.51.100.1', '2001:DB8:FA1::1', '127.0.0.1', '::1'] -hass = None - -def _url(path=''): - """Helper method to generate URLs.""" - return HTTP_BASE_URL + path - - -# pylint: disable=invalid-name -def setUpModule(): - """Initialize a Home Assistant server.""" - global hass - - hass = get_test_home_assistant() - - setup.setup_component( - hass, http.DOMAIN, { - http.DOMAIN: { - http.CONF_API_PASSWORD: API_PASSWORD, - http.CONF_SERVER_PORT: SERVER_PORT, - } +@pytest.fixture +def mock_api_client(hass, test_client): + """Start the Hass HTTP component.""" + hass.loop.run_until_complete(async_setup_component(hass, 'api', { + 'http': { + http.CONF_API_PASSWORD: API_PASSWORD, } - ) + })) + return hass.loop.run_until_complete(test_client(hass.http.app)) - setup.setup_component(hass, 'api') +@pytest.fixture +def mock_trusted_networks(hass, mock_api_client): + """Mock trusted networks.""" hass.http.app[KEY_TRUSTED_NETWORKS] = [ ip_network(trusted_network) for trusted_network in TRUSTED_NETWORKS] - hass.start() + +@asyncio.coroutine +def test_access_denied_without_password(mock_api_client): + """Test access without password.""" + resp = yield from mock_api_client.get(const.URL_API) + assert resp.status == 401 -# pylint: disable=invalid-name -def tearDownModule(): - """Stop the Home Assistant server.""" - hass.stop() +@asyncio.coroutine +def test_access_denied_with_wrong_password_in_header(mock_api_client): + """Test access with wrong password.""" + resp = yield from mock_api_client.get(const.URL_API, headers={ + const.HTTP_HEADER_HA_AUTH: 'wrongpassword' + }) + assert resp.status == 401 -class TestHttp: - """Test HTTP component.""" +@asyncio.coroutine +def test_access_denied_with_x_forwarded_for(hass, mock_api_client, + mock_trusted_networks): + """Test access denied through the X-Forwarded-For http header.""" + hass.http.use_x_forwarded_for = True + for remote_addr in UNTRUSTED_ADDRESSES: + resp = yield from mock_api_client.get(const.URL_API, headers={ + HTTP_HEADER_X_FORWARDED_FOR: remote_addr}) - def test_access_denied_without_password(self): - """Test access without password.""" - req = requests.get(_url(const.URL_API)) + assert resp.status == 401, \ + "{} shouldn't be trusted".format(remote_addr) - assert req.status_code == 401 - def test_access_denied_with_wrong_password_in_header(self): - """Test access with wrong password.""" - req = requests.get( - _url(const.URL_API), - headers={const.HTTP_HEADER_HA_AUTH: 'wrongpassword'}) +@asyncio.coroutine +def test_access_denied_with_untrusted_ip(mock_api_client, + mock_trusted_networks): + """Test access with an untrusted ip address.""" + for remote_addr in UNTRUSTED_ADDRESSES: + with patch('homeassistant.components.http.' + 'util.get_real_ip', + return_value=ip_address(remote_addr)): + resp = yield from mock_api_client.get( + const.URL_API, params={'api_password': ''}) - assert req.status_code == 401 - - def test_access_denied_with_x_forwarded_for(self, caplog): - """Test access denied through the X-Forwarded-For http header.""" - hass.http.use_x_forwarded_for = True - for remote_addr in UNTRUSTED_ADDRESSES: - req = requests.get(_url(const.URL_API), headers={ - HTTP_HEADER_X_FORWARDED_FOR: remote_addr}) - - assert req.status_code == 401, \ + assert resp.status == 401, \ "{} shouldn't be trusted".format(remote_addr) - def test_access_denied_with_untrusted_ip(self, caplog): - """Test access with an untrusted ip address.""" - for remote_addr in UNTRUSTED_ADDRESSES: - with patch('homeassistant.components.http.' - 'util.get_real_ip', - return_value=ip_address(remote_addr)): - req = requests.get( - _url(const.URL_API), params={'api_password': ''}) - assert req.status_code == 401, \ - "{} shouldn't be trusted".format(remote_addr) +@asyncio.coroutine +def test_access_with_password_in_header(mock_api_client, caplog): + """Test access with password in URL.""" + # Hide logging from requests package that we use to test logging + req = yield from mock_api_client.get( + const.URL_API, headers={const.HTTP_HEADER_HA_AUTH: API_PASSWORD}) - def test_access_with_password_in_header(self, caplog): - """Test access with password in URL.""" - # Hide logging from requests package that we use to test logging - caplog.set_level( - logging.WARNING, logger='requests.packages.urllib3.connectionpool') + assert req.status == 200 - req = requests.get( - _url(const.URL_API), - headers={const.HTTP_HEADER_HA_AUTH: API_PASSWORD}) + logs = caplog.text - assert req.status_code == 200 + assert const.URL_API in logs + assert API_PASSWORD not in logs - logs = caplog.text - assert const.URL_API in logs - assert API_PASSWORD not in logs +@asyncio.coroutine +def test_access_denied_with_wrong_password_in_url(mock_api_client): + """Test access with wrong password.""" + resp = yield from mock_api_client.get( + const.URL_API, params={'api_password': 'wrongpassword'}) - def test_access_denied_with_wrong_password_in_url(self): - """Test access with wrong password.""" - req = requests.get( - _url(const.URL_API), params={'api_password': 'wrongpassword'}) + assert resp.status == 401 - assert req.status_code == 401 - def test_access_with_password_in_url(self, caplog): - """Test access with password in URL.""" - # Hide logging from requests package that we use to test logging - caplog.set_level( - logging.WARNING, logger='requests.packages.urllib3.connectionpool') +@asyncio.coroutine +def test_access_with_password_in_url(mock_api_client, caplog): + """Test access with password in URL.""" + req = yield from mock_api_client.get( + const.URL_API, params={'api_password': API_PASSWORD}) - req = requests.get( - _url(const.URL_API), params={'api_password': API_PASSWORD}) + assert req.status == 200 - assert req.status_code == 200 + logs = caplog.text - logs = caplog.text + assert const.URL_API in logs + assert API_PASSWORD not in logs - assert const.URL_API in logs - assert API_PASSWORD not in logs - def test_access_granted_with_x_forwarded_for(self, caplog): - """Test access denied through the X-Forwarded-For http header.""" - hass.http.app[KEY_USE_X_FORWARDED_FOR] = True - for remote_addr in TRUSTED_ADDRESSES: - req = requests.get(_url(const.URL_API), headers={ - HTTP_HEADER_X_FORWARDED_FOR: remote_addr}) +@asyncio.coroutine +def test_access_granted_with_x_forwarded_for(hass, mock_api_client, caplog, + mock_trusted_networks): + """Test access denied through the X-Forwarded-For http header.""" + hass.http.app[KEY_USE_X_FORWARDED_FOR] = True + for remote_addr in TRUSTED_ADDRESSES: + resp = yield from mock_api_client.get(const.URL_API, headers={ + HTTP_HEADER_X_FORWARDED_FOR: remote_addr}) - assert req.status_code == 200, \ - "{} should be trusted".format(remote_addr) + assert resp.status == 200, \ + "{} should be trusted".format(remote_addr) - def test_access_granted_with_trusted_ip(self, caplog): - """Test access with trusted addresses.""" - for remote_addr in TRUSTED_ADDRESSES: - with patch('homeassistant.components.http.' - 'auth.get_real_ip', - return_value=ip_address(remote_addr)): - req = requests.get( - _url(const.URL_API), params={'api_password': ''}) - assert req.status_code == 200, \ - '{} should be trusted'.format(remote_addr) +@asyncio.coroutine +def test_access_granted_with_trusted_ip(mock_api_client, caplog, + mock_trusted_networks): + """Test access with trusted addresses.""" + for remote_addr in TRUSTED_ADDRESSES: + with patch('homeassistant.components.http.' + 'auth.get_real_ip', + return_value=ip_address(remote_addr)): + resp = yield from mock_api_client.get( + const.URL_API, params={'api_password': ''}) + + assert resp.status == 200, \ + '{} should be trusted'.format(remote_addr) diff --git a/tests/components/http/test_ban.py b/tests/components/http/test_ban.py index 0d8f1a92c7f..c9147367c10 100644 --- a/tests/components/http/test_ban.py +++ b/tests/components/http/test_ban.py @@ -1,117 +1,91 @@ """The tests for the Home Assistant HTTP component.""" # pylint: disable=protected-access +import asyncio from ipaddress import ip_address from unittest.mock import patch, mock_open -import requests +import pytest -from homeassistant import setup, const +from homeassistant import const +from homeassistant.setup import async_setup_component import homeassistant.components.http as http from homeassistant.components.http.const import ( KEY_BANS_ENABLED, KEY_LOGIN_THRESHOLD, KEY_BANNED_IPS) from homeassistant.components.http.ban import IpBan, IP_BANS_FILE -from tests.common import get_test_instance_port, get_test_home_assistant - API_PASSWORD = 'test1234' -SERVER_PORT = get_test_instance_port() -HTTP_BASE = '127.0.0.1:{}'.format(SERVER_PORT) -HTTP_BASE_URL = 'http://{}'.format(HTTP_BASE) -HA_HEADERS = { - const.HTTP_HEADER_HA_AUTH: API_PASSWORD, - const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON, -} BANNED_IPS = ['200.201.202.203', '100.64.0.2'] -hass = None - -def _url(path=''): - """Helper method to generate URLs.""" - return HTTP_BASE_URL + path - - -# pylint: disable=invalid-name -def setUpModule(): - """Initialize a Home Assistant server.""" - global hass - - hass = get_test_home_assistant() - - setup.setup_component( - hass, http.DOMAIN, { - http.DOMAIN: { - http.CONF_API_PASSWORD: API_PASSWORD, - http.CONF_SERVER_PORT: SERVER_PORT, - } +@pytest.fixture +def mock_api_client(hass, test_client): + """Start the Hass HTTP component.""" + hass.loop.run_until_complete(async_setup_component(hass, 'api', { + 'http': { + http.CONF_API_PASSWORD: API_PASSWORD, } - ) - - setup.setup_component(hass, 'api') - + })) hass.http.app[KEY_BANNED_IPS] = [IpBan(banned_ip) for banned_ip in BANNED_IPS] - hass.start() + return hass.loop.run_until_complete(test_client(hass.http.app)) -# pylint: disable=invalid-name -def tearDownModule(): - """Stop the Home Assistant server.""" - hass.stop() +@asyncio.coroutine +def test_access_from_banned_ip(hass, mock_api_client): + """Test accessing to server from banned IP. Both trusted and not.""" + hass.http.app[KEY_BANS_ENABLED] = True + for remote_addr in BANNED_IPS: + with patch('homeassistant.components.http.' + 'ban.get_real_ip', + return_value=ip_address(remote_addr)): + resp = yield from mock_api_client.get( + const.URL_API) + assert resp.status == 403 -class TestHttp: - """Test HTTP component.""" +@asyncio.coroutine +def test_access_from_banned_ip_when_ban_is_off(hass, mock_api_client): + """Test accessing to server from banned IP when feature is off.""" + hass.http.app[KEY_BANS_ENABLED] = False + for remote_addr in BANNED_IPS: + with patch('homeassistant.components.http.' + 'ban.get_real_ip', + return_value=ip_address(remote_addr)): + resp = yield from mock_api_client.get( + const.URL_API, + headers={const.HTTP_HEADER_HA_AUTH: API_PASSWORD}) + assert resp.status == 200 - def test_access_from_banned_ip(self): - """Test accessing to server from banned IP. Both trusted and not.""" - hass.http.app[KEY_BANS_ENABLED] = True - for remote_addr in BANNED_IPS: - with patch('homeassistant.components.http.' - 'ban.get_real_ip', - return_value=ip_address(remote_addr)): - req = requests.get( - _url(const.URL_API)) - assert req.status_code == 403 - def test_access_from_banned_ip_when_ban_is_off(self): - """Test accessing to server from banned IP when feature is off.""" - hass.http.app[KEY_BANS_ENABLED] = False - for remote_addr in BANNED_IPS: - with patch('homeassistant.components.http.' - 'ban.get_real_ip', - return_value=ip_address(remote_addr)): - req = requests.get( - _url(const.URL_API), - headers={const.HTTP_HEADER_HA_AUTH: API_PASSWORD}) - assert req.status_code == 200 +@asyncio.coroutine +def test_ip_bans_file_creation(hass, mock_api_client): + """Testing if banned IP file created.""" + hass.http.app[KEY_BANS_ENABLED] = True + hass.http.app[KEY_LOGIN_THRESHOLD] = 1 - def test_ip_bans_file_creation(self): - """Testing if banned IP file created.""" - hass.http.app[KEY_BANS_ENABLED] = True - hass.http.app[KEY_LOGIN_THRESHOLD] = 1 + m = mock_open() - m = mock_open() + @asyncio.coroutine + def call_server(): + with patch('homeassistant.components.http.' + 'ban.get_real_ip', + return_value=ip_address("200.201.202.204")): + resp = yield from mock_api_client.get( + const.URL_API, + headers={const.HTTP_HEADER_HA_AUTH: 'Wrong password'}) + return resp - def call_server(): - with patch('homeassistant.components.http.' - 'ban.get_real_ip', - return_value=ip_address("200.201.202.204")): - return requests.get( - _url(const.URL_API), - headers={const.HTTP_HEADER_HA_AUTH: 'Wrong password'}) + with patch('homeassistant.components.http.ban.open', m, create=True): + resp = yield from call_server() + assert resp.status == 401 + assert len(hass.http.app[KEY_BANNED_IPS]) == len(BANNED_IPS) + assert m.call_count == 0 - with patch('homeassistant.components.http.ban.open', m, create=True): - req = call_server() - assert req.status_code == 401 - assert len(hass.http.app[KEY_BANNED_IPS]) == len(BANNED_IPS) - assert m.call_count == 0 + resp = yield from call_server() + assert resp.status == 401 + assert len(hass.http.app[KEY_BANNED_IPS]) == len(BANNED_IPS) + 1 + m.assert_called_once_with(hass.config.path(IP_BANS_FILE), 'a') - req = call_server() - assert req.status_code == 401 - assert len(hass.http.app[KEY_BANNED_IPS]) == len(BANNED_IPS) + 1 - m.assert_called_once_with(hass.config.path(IP_BANS_FILE), 'a') - - req = call_server() - assert req.status_code == 403 - assert m.call_count == 1 + resp = yield from call_server() + assert resp.status == 403 + assert m.call_count == 1 diff --git a/tests/components/test_api.py b/tests/components/test_api.py index 8d6041b49c1..f110a832752 100644 --- a/tests/components/test_api.py +++ b/tests/components/test_api.py @@ -1,394 +1,380 @@ """The tests for the Home Assistant API component.""" # pylint: disable=protected-access -from contextlib import closing +import asyncio import json -import unittest -import requests +import pytest -from homeassistant import setup, const +from homeassistant import const import homeassistant.core as ha -import homeassistant.components.http as http - -from tests.common import get_test_instance_port, get_test_home_assistant - -API_PASSWORD = "test1234" -SERVER_PORT = get_test_instance_port() -HTTP_BASE_URL = "http://127.0.0.1:{}".format(SERVER_PORT) -HA_HEADERS = { - const.HTTP_HEADER_HA_AUTH: API_PASSWORD, - const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON, -} - -hass = None +from homeassistant.setup import async_setup_component -def _url(path=""): - """Helper method to generate URLs.""" - return HTTP_BASE_URL + path +@pytest.fixture +def mock_api_client(hass, test_client): + """Start the Hass HTTP component.""" + hass.loop.run_until_complete(async_setup_component(hass, 'api', {})) + return hass.loop.run_until_complete(test_client(hass.http.app)) + + +@asyncio.coroutine +def test_api_list_state_entities(hass, mock_api_client): + """Test if the debug interface allows us to list state entities.""" + hass.states.async_set('test.entity', 'hello') + resp = yield from mock_api_client.get(const.URL_API_STATES) + assert resp.status == 200 + json = yield from resp.json() + + remote_data = [ha.State.from_dict(item) for item in json] + assert remote_data == hass.states.async_all() + + +@asyncio.coroutine +def test_api_get_state(hass, mock_api_client): + """Test if the debug interface allows us to get a state.""" + hass.states.async_set('hello.world', 'nice', { + 'attr': 1, + }) + resp = yield from mock_api_client.get( + const.URL_API_STATES_ENTITY.format("hello.world")) + assert resp.status == 200 + json = yield from resp.json() + + data = ha.State.from_dict(json) + + state = hass.states.get("hello.world") + + assert data.state == state.state + assert data.last_changed == state.last_changed + assert data.attributes == state.attributes + + +@asyncio.coroutine +def test_api_get_non_existing_state(hass, mock_api_client): + """Test if the debug interface allows us to get a state.""" + resp = yield from mock_api_client.get( + const.URL_API_STATES_ENTITY.format("does_not_exist")) + assert resp.status == 404 + + +@asyncio.coroutine +def test_api_state_change(hass, mock_api_client): + """Test if we can change the state of an entity that exists.""" + hass.states.async_set("test.test", "not_to_be_set") + + yield from mock_api_client.post( + const.URL_API_STATES_ENTITY.format("test.test"), + json={"state": "debug_state_change2"}) + + assert hass.states.get("test.test").state == "debug_state_change2" # pylint: disable=invalid-name -def setUpModule(): - """Initialize a Home Assistant server.""" - global hass +@asyncio.coroutine +def test_api_state_change_of_non_existing_entity(hass, mock_api_client): + """Test if changing a state of a non existing entity is possible.""" + new_state = "debug_state_change" - hass = get_test_home_assistant() + resp = yield from mock_api_client.post( + const.URL_API_STATES_ENTITY.format("test_entity.that_does_not_exist"), + json={'state': new_state}) - hass.bus.listen('test_event', lambda _: _) - hass.states.set('test.test', 'a_state') + assert resp.status == 201 - setup.setup_component( - hass, http.DOMAIN, - {http.DOMAIN: {http.CONF_API_PASSWORD: API_PASSWORD, - http.CONF_SERVER_PORT: SERVER_PORT}}) - - setup.setup_component(hass, 'api') - - hass.start() + assert hass.states.get("test_entity.that_does_not_exist").state == \ + new_state # pylint: disable=invalid-name -def tearDownModule(): - """Stop the Home Assistant server.""" - hass.stop() +@asyncio.coroutine +def test_api_state_change_with_bad_data(hass, mock_api_client): + """Test if API sends appropriate error if we omit state.""" + resp = yield from mock_api_client.post( + const.URL_API_STATES_ENTITY.format("test_entity.that_does_not_exist"), + json={}) + + assert resp.status == 400 -class TestAPI(unittest.TestCase): - """Test the API.""" +# pylint: disable=invalid-name +@asyncio.coroutine +def test_api_state_change_push(hass, mock_api_client): + """Test if we can push a change the state of an entity.""" + hass.states.async_set("test.test", "not_to_be_set") - def tearDown(self): - """Stop everything that was started.""" - hass.block_till_done() + events = [] - def test_api_list_state_entities(self): - """Test if the debug interface allows us to list state entities.""" - req = requests.get(_url(const.URL_API_STATES), - headers=HA_HEADERS) + @ha.callback + def event_listener(event): + """Track events.""" + events.append(event) - remote_data = [ha.State.from_dict(item) for item in req.json()] + hass.bus.async_listen(const.EVENT_STATE_CHANGED, event_listener) - self.assertEqual(hass.states.all(), remote_data) + yield from mock_api_client.post( + const.URL_API_STATES_ENTITY.format("test.test"), + json={"state": "not_to_be_set"}) + yield from hass.async_block_till_done() + assert len(events) == 0 - def test_api_get_state(self): - """Test if the debug interface allows us to get a state.""" - req = requests.get( - _url(const.URL_API_STATES_ENTITY.format("test.test")), - headers=HA_HEADERS) + yield from mock_api_client.post( + const.URL_API_STATES_ENTITY.format("test.test"), + json={"state": "not_to_be_set", "force_update": True}) + yield from hass.async_block_till_done() + assert len(events) == 1 - data = ha.State.from_dict(req.json()) - state = hass.states.get("test.test") +# pylint: disable=invalid-name +@asyncio.coroutine +def test_api_fire_event_with_no_data(hass, mock_api_client): + """Test if the API allows us to fire an event.""" + test_value = [] - self.assertEqual(state.state, data.state) - self.assertEqual(state.last_changed, data.last_changed) - self.assertEqual(state.attributes, data.attributes) + @ha.callback + def listener(event): + """Helper method that will verify our event got called.""" + test_value.append(1) - def test_api_get_non_existing_state(self): - """Test if the debug interface allows us to get a state.""" - req = requests.get( - _url(const.URL_API_STATES_ENTITY.format("does_not_exist")), - headers=HA_HEADERS) + hass.bus.async_listen_once("test.event_no_data", listener) - self.assertEqual(404, req.status_code) + yield from mock_api_client.post( + const.URL_API_EVENTS_EVENT.format("test.event_no_data")) + yield from hass.async_block_till_done() - def test_api_state_change(self): - """Test if we can change the state of an entity that exists.""" - hass.states.set("test.test", "not_to_be_set") + assert len(test_value) == 1 - requests.post(_url(const.URL_API_STATES_ENTITY.format("test.test")), - data=json.dumps({"state": "debug_state_change2"}), - headers=HA_HEADERS) - self.assertEqual("debug_state_change2", - hass.states.get("test.test").state) +# pylint: disable=invalid-name +@asyncio.coroutine +def test_api_fire_event_with_data(hass, mock_api_client): + """Test if the API allows us to fire an event.""" + test_value = [] - # pylint: disable=invalid-name - def test_api_state_change_of_non_existing_entity(self): - """Test if changing a state of a non existing entity is possible.""" - new_state = "debug_state_change" + @ha.callback + def listener(event): + """Helper method that will verify that our event got called. - req = requests.post( - _url(const.URL_API_STATES_ENTITY.format( - "test_entity.that_does_not_exist")), - data=json.dumps({'state': new_state}), - headers=HA_HEADERS) - - cur_state = (hass.states. - get("test_entity.that_does_not_exist").state) - - self.assertEqual(201, req.status_code) - self.assertEqual(cur_state, new_state) - - # pylint: disable=invalid-name - def test_api_state_change_with_bad_data(self): - """Test if API sends appropriate error if we omit state.""" - req = requests.post( - _url(const.URL_API_STATES_ENTITY.format( - "test_entity.that_does_not_exist")), - data=json.dumps({}), - headers=HA_HEADERS) - - self.assertEqual(400, req.status_code) - - # pylint: disable=invalid-name - def test_api_state_change_push(self): - """Test if we can push a change the state of an entity.""" - hass.states.set("test.test", "not_to_be_set") - - events = [] - hass.bus.listen(const.EVENT_STATE_CHANGED, - lambda ev: events.append(ev)) - - requests.post(_url(const.URL_API_STATES_ENTITY.format("test.test")), - data=json.dumps({"state": "not_to_be_set"}), - headers=HA_HEADERS) - hass.block_till_done() - self.assertEqual(0, len(events)) - - requests.post(_url(const.URL_API_STATES_ENTITY.format("test.test")), - data=json.dumps({"state": "not_to_be_set", - "force_update": True}), - headers=HA_HEADERS) - hass.block_till_done() - self.assertEqual(1, len(events)) - - # pylint: disable=invalid-name - def test_api_fire_event_with_no_data(self): - """Test if the API allows us to fire an event.""" - test_value = [] - - def listener(event): - """Helper method that will verify our event got called.""" + Also test if our data came through. + """ + if "test" in event.data: test_value.append(1) - hass.bus.listen_once("test.event_no_data", listener) + hass.bus.async_listen_once("test_event_with_data", listener) - requests.post( - _url(const.URL_API_EVENTS_EVENT.format("test.event_no_data")), - headers=HA_HEADERS) + yield from mock_api_client.post( + const.URL_API_EVENTS_EVENT.format("test_event_with_data"), + json={"test": 1}) - hass.block_till_done() + yield from hass.async_block_till_done() - self.assertEqual(1, len(test_value)) + assert len(test_value) == 1 - # pylint: disable=invalid-name - def test_api_fire_event_with_data(self): - """Test if the API allows us to fire an event.""" - test_value = [] - def listener(event): - """Helper method that will verify that our event got called. +# pylint: disable=invalid-name +@asyncio.coroutine +def test_api_fire_event_with_invalid_json(hass, mock_api_client): + """Test if the API allows us to fire an event.""" + test_value = [] - Also test if our data came through. - """ - if "test" in event.data: - test_value.append(1) + @ha.callback + def listener(event): + """Helper method that will verify our event got called.""" + test_value.append(1) - hass.bus.listen_once("test_event_with_data", listener) + hass.bus.async_listen_once("test_event_bad_data", listener) - requests.post( - _url(const.URL_API_EVENTS_EVENT.format("test_event_with_data")), - data=json.dumps({"test": 1}), - headers=HA_HEADERS) + resp = yield from mock_api_client.post( + const.URL_API_EVENTS_EVENT.format("test_event_bad_data"), + data=json.dumps('not an object')) - hass.block_till_done() + yield from hass.async_block_till_done() - self.assertEqual(1, len(test_value)) + assert resp.status == 400 + assert len(test_value) == 0 - # pylint: disable=invalid-name - def test_api_fire_event_with_invalid_json(self): - """Test if the API allows us to fire an event.""" - test_value = [] + # Try now with valid but unusable JSON + resp = yield from mock_api_client.post( + const.URL_API_EVENTS_EVENT.format("test_event_bad_data"), + data=json.dumps([1, 2, 3])) - def listener(event): - """Helper method that will verify our event got called.""" + yield from hass.async_block_till_done() + + assert resp.status == 400 + assert len(test_value) == 0 + + +@asyncio.coroutine +def test_api_get_config(hass, mock_api_client): + """Test the return of the configuration.""" + resp = yield from mock_api_client.get(const.URL_API_CONFIG) + result = yield from resp.json() + if 'components' in result: + result['components'] = set(result['components']) + + assert hass.config.as_dict() == result + + +@asyncio.coroutine +def test_api_get_components(hass, mock_api_client): + """Test the return of the components.""" + resp = yield from mock_api_client.get(const.URL_API_COMPONENTS) + result = yield from resp.json() + assert set(result) == hass.config.components + + +@asyncio.coroutine +def test_api_get_event_listeners(hass, mock_api_client): + """Test if we can get the list of events being listened for.""" + resp = yield from mock_api_client.get(const.URL_API_EVENTS) + data = yield from resp.json() + + local = hass.bus.async_listeners() + + for event in data: + assert local.pop(event["event"]) == event["listener_count"] + + assert len(local) == 0 + + +@asyncio.coroutine +def test_api_get_services(hass, mock_api_client): + """Test if we can get a dict describing current services.""" + resp = yield from mock_api_client.get(const.URL_API_SERVICES) + data = yield from resp.json() + local_services = hass.services.async_services() + + for serv_domain in data: + local = local_services.pop(serv_domain["domain"]) + + assert serv_domain["services"] == local + + +@asyncio.coroutine +def test_api_call_service_no_data(hass, mock_api_client): + """Test if the API allows us to call a service.""" + test_value = [] + + @ha.callback + def listener(service_call): + """Helper method that will verify that our service got called.""" + test_value.append(1) + + hass.services.async_register("test_domain", "test_service", listener) + + yield from mock_api_client.post( + const.URL_API_SERVICES_SERVICE.format( + "test_domain", "test_service")) + yield from hass.async_block_till_done() + assert len(test_value) == 1 + + +@asyncio.coroutine +def test_api_call_service_with_data(hass, mock_api_client): + """Test if the API allows us to call a service.""" + test_value = [] + + @ha.callback + def listener(service_call): + """Helper method that will verify that our service got called. + + Also test if our data came through. + """ + if "test" in service_call.data: test_value.append(1) - hass.bus.listen_once("test_event_bad_data", listener) + hass.services.async_register("test_domain", "test_service", listener) - req = requests.post( - _url(const.URL_API_EVENTS_EVENT.format("test_event_bad_data")), - data=json.dumps('not an object'), - headers=HA_HEADERS) + yield from mock_api_client.post( + const.URL_API_SERVICES_SERVICE.format("test_domain", "test_service"), + json={"test": 1}) - hass.block_till_done() + yield from hass.async_block_till_done() + assert len(test_value) == 1 - self.assertEqual(400, req.status_code) - self.assertEqual(0, len(test_value)) - # Try now with valid but unusable JSON - req = requests.post( - _url(const.URL_API_EVENTS_EVENT.format("test_event_bad_data")), - data=json.dumps([1, 2, 3]), - headers=HA_HEADERS) +@asyncio.coroutine +def test_api_template(hass, mock_api_client): + """Test the template API.""" + hass.states.async_set('sensor.temperature', 10) - hass.block_till_done() + resp = yield from mock_api_client.post( + const.URL_API_TEMPLATE, + json={"template": '{{ states.sensor.temperature.state }}'}) - self.assertEqual(400, req.status_code) - self.assertEqual(0, len(test_value)) + body = yield from resp.text() - def test_api_get_config(self): - """Test the return of the configuration.""" - req = requests.get(_url(const.URL_API_CONFIG), - headers=HA_HEADERS) - result = req.json() - if 'components' in result: - result['components'] = set(result['components']) + assert body == '10' - self.assertEqual(hass.config.as_dict(), result) - def test_api_get_components(self): - """Test the return of the components.""" - req = requests.get(_url(const.URL_API_COMPONENTS), - headers=HA_HEADERS) - self.assertEqual(hass.config.components, set(req.json())) +@asyncio.coroutine +def test_api_template_error(hass, mock_api_client): + """Test the template API.""" + hass.states.async_set('sensor.temperature', 10) - def test_api_get_event_listeners(self): - """Test if we can get the list of events being listened for.""" - req = requests.get(_url(const.URL_API_EVENTS), - headers=HA_HEADERS) + resp = yield from mock_api_client.post( + const.URL_API_TEMPLATE, + json={"template": '{{ states.sensor.temperature.state'}) - local = hass.bus.listeners + assert resp.status == 400 - for event in req.json(): - self.assertEqual(event["listener_count"], - local.pop(event["event"])) - self.assertEqual(0, len(local)) +@asyncio.coroutine +def test_stream(hass, mock_api_client): + """Test the stream.""" + listen_count = _listen_count(hass) - def test_api_get_services(self): - """Test if we can get a dict describing current services.""" - req = requests.get(_url(const.URL_API_SERVICES), - headers=HA_HEADERS) + resp = yield from mock_api_client.get(const.URL_API_STREAM) + assert resp.status == 200 + assert listen_count + 1 == _listen_count(hass) - local_services = hass.services.services + hass.bus.async_fire('test_event') - for serv_domain in req.json(): - local = local_services.pop(serv_domain["domain"]) + data = yield from _stream_next_event(resp.content) - self.assertEqual(local, serv_domain["services"]) + assert data['event_type'] == 'test_event' - def test_api_call_service_no_data(self): - """Test if the API allows us to call a service.""" - test_value = [] - @ha.callback - def listener(service_call): - """Helper method that will verify that our service got called.""" - test_value.append(1) +@asyncio.coroutine +def test_stream_with_restricted(hass, mock_api_client): + """Test the stream with restrictions.""" + listen_count = _listen_count(hass) - hass.services.register("test_domain", "test_service", listener) + resp = yield from mock_api_client.get( + '{}?restrict=test_event1,test_event3'.format(const.URL_API_STREAM)) + assert resp.status == 200 + assert listen_count + 1 == _listen_count(hass) - requests.post( - _url(const.URL_API_SERVICES_SERVICE.format( - "test_domain", "test_service")), - headers=HA_HEADERS) + hass.bus.async_fire('test_event1') + data = yield from _stream_next_event(resp.content) + assert data['event_type'] == 'test_event1' - hass.block_till_done() + hass.bus.async_fire('test_event2') + hass.bus.async_fire('test_event3') + data = yield from _stream_next_event(resp.content) + assert data['event_type'] == 'test_event3' - self.assertEqual(1, len(test_value)) - def test_api_call_service_with_data(self): - """Test if the API allows us to call a service.""" - test_value = [] +@asyncio.coroutine +def _stream_next_event(stream): + """Read the stream for next event while ignoring ping.""" + while True: + last_new_line = False + data = b'' - @ha.callback - def listener(service_call): - """Helper method that will verify that our service got called. - - Also test if our data came through. - """ - if "test" in service_call.data: - test_value.append(1) - - hass.services.register("test_domain", "test_service", listener) - - requests.post( - _url(const.URL_API_SERVICES_SERVICE.format( - "test_domain", "test_service")), - data=json.dumps({"test": 1}), - headers=HA_HEADERS) - - hass.block_till_done() - - self.assertEqual(1, len(test_value)) - - def test_api_template(self): - """Test the template API.""" - hass.states.set('sensor.temperature', 10) - - req = requests.post( - _url(const.URL_API_TEMPLATE), - json={"template": '{{ states.sensor.temperature.state }}'}, - headers=HA_HEADERS) - - self.assertEqual('10', req.text) - - def test_api_template_error(self): - """Test the template API.""" - hass.states.set('sensor.temperature', 10) - - req = requests.post( - _url(const.URL_API_TEMPLATE), - data=json.dumps({"template": - '{{ states.sensor.temperature.state'}), - headers=HA_HEADERS) - - self.assertEqual(400, req.status_code) - - def test_stream(self): - """Test the stream.""" - listen_count = self._listen_count() - with closing(requests.get(_url(const.URL_API_STREAM), timeout=3, - stream=True, headers=HA_HEADERS)) as req: - stream = req.iter_content(1) - self.assertEqual(listen_count + 1, self._listen_count()) - - hass.bus.fire('test_event') - - data = self._stream_next_event(stream) - - self.assertEqual('test_event', data['event_type']) - - def test_stream_with_restricted(self): - """Test the stream with restrictions.""" - listen_count = self._listen_count() - url = _url('{}?restrict=test_event1,test_event3'.format( - const.URL_API_STREAM)) - with closing(requests.get(url, stream=True, timeout=3, - headers=HA_HEADERS)) as req: - stream = req.iter_content(1) - self.assertEqual(listen_count + 1, self._listen_count()) - - hass.bus.fire('test_event1') - data = self._stream_next_event(stream) - self.assertEqual('test_event1', data['event_type']) - - hass.bus.fire('test_event2') - hass.bus.fire('test_event3') - - data = self._stream_next_event(stream) - self.assertEqual('test_event3', data['event_type']) - - def _stream_next_event(self, stream): - """Read the stream for next event while ignoring ping.""" while True: - data = b'' - last_new_line = False - for dat in stream: - if dat == b'\n' and last_new_line: - break - data += dat - last_new_line = dat == b'\n' - - conv = data.decode('utf-8').strip()[6:] - - if conv != 'ping': + dat = yield from stream.read(1) + if dat == b'\n' and last_new_line: break + data += dat + last_new_line = dat == b'\n' - return json.loads(conv) + conv = data.decode('utf-8').strip()[6:] - def _listen_count(self): - """Return number of event listeners.""" - return sum(hass.bus.listeners.values()) + if conv != 'ping': + break + return json.loads(conv) + + +def _listen_count(hass): + """Return number of event listeners.""" + return sum(hass.bus.async_listeners().values()) diff --git a/tests/components/test_device_sun_light_trigger.py b/tests/components/test_device_sun_light_trigger.py index 5cd85a16a7a..3c73e85c4e5 100644 --- a/tests/components/test_device_sun_light_trigger.py +++ b/tests/components/test_device_sun_light_trigger.py @@ -1,7 +1,6 @@ """The tests device sun light trigger component.""" # pylint: disable=protected-access from datetime import datetime -import os import unittest from unittest.mock import patch @@ -12,32 +11,7 @@ from homeassistant.components import ( device_tracker, light, device_sun_light_trigger) from homeassistant.util import dt as dt_util -from tests.common import ( - get_test_config_dir, get_test_home_assistant, fire_time_changed) - - -KNOWN_DEV_YAML_PATH = os.path.join(get_test_config_dir(), - device_tracker.YAML_DEVICES) - - -# pylint: disable=invalid-name -def setUpModule(): - """Write a device tracker known devices file to be used.""" - device_tracker.update_config( - KNOWN_DEV_YAML_PATH, 'device_1', device_tracker.Device( - None, None, True, 'device_1', 'DEV1', - picture='http://example.com/dev1.jpg')) - - device_tracker.update_config( - KNOWN_DEV_YAML_PATH, 'device_2', device_tracker.Device( - None, None, True, 'device_2', 'DEV2', - picture='http://example.com/dev2.jpg')) - - -# pylint: disable=invalid-name -def tearDownModule(): - """Remove device tracker known devices file.""" - os.remove(KNOWN_DEV_YAML_PATH) +from tests.common import get_test_home_assistant, fire_time_changed class TestDeviceSunLightTrigger(unittest.TestCase): @@ -55,9 +29,28 @@ class TestDeviceSunLightTrigger(unittest.TestCase): loader.get_component('light.test').init() - self.assertTrue(setup_component(self.hass, device_tracker.DOMAIN, { - device_tracker.DOMAIN: {CONF_PLATFORM: 'test'} - })) + with patch( + 'homeassistant.components.device_tracker.load_yaml_config_file', + return_value={ + 'device_1': { + 'hide_if_away': False, + 'mac': 'DEV1', + 'name': 'Unnamed Device', + 'picture': 'http://example.com/dev1.jpg', + 'track': True, + 'vendor': None + }, + 'device_2': { + 'hide_if_away': False, + 'mac': 'DEV2', + 'name': 'Unnamed Device', + 'picture': 'http://example.com/dev2.jpg', + 'track': True, + 'vendor': None} + }): + self.assertTrue(setup_component(self.hass, device_tracker.DOMAIN, { + device_tracker.DOMAIN: {CONF_PLATFORM: 'test'} + })) self.assertTrue(setup_component(self.hass, light.DOMAIN, { light.DOMAIN: {CONF_PLATFORM: 'test'} diff --git a/tests/components/test_frontend.py b/tests/components/test_frontend.py index 952061be3c2..ce6fce03e83 100644 --- a/tests/components/test_frontend.py +++ b/tests/components/test_frontend.py @@ -8,10 +8,10 @@ from homeassistant.setup import async_setup_component @pytest.fixture -def mock_http_client(loop, hass, test_client): +def mock_http_client(hass, test_client): """Start the Hass HTTP component.""" - loop.run_until_complete(async_setup_component(hass, 'frontend', {})) - return loop.run_until_complete(test_client(hass.http.app)) + hass.loop.run_until_complete(async_setup_component(hass, 'frontend', {})) + return hass.loop.run_until_complete(test_client(hass.http.app)) @asyncio.coroutine