From e68e29e03ebd43175761d1ae2b4e598d382d2cf4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 11 Jan 2017 21:25:02 +0100 Subject: [PATCH] Upgrade to aiohttp 1.2 (#4964) * Upgrade to aiohttp 1.2 * Clean up emulated_hue tests --- .../components/emulated_hue/hue_api.py | 2 +- homeassistant/components/http/__init__.py | 4 +- homeassistant/components/http/static.py | 65 +- requirements_all.txt | 2 +- setup.py | 2 +- tests/components/emulated_hue/test_hue_api.py | 571 +++++++++--------- 6 files changed, 316 insertions(+), 330 deletions(-) diff --git a/homeassistant/components/emulated_hue/hue_api.py b/homeassistant/components/emulated_hue/hue_api.py index 24060bdfbcb..9b0a2828394 100644 --- a/homeassistant/components/emulated_hue/hue_api.py +++ b/homeassistant/components/emulated_hue/hue_api.py @@ -91,7 +91,7 @@ class HueOneLightStateView(HomeAssistantView): self.config = config @core.callback - def get(self, request, username, entity_id=None): + def get(self, request, username, entity_id): """Process a request to get the state of an individual light.""" hass = request.app['hass'] entity_id = self.config.number_to_entity_id(entity_id) diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index e35b5f31d8f..2bb35dd8f3f 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -32,7 +32,7 @@ from .const import ( KEY_USE_X_FORWARDED_FOR, KEY_TRUSTED_NETWORKS, KEY_BANS_ENABLED, KEY_LOGIN_THRESHOLD, KEY_DEVELOPMENT, KEY_AUTHENTICATED) -from .static import FILE_SENDER, GZIP_FILE_SENDER, staticresource_middleware +from .static import FILE_SENDER, CACHING_FILE_SENDER, staticresource_middleware from .util import get_real_ip DOMAIN = 'http' @@ -272,7 +272,7 @@ class HomeAssistantWSGI(object): @asyncio.coroutine def serve_file(request): """Serve file from disk.""" - res = yield from GZIP_FILE_SENDER.send(request, filepath) + res = yield from CACHING_FILE_SENDER.send(request, filepath) return res # aiohttp supports regex matching for variables. Using that as temp diff --git a/homeassistant/components/http/static.py b/homeassistant/components/http/static.py index 0bd68d6136e..6489144ec70 100644 --- a/homeassistant/components/http/static.py +++ b/homeassistant/components/http/static.py @@ -1,69 +1,40 @@ """Static file handling for HTTP component.""" import asyncio -import mimetypes import re from aiohttp import hdrs from aiohttp.file_sender import FileSender from aiohttp.web_urldispatcher import StaticResource -from aiohttp.web_exceptions import HTTPNotModified - from .const import KEY_DEVELOPMENT _FINGERPRINT = re.compile(r'^(.+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE) -class GzipFileSender(FileSender): - """FileSender class capable of sending gzip version if available.""" +class CachingFileSender(FileSender): + """FileSender class that caches output if not in dev mode.""" - # pylint: disable=invalid-name + def __init__(self, *args, **kwargs): + """Initialize the hass file sender.""" + super().__init__(*args, **kwargs) - @asyncio.coroutine - def send(self, request, filepath): - """Send filepath to client using request.""" - gzip = False - if 'gzip' in request.headers[hdrs.ACCEPT_ENCODING]: - gzip_path = filepath.with_name(filepath.name + '.gz') + orig_sendfile = self._sendfile - if gzip_path.is_file(): - filepath = gzip_path - gzip = True + @asyncio.coroutine + def sendfile(request, resp, fobj, count): + """Sendfile that includes a cache header.""" + if not request.app[KEY_DEVELOPMENT]: + cache_time = 31 * 86400 # = 1 month + resp.headers[hdrs.CACHE_CONTROL] = "public, max-age={}".format( + cache_time) - st = filepath.stat() + yield from orig_sendfile(request, resp, fobj, count) - modsince = request.if_modified_since - if modsince is not None and st.st_mtime <= modsince.timestamp(): - raise HTTPNotModified() - - ct, encoding = mimetypes.guess_type(str(filepath)) - if not ct: - ct = 'application/octet-stream' - - resp = self._response_factory() - resp.content_type = ct - if encoding: - resp.headers[hdrs.CONTENT_ENCODING] = encoding - if gzip: - resp.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING - resp.last_modified = st.st_mtime - - # CACHE HACK - if not request.app[KEY_DEVELOPMENT]: - cache_time = 31 * 86400 # = 1 month - resp.headers[hdrs.CACHE_CONTROL] = "public, max-age={}".format( - cache_time) - - file_size = st.st_size - - resp.content_length = file_size - with filepath.open('rb') as f: - yield from self._sendfile(request, resp, f, file_size) - - return resp + # Overwriting like this because __init__ can change implementation. + self._sendfile = sendfile -GZIP_FILE_SENDER = GzipFileSender() FILE_SENDER = FileSender() +CACHING_FILE_SENDER = CachingFileSender() @asyncio.coroutine @@ -77,7 +48,7 @@ def staticresource_middleware(app, handler): return handler # pylint: disable=protected-access - inst._file_sender = GZIP_FILE_SENDER + inst._file_sender = CACHING_FILE_SENDER @asyncio.coroutine def static_middleware_handler(request): diff --git a/requirements_all.txt b/requirements_all.txt index fdd35f6dfe3..78f0bba24e9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -6,7 +6,7 @@ pip>=7.0.0 jinja2>=2.8 voluptuous==0.9.2 typing>=3,<4 -aiohttp==1.1.6 +aiohttp==1.2 async_timeout==1.1.0 # homeassistant.components.nuimo_controller diff --git a/setup.py b/setup.py index a62dbed80e8..d83fed1329b 100755 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ REQUIRES = [ 'jinja2>=2.8', 'voluptuous==0.9.2', 'typing>=3,<4', - 'aiohttp==1.1.6', + 'aiohttp==1.2', 'async_timeout==1.1.0', ] diff --git a/tests/components/emulated_hue/test_hue_api.py b/tests/components/emulated_hue/test_hue_api.py index 4aab6401939..0b36b835cd5 100644 --- a/tests/components/emulated_hue/test_hue_api.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -1,9 +1,9 @@ """The tests for the emulated Hue component.""" +import asyncio import json -import unittest from unittest.mock import patch -import requests +import pytest from homeassistant import bootstrap, const, core import homeassistant.components as core_components @@ -12,10 +12,12 @@ from homeassistant.components import ( ) from homeassistant.const import STATE_ON, STATE_OFF from homeassistant.components.emulated_hue.hue_api import ( - HUE_API_STATE_ON, HUE_API_STATE_BRI) -from homeassistant.util.async import run_coroutine_threadsafe + HUE_API_STATE_ON, HUE_API_STATE_BRI, HueUsernameView, + HueAllLightsStateView, HueOneLightStateView, HueOneLightChangeView) +from homeassistant.components.emulated_hue import Config -from tests.common import get_test_instance_port, get_test_home_assistant +from tests.common import ( + get_test_instance_port, mock_http_component_app) HTTP_SERVER_PORT = get_test_instance_port() BRIDGE_SERVER_PORT = get_test_instance_port() @@ -24,41 +26,38 @@ BRIDGE_URL_BASE = 'http://127.0.0.1:{}'.format(BRIDGE_SERVER_PORT) + '{}' JSON_HEADERS = {const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON} -class TestEmulatedHueExposedByDefault(unittest.TestCase): - """Test class for emulated hue component.""" +@pytest.fixture +def hass_hue(loop, hass): + """Setup a hass instance for these tests.""" + # We need to do this to get access to homeassistant/turn_(on,off) + loop.run_until_complete( + core_components.async_setup(hass, {core.DOMAIN: {}})) - @classmethod - def setUpClass(cls): - """Setup the class.""" - cls.hass = hass = get_test_home_assistant() + loop.run_until_complete(bootstrap.async_setup_component( + hass, http.DOMAIN, + {http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}})) - # We need to do this to get access to homeassistant/turn_(on,off) - run_coroutine_threadsafe( - core_components.async_setup(hass, {core.DOMAIN: {}}), hass.loop - ).result() - - bootstrap.setup_component( - hass, http.DOMAIN, - {http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}}) - - with patch('homeassistant.components' - '.emulated_hue.UPNPResponderThread'): - bootstrap.setup_component(hass, emulated_hue.DOMAIN, { + with patch('homeassistant.components' + '.emulated_hue.UPNPResponderThread'): + loop.run_until_complete( + bootstrap.async_setup_component(hass, emulated_hue.DOMAIN, { emulated_hue.DOMAIN: { emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT, emulated_hue.CONF_EXPOSE_BY_DEFAULT: True } - }) + })) - bootstrap.setup_component(cls.hass, light.DOMAIN, { + loop.run_until_complete( + bootstrap.async_setup_component(hass, light.DOMAIN, { 'light': [ { 'platform': 'demo', } ] - }) + })) - bootstrap.setup_component(cls.hass, script.DOMAIN, { + loop.run_until_complete( + bootstrap.async_setup_component(hass, script.DOMAIN, { 'script': { 'set_kitchen_light': { 'sequence': [ @@ -73,338 +72,354 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): ] } } - }) + })) - bootstrap.setup_component(cls.hass, media_player.DOMAIN, { + loop.run_until_complete( + bootstrap.async_setup_component(hass, media_player.DOMAIN, { 'media_player': [ { 'platform': 'demo', } ] - }) + })) - cls.hass.start() + # Kitchen light is explicitly excluded from being exposed + kitchen_light_entity = hass.states.get('light.kitchen_lights') + attrs = dict(kitchen_light_entity.attributes) + attrs[emulated_hue.ATTR_EMULATED_HUE] = False + hass.states.async_set( + kitchen_light_entity.entity_id, kitchen_light_entity.state, + attributes=attrs) - # Kitchen light is explicitly excluded from being exposed - kitchen_light_entity = cls.hass.states.get('light.kitchen_lights') - attrs = dict(kitchen_light_entity.attributes) - attrs[emulated_hue.ATTR_EMULATED_HUE] = False - cls.hass.states.set( - kitchen_light_entity.entity_id, kitchen_light_entity.state, - attributes=attrs) + # Expose the script + script_entity = hass.states.get('script.set_kitchen_light') + attrs = dict(script_entity.attributes) + attrs[emulated_hue.ATTR_EMULATED_HUE] = True + hass.states.async_set( + script_entity.entity_id, script_entity.state, attributes=attrs + ) - # Expose the script - script_entity = cls.hass.states.get('script.set_kitchen_light') - attrs = dict(script_entity.attributes) - attrs[emulated_hue.ATTR_EMULATED_HUE] = True - cls.hass.states.set( - script_entity.entity_id, script_entity.state, attributes=attrs - ) + return hass - @classmethod - def tearDownClass(cls): - """Stop the class.""" - cls.hass.stop() - def test_discover_lights(self): - """Test the discovery of lights.""" - result = requests.get( - BRIDGE_URL_BASE.format('/api/username/lights'), timeout=5) +@pytest.fixture +def hue_client(loop, hass_hue, test_client): + """Create web client for emulated hue api.""" + web_app = mock_http_component_app(hass_hue) + config = Config({'type': 'alexa'}) - self.assertEqual(result.status_code, 200) - self.assertTrue('application/json' in result.headers['content-type']) + HueUsernameView().register(web_app.router) + HueAllLightsStateView(config).register(web_app.router) + HueOneLightStateView(config).register(web_app.router) + HueOneLightChangeView(config).register(web_app.router) - result_json = result.json() + return loop.run_until_complete(test_client(web_app)) - # Make sure the lights we added to the config are there - self.assertTrue('light.ceiling_lights' in result_json) - self.assertTrue('light.bed_light' in result_json) - self.assertTrue('script.set_kitchen_light' in result_json) - self.assertTrue('light.kitchen_lights' not in result_json) - self.assertTrue('media_player.living_room' in result_json) - self.assertTrue('media_player.bedroom' in result_json) - self.assertTrue('media_player.walkman' in result_json) - self.assertTrue('media_player.lounge_room' in result_json) - def test_get_light_state(self): - """Test the getting of light state.""" - # Turn office light on and set to 127 brightness - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_ON, - { - const.ATTR_ENTITY_ID: 'light.ceiling_lights', - light.ATTR_BRIGHTNESS: 127 - }, - blocking=True) +@asyncio.coroutine +def test_discover_lights(hue_client): + """Test the discovery of lights.""" + result = yield from hue_client.get('/api/username/lights') - office_json = self.perform_get_light_state('light.ceiling_lights', 200) + assert result.status == 200 + assert 'application/json' in result.headers['content-type'] - self.assertEqual(office_json['state'][HUE_API_STATE_ON], True) - self.assertEqual(office_json['state'][HUE_API_STATE_BRI], 127) + result_json = yield from result.json() - # Check all lights view - result = requests.get( - BRIDGE_URL_BASE.format('/api/username/lights'), timeout=5) + devices = set(val['uniqueid'] for val in result_json.values()) - self.assertEqual(result.status_code, 200) - self.assertTrue('application/json' in result.headers['content-type']) + # Make sure the lights we added to the config are there + assert 'light.ceiling_lights' in devices + assert 'light.bed_light' in devices + assert 'script.set_kitchen_light' in devices + assert 'light.kitchen_lights' not in devices + assert 'media_player.living_room' in devices + assert 'media_player.bedroom' in devices + assert 'media_player.walkman' in devices + assert 'media_player.lounge_room' in devices - result_json = result.json() - self.assertTrue('light.ceiling_lights' in result_json) - self.assertEqual( - result_json['light.ceiling_lights']['state'][HUE_API_STATE_BRI], - 127, - ) +@asyncio.coroutine +def test_get_light_state(hass_hue, hue_client): + """Test the getting of light state.""" + # Turn office light on and set to 127 brightness + yield from hass_hue.services.async_call( + light.DOMAIN, const.SERVICE_TURN_ON, + { + const.ATTR_ENTITY_ID: 'light.ceiling_lights', + light.ATTR_BRIGHTNESS: 127 + }, + blocking=True) - # Turn bedroom light off - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_OFF, - { - const.ATTR_ENTITY_ID: 'light.bed_light' - }, - blocking=True) + office_json = yield from perform_get_light_state( + hue_client, 'light.ceiling_lights', 200) - bedroom_json = self.perform_get_light_state('light.bed_light', 200) + assert office_json['state'][HUE_API_STATE_ON] is True + assert office_json['state'][HUE_API_STATE_BRI] == 127 - self.assertEqual(bedroom_json['state'][HUE_API_STATE_ON], False) - self.assertEqual(bedroom_json['state'][HUE_API_STATE_BRI], 0) + # Check all lights view + result = yield from hue_client.get('/api/username/lights') - # Make sure kitchen light isn't accessible - kitchen_url = '/api/username/lights/{}'.format('light.kitchen_lights') - kitchen_result = requests.get( - BRIDGE_URL_BASE.format(kitchen_url), timeout=5) + assert result.status == 200 + assert 'application/json' in result.headers['content-type'] - self.assertEqual(kitchen_result.status_code, 404) + result_json = yield from result.json() - def test_put_light_state(self): - """Test the seeting of light states.""" - self.perform_put_test_on_ceiling_lights() + assert 'light.ceiling_lights' in result_json + assert result_json['light.ceiling_lights']['state'][HUE_API_STATE_BRI] == \ + 127 - # Turn the bedroom light on first - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_ON, - {const.ATTR_ENTITY_ID: 'light.bed_light', - light.ATTR_BRIGHTNESS: 153}, - blocking=True) + # Turn bedroom light off + yield from hass_hue.services.async_call( + light.DOMAIN, const.SERVICE_TURN_OFF, + { + const.ATTR_ENTITY_ID: 'light.bed_light' + }, + blocking=True) - bed_light = self.hass.states.get('light.bed_light') - self.assertEqual(bed_light.state, STATE_ON) - self.assertEqual(bed_light.attributes[light.ATTR_BRIGHTNESS], 153) + bedroom_json = yield from perform_get_light_state( + hue_client, 'light.bed_light', 200) - # Go through the API to turn it off - bedroom_result = self.perform_put_light_state( - 'light.bed_light', False) + assert bedroom_json['state'][HUE_API_STATE_ON] is False + assert bedroom_json['state'][HUE_API_STATE_BRI] == 0 - bedroom_result_json = bedroom_result.json() + # Make sure kitchen light isn't accessible + yield from perform_get_light_state( + hue_client, 'light.kitchen_lights', 404) - self.assertEqual(bedroom_result.status_code, 200) - self.assertTrue( - 'application/json' in bedroom_result.headers['content-type']) - self.assertEqual(len(bedroom_result_json), 1) +@asyncio.coroutine +def test_put_light_state(hass_hue, hue_client): + """Test the seeting of light states.""" + yield from perform_put_test_on_ceiling_lights(hass_hue, hue_client) - # Check to make sure the state changed - bed_light = self.hass.states.get('light.bed_light') - self.assertEqual(bed_light.state, STATE_OFF) + # Turn the bedroom light on first + yield from hass_hue.services.async_call( + light.DOMAIN, const.SERVICE_TURN_ON, + {const.ATTR_ENTITY_ID: 'light.bed_light', + light.ATTR_BRIGHTNESS: 153}, + blocking=True) - # Make sure we can't change the kitchen light state - kitchen_result = self.perform_put_light_state( - 'light.kitchen_light', True) - self.assertEqual(kitchen_result.status_code, 404) + bed_light = hass_hue.states.get('light.bed_light') + assert bed_light.state == STATE_ON + assert bed_light.attributes[light.ATTR_BRIGHTNESS] == 153 - def test_put_light_state_script(self): - """Test the setting of script variables.""" - # Turn the kitchen light off first - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_OFF, - {const.ATTR_ENTITY_ID: 'light.kitchen_lights'}, - blocking=True) + # Go through the API to turn it off + bedroom_result = yield from perform_put_light_state( + hass_hue, hue_client, + 'light.bed_light', False) - # Emulated hue converts 0-100% to 0-255. - level = 23 - brightness = round(level * 255 / 100) + bedroom_result_json = yield from bedroom_result.json() - script_result = self.perform_put_light_state( - 'script.set_kitchen_light', True, brightness) + assert bedroom_result.status == 200 + assert 'application/json' in bedroom_result.headers['content-type'] - script_result_json = script_result.json() + assert len(bedroom_result_json) == 1 - self.assertEqual(script_result.status_code, 200) - self.assertEqual(len(script_result_json), 2) + # Check to make sure the state changed + bed_light = hass_hue.states.get('light.bed_light') + assert bed_light.state == STATE_OFF - kitchen_light = self.hass.states.get('light.kitchen_lights') - self.assertEqual(kitchen_light.state, 'on') - self.assertEqual( - kitchen_light.attributes[light.ATTR_BRIGHTNESS], - level) + # Make sure we can't change the kitchen light state + kitchen_result = yield from perform_put_light_state( + hass_hue, hue_client, + 'light.kitchen_light', True) + assert kitchen_result.status == 404 - def test_put_light_state_media_player(self): - """Test turning on media player and setting volume.""" - # Turn the music player off first - self.hass.services.call( - media_player.DOMAIN, const.SERVICE_TURN_OFF, - {const.ATTR_ENTITY_ID: 'media_player.walkman'}, - blocking=True) - # Emulated hue converts 0.0-1.0 to 0-255. - level = 0.25 - brightness = round(level * 255) +@asyncio.coroutine +def test_put_light_state_script(hass_hue, hue_client): + """Test the setting of script variables.""" + # Turn the kitchen light off first + yield from hass_hue.services.async_call( + light.DOMAIN, const.SERVICE_TURN_OFF, + {const.ATTR_ENTITY_ID: 'light.kitchen_lights'}, + blocking=True) - mp_result = self.perform_put_light_state( - 'media_player.walkman', True, brightness) + # Emulated hue converts 0-100% to 0-255. + level = 23 + brightness = round(level * 255 / 100) - mp_result_json = mp_result.json() + script_result = yield from perform_put_light_state( + hass_hue, hue_client, + 'script.set_kitchen_light', True, brightness) - self.assertEqual(mp_result.status_code, 200) - self.assertEqual(len(mp_result_json), 2) + script_result_json = yield from script_result.json() - walkman = self.hass.states.get('media_player.walkman') - self.assertEqual(walkman.state, 'playing') - self.assertEqual( - walkman.attributes[media_player.ATTR_MEDIA_VOLUME_LEVEL], - level) + assert script_result.status == 200 + assert len(script_result_json) == 2 - # pylint: disable=invalid-name - def test_put_with_form_urlencoded_content_type(self): - """Test the form with urlencoded content.""" - # Needed for Alexa - self.perform_put_test_on_ceiling_lights( - 'application/x-www-form-urlencoded') + kitchen_light = hass_hue.states.get('light.kitchen_lights') + assert kitchen_light.state == 'on' + assert kitchen_light.attributes[light.ATTR_BRIGHTNESS] == level - # Make sure we fail gracefully when we can't parse the data - data = {'key1': 'value1', 'key2': 'value2'} - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - 'light.ceiling_lights')), data=data) - self.assertEqual(result.status_code, 400) +@asyncio.coroutine +def test_put_light_state_media_player(hass_hue, hue_client): + """Test turning on media player and setting volume.""" + # Turn the music player off first + yield from hass_hue.services.async_call( + media_player.DOMAIN, const.SERVICE_TURN_OFF, + {const.ATTR_ENTITY_ID: 'media_player.walkman'}, + blocking=True) - def test_entity_not_found(self): - """Test for entity which are not found.""" - result = requests.get( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}'.format("not.existant_entity")), - timeout=5) + # Emulated hue converts 0.0-1.0 to 0-255. + level = 0.25 + brightness = round(level * 255) - self.assertEqual(result.status_code, 404) + mp_result = yield from perform_put_light_state( + hass_hue, hue_client, + 'media_player.walkman', True, brightness) - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format("non.existant_entity")), - timeout=5) + mp_result_json = yield from mp_result.json() - self.assertEqual(result.status_code, 404) + assert mp_result.status == 200 + assert len(mp_result_json) == 2 - def test_allowed_methods(self): - """Test the allowed methods.""" - result = requests.get( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - "light.ceiling_lights"))) + walkman = hass_hue.states.get('media_player.walkman') + assert walkman.state == 'playing' + assert walkman.attributes[media_player.ATTR_MEDIA_VOLUME_LEVEL] == level - self.assertEqual(result.status_code, 405) - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}'.format("light.ceiling_lights")), - data={'key1': 'value1'}) +# pylint: disable=invalid-name +@asyncio.coroutine +def test_put_with_form_urlencoded_content_type(hass_hue, hue_client): + """Test the form with urlencoded content.""" + # Needed for Alexa + yield from perform_put_test_on_ceiling_lights( + hass_hue, hue_client, 'application/x-www-form-urlencoded') - self.assertEqual(result.status_code, 405) + # Make sure we fail gracefully when we can't parse the data + data = {'key1': 'value1', 'key2': 'value2'} + result = yield from hue_client.put( + '/api/username/lights/light.ceiling_lights/state', + headers={ + 'content-type': 'application/x-www-form-urlencoded' + }, + data=data, + ) - result = requests.put( - BRIDGE_URL_BASE.format('/api/username/lights'), - data={'key1': 'value1'}) + assert result.status == 400 - self.assertEqual(result.status_code, 405) - def test_proper_put_state_request(self): - """Test the request to set the state.""" - # Test proper on value parsing - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - 'light.ceiling_lights')), +@asyncio.coroutine +def test_entity_not_found(hue_client): + """Test for entity which are not found.""" + result = yield from hue_client.get( + '/api/username/lights/not.existant_entity') + + assert result.status == 404 + + result = yield from hue_client.put( + '/api/username/lights/not.existant_entity/state') + + assert result.status == 404 + + +@asyncio.coroutine +def test_allowed_methods(hue_client): + """Test the allowed methods.""" + result = yield from hue_client.get( + '/api/username/lights/light.ceiling_lights/state') + + assert result.status == 405 + + result = yield from hue_client.put( + '/api/username/lights/light.ceiling_lights') + + assert result.status == 405 + + result = yield from hue_client.put( + '/api/username/lights') + + assert result.status == 405 + + +@asyncio.coroutine +def test_proper_put_state_request(hue_client): + """Test the request to set the state.""" + # Test proper on value parsing + result = yield from hue_client.put( + '/api/username/lights/{}/state'.format( + 'light.ceiling_lights'), data=json.dumps({HUE_API_STATE_ON: 1234})) - self.assertEqual(result.status_code, 400) + assert result.status == 400 - # Test proper brightness value parsing - result = requests.put( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format( - 'light.ceiling_lights')), data=json.dumps({ - HUE_API_STATE_ON: True, - HUE_API_STATE_BRI: 'Hello world!' - })) + # Test proper brightness value parsing + result = yield from hue_client.put( + '/api/username/lights/{}/state'.format( + 'light.ceiling_lights'), + data=json.dumps({ + HUE_API_STATE_ON: True, + HUE_API_STATE_BRI: 'Hello world!' + })) - self.assertEqual(result.status_code, 400) + assert result.status == 400 - # pylint: disable=invalid-name - def perform_put_test_on_ceiling_lights(self, - content_type='application/json'): - """Test the setting of a light.""" - # Turn the office light off first - self.hass.services.call( - light.DOMAIN, const.SERVICE_TURN_OFF, - {const.ATTR_ENTITY_ID: 'light.ceiling_lights'}, - blocking=True) - ceiling_lights = self.hass.states.get('light.ceiling_lights') - self.assertEqual(ceiling_lights.state, STATE_OFF) +# pylint: disable=invalid-name +def perform_put_test_on_ceiling_lights(hass_hue, hue_client, + content_type='application/json'): + """Test the setting of a light.""" + # Turn the office light off first + yield from hass_hue.services.async_call( + light.DOMAIN, const.SERVICE_TURN_OFF, + {const.ATTR_ENTITY_ID: 'light.ceiling_lights'}, + blocking=True) - # Go through the API to turn it on - office_result = self.perform_put_light_state( - 'light.ceiling_lights', True, 56, content_type) + ceiling_lights = hass_hue.states.get('light.ceiling_lights') + assert ceiling_lights.state == STATE_OFF - office_result_json = office_result.json() + # Go through the API to turn it on + office_result = yield from perform_put_light_state( + hass_hue, hue_client, + 'light.ceiling_lights', True, 56, content_type) - self.assertEqual(office_result.status_code, 200) - self.assertTrue( - 'application/json' in office_result.headers['content-type']) + assert office_result.status == 200 + assert 'application/json' in office_result.headers['content-type'] - self.assertEqual(len(office_result_json), 2) + office_result_json = yield from office_result.json() - # Check to make sure the state changed - ceiling_lights = self.hass.states.get('light.ceiling_lights') - self.assertEqual(ceiling_lights.state, STATE_ON) - self.assertEqual(ceiling_lights.attributes[light.ATTR_BRIGHTNESS], 56) + assert len(office_result_json) == 2 - def perform_get_light_state(self, entity_id, expected_status): - """Test the gettting of a light state.""" - result = requests.get( - BRIDGE_URL_BASE.format( - '/api/username/lights/{}'.format(entity_id)), timeout=5) + # Check to make sure the state changed + ceiling_lights = hass_hue.states.get('light.ceiling_lights') + assert ceiling_lights.state == STATE_ON + assert ceiling_lights.attributes[light.ATTR_BRIGHTNESS] == 56 - self.assertEqual(result.status_code, expected_status) - if expected_status == 200: - self.assertTrue( - 'application/json' in result.headers['content-type']) +@asyncio.coroutine +def perform_get_light_state(client, entity_id, expected_status): + """Test the gettting of a light state.""" + result = yield from client.get('/api/username/lights/{}'.format(entity_id)) - return result.json() + assert result.status == expected_status - return None + if expected_status == 200: + assert 'application/json' in result.headers['content-type'] - # pylint: disable=no-self-use - def perform_put_light_state(self, entity_id, is_on, brightness=None, - content_type='application/json'): - """Test the setting of a light state.""" - url = BRIDGE_URL_BASE.format( - '/api/username/lights/{}/state'.format(entity_id)) + return (yield from result.json()) - req_headers = {'Content-Type': content_type} + return None - data = {HUE_API_STATE_ON: is_on} - if brightness is not None: - data[HUE_API_STATE_BRI] = brightness +@asyncio.coroutine +def perform_put_light_state(hass_hue, client, entity_id, is_on, + brightness=None, content_type='application/json'): + """Test the setting of a light state.""" + req_headers = {'Content-Type': content_type} - result = requests.put( - url, data=json.dumps(data), timeout=5, headers=req_headers) + data = {HUE_API_STATE_ON: is_on} - # Wait until state change is complete before continuing - self.hass.block_till_done() + if brightness is not None: + data[HUE_API_STATE_BRI] = brightness - return result + result = yield from client.put( + '/api/username/lights/{}/state'.format(entity_id), headers=req_headers, + data=json.dumps(data).encode()) + + # Wait until state change is complete before continuing + yield from hass_hue.async_block_till_done() + + return result