Upgrade to aiohttp 1.2 (#4964)

* Upgrade to aiohttp 1.2

* Clean up emulated_hue tests
This commit is contained in:
Paulus Schoutsen 2017-01-11 21:25:02 +01:00 committed by GitHub
parent 1cf9ae5a01
commit e68e29e03e
6 changed files with 316 additions and 330 deletions

View file

@ -91,7 +91,7 @@ class HueOneLightStateView(HomeAssistantView):
self.config = config self.config = config
@core.callback @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.""" """Process a request to get the state of an individual light."""
hass = request.app['hass'] hass = request.app['hass']
entity_id = self.config.number_to_entity_id(entity_id) entity_id = self.config.number_to_entity_id(entity_id)

View file

@ -32,7 +32,7 @@ from .const import (
KEY_USE_X_FORWARDED_FOR, KEY_TRUSTED_NETWORKS, KEY_USE_X_FORWARDED_FOR, KEY_TRUSTED_NETWORKS,
KEY_BANS_ENABLED, KEY_LOGIN_THRESHOLD, KEY_BANS_ENABLED, KEY_LOGIN_THRESHOLD,
KEY_DEVELOPMENT, KEY_AUTHENTICATED) 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 from .util import get_real_ip
DOMAIN = 'http' DOMAIN = 'http'
@ -272,7 +272,7 @@ class HomeAssistantWSGI(object):
@asyncio.coroutine @asyncio.coroutine
def serve_file(request): def serve_file(request):
"""Serve file from disk.""" """Serve file from disk."""
res = yield from GZIP_FILE_SENDER.send(request, filepath) res = yield from CACHING_FILE_SENDER.send(request, filepath)
return res return res
# aiohttp supports regex matching for variables. Using that as temp # aiohttp supports regex matching for variables. Using that as temp

View file

@ -1,69 +1,40 @@
"""Static file handling for HTTP component.""" """Static file handling for HTTP component."""
import asyncio import asyncio
import mimetypes
import re import re
from aiohttp import hdrs from aiohttp import hdrs
from aiohttp.file_sender import FileSender from aiohttp.file_sender import FileSender
from aiohttp.web_urldispatcher import StaticResource from aiohttp.web_urldispatcher import StaticResource
from aiohttp.web_exceptions import HTTPNotModified
from .const import KEY_DEVELOPMENT from .const import KEY_DEVELOPMENT
_FINGERPRINT = re.compile(r'^(.+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE) _FINGERPRINT = re.compile(r'^(.+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE)
class GzipFileSender(FileSender): class CachingFileSender(FileSender):
"""FileSender class capable of sending gzip version if available.""" """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)
orig_sendfile = self._sendfile
@asyncio.coroutine @asyncio.coroutine
def send(self, request, filepath): def sendfile(request, resp, fobj, count):
"""Send filepath to client using request.""" """Sendfile that includes a cache header."""
gzip = False
if 'gzip' in request.headers[hdrs.ACCEPT_ENCODING]:
gzip_path = filepath.with_name(filepath.name + '.gz')
if gzip_path.is_file():
filepath = gzip_path
gzip = True
st = filepath.stat()
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]: if not request.app[KEY_DEVELOPMENT]:
cache_time = 31 * 86400 # = 1 month cache_time = 31 * 86400 # = 1 month
resp.headers[hdrs.CACHE_CONTROL] = "public, max-age={}".format( resp.headers[hdrs.CACHE_CONTROL] = "public, max-age={}".format(
cache_time) cache_time)
file_size = st.st_size yield from orig_sendfile(request, resp, fobj, count)
resp.content_length = file_size # Overwriting like this because __init__ can change implementation.
with filepath.open('rb') as f: self._sendfile = sendfile
yield from self._sendfile(request, resp, f, file_size)
return resp
GZIP_FILE_SENDER = GzipFileSender()
FILE_SENDER = FileSender() FILE_SENDER = FileSender()
CACHING_FILE_SENDER = CachingFileSender()
@asyncio.coroutine @asyncio.coroutine
@ -77,7 +48,7 @@ def staticresource_middleware(app, handler):
return handler return handler
# pylint: disable=protected-access # pylint: disable=protected-access
inst._file_sender = GZIP_FILE_SENDER inst._file_sender = CACHING_FILE_SENDER
@asyncio.coroutine @asyncio.coroutine
def static_middleware_handler(request): def static_middleware_handler(request):

View file

@ -6,7 +6,7 @@ pip>=7.0.0
jinja2>=2.8 jinja2>=2.8
voluptuous==0.9.2 voluptuous==0.9.2
typing>=3,<4 typing>=3,<4
aiohttp==1.1.6 aiohttp==1.2
async_timeout==1.1.0 async_timeout==1.1.0
# homeassistant.components.nuimo_controller # homeassistant.components.nuimo_controller

View file

@ -22,7 +22,7 @@ REQUIRES = [
'jinja2>=2.8', 'jinja2>=2.8',
'voluptuous==0.9.2', 'voluptuous==0.9.2',
'typing>=3,<4', 'typing>=3,<4',
'aiohttp==1.1.6', 'aiohttp==1.2',
'async_timeout==1.1.0', 'async_timeout==1.1.0',
] ]

View file

@ -1,9 +1,9 @@
"""The tests for the emulated Hue component.""" """The tests for the emulated Hue component."""
import asyncio
import json import json
import unittest
from unittest.mock import patch from unittest.mock import patch
import requests import pytest
from homeassistant import bootstrap, const, core from homeassistant import bootstrap, const, core
import homeassistant.components as core_components import homeassistant.components as core_components
@ -12,10 +12,12 @@ from homeassistant.components import (
) )
from homeassistant.const import STATE_ON, STATE_OFF from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.components.emulated_hue.hue_api import ( from homeassistant.components.emulated_hue.hue_api import (
HUE_API_STATE_ON, HUE_API_STATE_BRI) HUE_API_STATE_ON, HUE_API_STATE_BRI, HueUsernameView,
from homeassistant.util.async import run_coroutine_threadsafe 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() HTTP_SERVER_PORT = get_test_instance_port()
BRIDGE_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} JSON_HEADERS = {const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON}
class TestEmulatedHueExposedByDefault(unittest.TestCase): @pytest.fixture
"""Test class for emulated hue component.""" def hass_hue(loop, hass):
"""Setup a hass instance for these tests."""
@classmethod
def setUpClass(cls):
"""Setup the class."""
cls.hass = hass = get_test_home_assistant()
# We need to do this to get access to homeassistant/turn_(on,off) # We need to do this to get access to homeassistant/turn_(on,off)
run_coroutine_threadsafe( loop.run_until_complete(
core_components.async_setup(hass, {core.DOMAIN: {}}), hass.loop core_components.async_setup(hass, {core.DOMAIN: {}}))
).result()
bootstrap.setup_component( loop.run_until_complete(bootstrap.async_setup_component(
hass, http.DOMAIN, hass, http.DOMAIN,
{http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}}) {http.DOMAIN: {http.CONF_SERVER_PORT: HTTP_SERVER_PORT}}))
with patch('homeassistant.components' with patch('homeassistant.components'
'.emulated_hue.UPNPResponderThread'): '.emulated_hue.UPNPResponderThread'):
bootstrap.setup_component(hass, emulated_hue.DOMAIN, { loop.run_until_complete(
bootstrap.async_setup_component(hass, emulated_hue.DOMAIN, {
emulated_hue.DOMAIN: { emulated_hue.DOMAIN: {
emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT, emulated_hue.CONF_LISTEN_PORT: BRIDGE_SERVER_PORT,
emulated_hue.CONF_EXPOSE_BY_DEFAULT: True 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': [ 'light': [
{ {
'platform': 'demo', 'platform': 'demo',
} }
] ]
}) }))
bootstrap.setup_component(cls.hass, script.DOMAIN, { loop.run_until_complete(
bootstrap.async_setup_component(hass, script.DOMAIN, {
'script': { 'script': {
'set_kitchen_light': { 'set_kitchen_light': {
'sequence': [ 'sequence': [
@ -73,63 +72,78 @@ 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': [ 'media_player': [
{ {
'platform': 'demo', 'platform': 'demo',
} }
] ]
}) }))
cls.hass.start()
# Kitchen light is explicitly excluded from being exposed # Kitchen light is explicitly excluded from being exposed
kitchen_light_entity = cls.hass.states.get('light.kitchen_lights') kitchen_light_entity = hass.states.get('light.kitchen_lights')
attrs = dict(kitchen_light_entity.attributes) attrs = dict(kitchen_light_entity.attributes)
attrs[emulated_hue.ATTR_EMULATED_HUE] = False attrs[emulated_hue.ATTR_EMULATED_HUE] = False
cls.hass.states.set( hass.states.async_set(
kitchen_light_entity.entity_id, kitchen_light_entity.state, kitchen_light_entity.entity_id, kitchen_light_entity.state,
attributes=attrs) attributes=attrs)
# Expose the script # Expose the script
script_entity = cls.hass.states.get('script.set_kitchen_light') script_entity = hass.states.get('script.set_kitchen_light')
attrs = dict(script_entity.attributes) attrs = dict(script_entity.attributes)
attrs[emulated_hue.ATTR_EMULATED_HUE] = True attrs[emulated_hue.ATTR_EMULATED_HUE] = True
cls.hass.states.set( hass.states.async_set(
script_entity.entity_id, script_entity.state, attributes=attrs script_entity.entity_id, script_entity.state, attributes=attrs
) )
@classmethod return hass
def tearDownClass(cls):
"""Stop the class."""
cls.hass.stop()
def test_discover_lights(self):
@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'})
HueUsernameView().register(web_app.router)
HueAllLightsStateView(config).register(web_app.router)
HueOneLightStateView(config).register(web_app.router)
HueOneLightChangeView(config).register(web_app.router)
return loop.run_until_complete(test_client(web_app))
@asyncio.coroutine
def test_discover_lights(hue_client):
"""Test the discovery of lights.""" """Test the discovery of lights."""
result = requests.get( result = yield from hue_client.get('/api/username/lights')
BRIDGE_URL_BASE.format('/api/username/lights'), timeout=5)
self.assertEqual(result.status_code, 200) assert result.status == 200
self.assertTrue('application/json' in result.headers['content-type']) assert 'application/json' in result.headers['content-type']
result_json = result.json() result_json = yield from result.json()
devices = set(val['uniqueid'] for val in result_json.values())
# Make sure the lights we added to the config are there # Make sure the lights we added to the config are there
self.assertTrue('light.ceiling_lights' in result_json) assert 'light.ceiling_lights' in devices
self.assertTrue('light.bed_light' in result_json) assert 'light.bed_light' in devices
self.assertTrue('script.set_kitchen_light' in result_json) assert 'script.set_kitchen_light' in devices
self.assertTrue('light.kitchen_lights' not in result_json) assert 'light.kitchen_lights' not in devices
self.assertTrue('media_player.living_room' in result_json) assert 'media_player.living_room' in devices
self.assertTrue('media_player.bedroom' in result_json) assert 'media_player.bedroom' in devices
self.assertTrue('media_player.walkman' in result_json) assert 'media_player.walkman' in devices
self.assertTrue('media_player.lounge_room' in result_json) assert 'media_player.lounge_room' in devices
def test_get_light_state(self):
@asyncio.coroutine
def test_get_light_state(hass_hue, hue_client):
"""Test the getting of light state.""" """Test the getting of light state."""
# Turn office light on and set to 127 brightness # Turn office light on and set to 127 brightness
self.hass.services.call( yield from hass_hue.services.async_call(
light.DOMAIN, const.SERVICE_TURN_ON, light.DOMAIN, const.SERVICE_TURN_ON,
{ {
const.ATTR_ENTITY_ID: 'light.ceiling_lights', const.ATTR_ENTITY_ID: 'light.ceiling_lights',
@ -137,86 +151,87 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase):
}, },
blocking=True) blocking=True)
office_json = self.perform_get_light_state('light.ceiling_lights', 200) office_json = yield from perform_get_light_state(
hue_client, 'light.ceiling_lights', 200)
self.assertEqual(office_json['state'][HUE_API_STATE_ON], True) assert office_json['state'][HUE_API_STATE_ON] is True
self.assertEqual(office_json['state'][HUE_API_STATE_BRI], 127) assert office_json['state'][HUE_API_STATE_BRI] == 127
# Check all lights view # Check all lights view
result = requests.get( result = yield from hue_client.get('/api/username/lights')
BRIDGE_URL_BASE.format('/api/username/lights'), timeout=5)
self.assertEqual(result.status_code, 200) assert result.status == 200
self.assertTrue('application/json' in result.headers['content-type']) assert 'application/json' in result.headers['content-type']
result_json = result.json() result_json = yield from result.json()
self.assertTrue('light.ceiling_lights' in result_json) assert 'light.ceiling_lights' in result_json
self.assertEqual( assert result_json['light.ceiling_lights']['state'][HUE_API_STATE_BRI] == \
result_json['light.ceiling_lights']['state'][HUE_API_STATE_BRI], 127
127,
)
# Turn bedroom light off # Turn bedroom light off
self.hass.services.call( yield from hass_hue.services.async_call(
light.DOMAIN, const.SERVICE_TURN_OFF, light.DOMAIN, const.SERVICE_TURN_OFF,
{ {
const.ATTR_ENTITY_ID: 'light.bed_light' const.ATTR_ENTITY_ID: 'light.bed_light'
}, },
blocking=True) blocking=True)
bedroom_json = self.perform_get_light_state('light.bed_light', 200) bedroom_json = yield from perform_get_light_state(
hue_client, 'light.bed_light', 200)
self.assertEqual(bedroom_json['state'][HUE_API_STATE_ON], False) assert bedroom_json['state'][HUE_API_STATE_ON] is False
self.assertEqual(bedroom_json['state'][HUE_API_STATE_BRI], 0) assert bedroom_json['state'][HUE_API_STATE_BRI] == 0
# Make sure kitchen light isn't accessible # Make sure kitchen light isn't accessible
kitchen_url = '/api/username/lights/{}'.format('light.kitchen_lights') yield from perform_get_light_state(
kitchen_result = requests.get( hue_client, 'light.kitchen_lights', 404)
BRIDGE_URL_BASE.format(kitchen_url), timeout=5)
self.assertEqual(kitchen_result.status_code, 404)
def test_put_light_state(self): @asyncio.coroutine
def test_put_light_state(hass_hue, hue_client):
"""Test the seeting of light states.""" """Test the seeting of light states."""
self.perform_put_test_on_ceiling_lights() yield from perform_put_test_on_ceiling_lights(hass_hue, hue_client)
# Turn the bedroom light on first # Turn the bedroom light on first
self.hass.services.call( yield from hass_hue.services.async_call(
light.DOMAIN, const.SERVICE_TURN_ON, light.DOMAIN, const.SERVICE_TURN_ON,
{const.ATTR_ENTITY_ID: 'light.bed_light', {const.ATTR_ENTITY_ID: 'light.bed_light',
light.ATTR_BRIGHTNESS: 153}, light.ATTR_BRIGHTNESS: 153},
blocking=True) blocking=True)
bed_light = self.hass.states.get('light.bed_light') bed_light = hass_hue.states.get('light.bed_light')
self.assertEqual(bed_light.state, STATE_ON) assert bed_light.state == STATE_ON
self.assertEqual(bed_light.attributes[light.ATTR_BRIGHTNESS], 153) assert bed_light.attributes[light.ATTR_BRIGHTNESS] == 153
# Go through the API to turn it off # Go through the API to turn it off
bedroom_result = self.perform_put_light_state( bedroom_result = yield from perform_put_light_state(
hass_hue, hue_client,
'light.bed_light', False) 'light.bed_light', False)
bedroom_result_json = bedroom_result.json() bedroom_result_json = yield from bedroom_result.json()
self.assertEqual(bedroom_result.status_code, 200) assert bedroom_result.status == 200
self.assertTrue( assert 'application/json' in bedroom_result.headers['content-type']
'application/json' in bedroom_result.headers['content-type'])
self.assertEqual(len(bedroom_result_json), 1) assert len(bedroom_result_json) == 1
# Check to make sure the state changed # Check to make sure the state changed
bed_light = self.hass.states.get('light.bed_light') bed_light = hass_hue.states.get('light.bed_light')
self.assertEqual(bed_light.state, STATE_OFF) assert bed_light.state == STATE_OFF
# Make sure we can't change the kitchen light state # Make sure we can't change the kitchen light state
kitchen_result = self.perform_put_light_state( kitchen_result = yield from perform_put_light_state(
hass_hue, hue_client,
'light.kitchen_light', True) 'light.kitchen_light', True)
self.assertEqual(kitchen_result.status_code, 404) assert kitchen_result.status == 404
def test_put_light_state_script(self):
@asyncio.coroutine
def test_put_light_state_script(hass_hue, hue_client):
"""Test the setting of script variables.""" """Test the setting of script variables."""
# Turn the kitchen light off first # Turn the kitchen light off first
self.hass.services.call( yield from hass_hue.services.async_call(
light.DOMAIN, const.SERVICE_TURN_OFF, light.DOMAIN, const.SERVICE_TURN_OFF,
{const.ATTR_ENTITY_ID: 'light.kitchen_lights'}, {const.ATTR_ENTITY_ID: 'light.kitchen_lights'},
blocking=True) blocking=True)
@ -225,24 +240,25 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase):
level = 23 level = 23
brightness = round(level * 255 / 100) brightness = round(level * 255 / 100)
script_result = self.perform_put_light_state( script_result = yield from perform_put_light_state(
hass_hue, hue_client,
'script.set_kitchen_light', True, brightness) 'script.set_kitchen_light', True, brightness)
script_result_json = script_result.json() script_result_json = yield from script_result.json()
self.assertEqual(script_result.status_code, 200) assert script_result.status == 200
self.assertEqual(len(script_result_json), 2) assert len(script_result_json) == 2
kitchen_light = self.hass.states.get('light.kitchen_lights') kitchen_light = hass_hue.states.get('light.kitchen_lights')
self.assertEqual(kitchen_light.state, 'on') assert kitchen_light.state == 'on'
self.assertEqual( assert kitchen_light.attributes[light.ATTR_BRIGHTNESS] == level
kitchen_light.attributes[light.ATTR_BRIGHTNESS],
level)
def test_put_light_state_media_player(self):
@asyncio.coroutine
def test_put_light_state_media_player(hass_hue, hue_client):
"""Test turning on media player and setting volume.""" """Test turning on media player and setting volume."""
# Turn the music player off first # Turn the music player off first
self.hass.services.call( yield from hass_hue.services.async_call(
media_player.DOMAIN, const.SERVICE_TURN_OFF, media_player.DOMAIN, const.SERVICE_TURN_OFF,
{const.ATTR_ENTITY_ID: 'media_player.walkman'}, {const.ATTR_ENTITY_ID: 'media_player.walkman'},
blocking=True) blocking=True)
@ -251,149 +267,147 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase):
level = 0.25 level = 0.25
brightness = round(level * 255) brightness = round(level * 255)
mp_result = self.perform_put_light_state( mp_result = yield from perform_put_light_state(
hass_hue, hue_client,
'media_player.walkman', True, brightness) 'media_player.walkman', True, brightness)
mp_result_json = mp_result.json() mp_result_json = yield from mp_result.json()
self.assertEqual(mp_result.status_code, 200) assert mp_result.status == 200
self.assertEqual(len(mp_result_json), 2) assert len(mp_result_json) == 2
walkman = hass_hue.states.get('media_player.walkman')
assert walkman.state == 'playing'
assert walkman.attributes[media_player.ATTR_MEDIA_VOLUME_LEVEL] == level
walkman = self.hass.states.get('media_player.walkman')
self.assertEqual(walkman.state, 'playing')
self.assertEqual(
walkman.attributes[media_player.ATTR_MEDIA_VOLUME_LEVEL],
level)
# pylint: disable=invalid-name # pylint: disable=invalid-name
def test_put_with_form_urlencoded_content_type(self): @asyncio.coroutine
def test_put_with_form_urlencoded_content_type(hass_hue, hue_client):
"""Test the form with urlencoded content.""" """Test the form with urlencoded content."""
# Needed for Alexa # Needed for Alexa
self.perform_put_test_on_ceiling_lights( yield from perform_put_test_on_ceiling_lights(
'application/x-www-form-urlencoded') hass_hue, hue_client, 'application/x-www-form-urlencoded')
# Make sure we fail gracefully when we can't parse the data # Make sure we fail gracefully when we can't parse the data
data = {'key1': 'value1', 'key2': 'value2'} data = {'key1': 'value1', 'key2': 'value2'}
result = requests.put( result = yield from hue_client.put(
BRIDGE_URL_BASE.format( '/api/username/lights/light.ceiling_lights/state',
'/api/username/lights/{}/state'.format( headers={
'light.ceiling_lights')), data=data) 'content-type': 'application/x-www-form-urlencoded'
},
data=data,
)
self.assertEqual(result.status_code, 400) assert result.status == 400
def test_entity_not_found(self):
@asyncio.coroutine
def test_entity_not_found(hue_client):
"""Test for entity which are not found.""" """Test for entity which are not found."""
result = requests.get( result = yield from hue_client.get(
BRIDGE_URL_BASE.format( '/api/username/lights/not.existant_entity')
'/api/username/lights/{}'.format("not.existant_entity")),
timeout=5)
self.assertEqual(result.status_code, 404) assert result.status == 404
result = requests.put( result = yield from hue_client.put(
BRIDGE_URL_BASE.format( '/api/username/lights/not.existant_entity/state')
'/api/username/lights/{}/state'.format("non.existant_entity")),
timeout=5)
self.assertEqual(result.status_code, 404) assert result.status == 404
def test_allowed_methods(self):
@asyncio.coroutine
def test_allowed_methods(hue_client):
"""Test the allowed methods.""" """Test the allowed methods."""
result = requests.get( result = yield from hue_client.get(
BRIDGE_URL_BASE.format( '/api/username/lights/light.ceiling_lights/state')
'/api/username/lights/{}/state'.format(
"light.ceiling_lights")))
self.assertEqual(result.status_code, 405) assert result.status == 405
result = requests.put( result = yield from hue_client.put(
BRIDGE_URL_BASE.format( '/api/username/lights/light.ceiling_lights')
'/api/username/lights/{}'.format("light.ceiling_lights")),
data={'key1': 'value1'})
self.assertEqual(result.status_code, 405) assert result.status == 405
result = requests.put( result = yield from hue_client.put(
BRIDGE_URL_BASE.format('/api/username/lights'), '/api/username/lights')
data={'key1': 'value1'})
self.assertEqual(result.status_code, 405) assert result.status == 405
def test_proper_put_state_request(self):
@asyncio.coroutine
def test_proper_put_state_request(hue_client):
"""Test the request to set the state.""" """Test the request to set the state."""
# Test proper on value parsing # Test proper on value parsing
result = requests.put( result = yield from hue_client.put(
BRIDGE_URL_BASE.format(
'/api/username/lights/{}/state'.format( '/api/username/lights/{}/state'.format(
'light.ceiling_lights')), 'light.ceiling_lights'),
data=json.dumps({HUE_API_STATE_ON: 1234})) data=json.dumps({HUE_API_STATE_ON: 1234}))
self.assertEqual(result.status_code, 400) assert result.status == 400
# Test proper brightness value parsing # Test proper brightness value parsing
result = requests.put( result = yield from hue_client.put(
BRIDGE_URL_BASE.format(
'/api/username/lights/{}/state'.format( '/api/username/lights/{}/state'.format(
'light.ceiling_lights')), data=json.dumps({ 'light.ceiling_lights'),
data=json.dumps({
HUE_API_STATE_ON: True, HUE_API_STATE_ON: True,
HUE_API_STATE_BRI: 'Hello world!' HUE_API_STATE_BRI: 'Hello world!'
})) }))
self.assertEqual(result.status_code, 400) assert result.status == 400
# pylint: disable=invalid-name # pylint: disable=invalid-name
def perform_put_test_on_ceiling_lights(self, def perform_put_test_on_ceiling_lights(hass_hue, hue_client,
content_type='application/json'): content_type='application/json'):
"""Test the setting of a light.""" """Test the setting of a light."""
# Turn the office light off first # Turn the office light off first
self.hass.services.call( yield from hass_hue.services.async_call(
light.DOMAIN, const.SERVICE_TURN_OFF, light.DOMAIN, const.SERVICE_TURN_OFF,
{const.ATTR_ENTITY_ID: 'light.ceiling_lights'}, {const.ATTR_ENTITY_ID: 'light.ceiling_lights'},
blocking=True) blocking=True)
ceiling_lights = self.hass.states.get('light.ceiling_lights') ceiling_lights = hass_hue.states.get('light.ceiling_lights')
self.assertEqual(ceiling_lights.state, STATE_OFF) assert ceiling_lights.state == STATE_OFF
# Go through the API to turn it on # Go through the API to turn it on
office_result = self.perform_put_light_state( office_result = yield from perform_put_light_state(
hass_hue, hue_client,
'light.ceiling_lights', True, 56, content_type) 'light.ceiling_lights', True, 56, content_type)
office_result_json = office_result.json() assert office_result.status == 200
assert 'application/json' in office_result.headers['content-type']
self.assertEqual(office_result.status_code, 200) office_result_json = yield from office_result.json()
self.assertTrue(
'application/json' in office_result.headers['content-type'])
self.assertEqual(len(office_result_json), 2) assert len(office_result_json) == 2
# Check to make sure the state changed # Check to make sure the state changed
ceiling_lights = self.hass.states.get('light.ceiling_lights') ceiling_lights = hass_hue.states.get('light.ceiling_lights')
self.assertEqual(ceiling_lights.state, STATE_ON) assert ceiling_lights.state == STATE_ON
self.assertEqual(ceiling_lights.attributes[light.ATTR_BRIGHTNESS], 56) assert ceiling_lights.attributes[light.ATTR_BRIGHTNESS] == 56
def perform_get_light_state(self, entity_id, expected_status):
@asyncio.coroutine
def perform_get_light_state(client, entity_id, expected_status):
"""Test the gettting of a light state.""" """Test the gettting of a light state."""
result = requests.get( result = yield from client.get('/api/username/lights/{}'.format(entity_id))
BRIDGE_URL_BASE.format(
'/api/username/lights/{}'.format(entity_id)), timeout=5)
self.assertEqual(result.status_code, expected_status) assert result.status == expected_status
if expected_status == 200: if expected_status == 200:
self.assertTrue( assert 'application/json' in result.headers['content-type']
'application/json' in result.headers['content-type'])
return result.json() return (yield from result.json())
return None return None
# 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))
@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} req_headers = {'Content-Type': content_type}
data = {HUE_API_STATE_ON: is_on} data = {HUE_API_STATE_ON: is_on}
@ -401,10 +415,11 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase):
if brightness is not None: if brightness is not None:
data[HUE_API_STATE_BRI] = brightness data[HUE_API_STATE_BRI] = brightness
result = requests.put( result = yield from client.put(
url, data=json.dumps(data), timeout=5, headers=req_headers) '/api/username/lights/{}/state'.format(entity_id), headers=req_headers,
data=json.dumps(data).encode())
# Wait until state change is complete before continuing # Wait until state change is complete before continuing
self.hass.block_till_done() yield from hass_hue.async_block_till_done()
return result return result