* 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
This commit is contained in:
Paulus Schoutsen 2017-05-19 07:37:39 -07:00 committed by GitHub
parent 5aa72562a7
commit d369d70ca5
6 changed files with 499 additions and 564 deletions

View file

@ -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 []

View file

@ -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)

View file

@ -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

View file

@ -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())

View file

@ -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'}

View file

@ -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