From 4f96eeb06e230775f6f1848a39d69cd67f962f24 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 21 Feb 2018 00:24:31 +0100 Subject: [PATCH] Cleanup hass.io component (#12556) * Cleanup hass.io component * fix lint * Fix all tests * Fix lint * fix lint * fix doc lint --- .../{hassio.py => hassio/__init__.py} | 227 +-------- homeassistant/components/hassio/handler.py | 117 +++++ homeassistant/components/hassio/http.py | 140 ++++++ tests/components/hassio/__init__.py | 3 + tests/components/hassio/conftest.py | 40 ++ tests/components/hassio/test_handler.py | 151 ++++++ tests/components/hassio/test_http.py | 133 +++++ tests/components/hassio/test_init.py | 174 +++++++ tests/components/test_hassio.py | 474 ------------------ 9 files changed, 761 insertions(+), 698 deletions(-) rename homeassistant/components/{hassio.py => hassio/__init__.py} (52%) create mode 100644 homeassistant/components/hassio/handler.py create mode 100644 homeassistant/components/hassio/http.py create mode 100644 tests/components/hassio/__init__.py create mode 100644 tests/components/hassio/conftest.py create mode 100644 tests/components/hassio/test_handler.py create mode 100644 tests/components/hassio/test_http.py create mode 100644 tests/components/hassio/test_init.py delete mode 100644 tests/components/test_hassio.py diff --git a/homeassistant/components/hassio.py b/homeassistant/components/hassio/__init__.py similarity index 52% rename from homeassistant/components/hassio.py rename to homeassistant/components/hassio/__init__.py index f8730f14a1a..9b229bd7a85 100644 --- a/homeassistant/components/hassio.py +++ b/homeassistant/components/hassio/__init__.py @@ -8,35 +8,25 @@ import asyncio from datetime import timedelta import logging import os -import re -import aiohttp -from aiohttp import web -from aiohttp.hdrs import CONTENT_TYPE -from aiohttp.web_exceptions import HTTPBadGateway -import async_timeout import voluptuous as vol from homeassistant.components import SERVICE_CHECK_CONFIG -from homeassistant.components.http import ( - CONF_API_PASSWORD, CONF_SERVER_HOST, CONF_SERVER_PORT, - CONF_SSL_CERTIFICATE, KEY_AUTHENTICATED, HomeAssistantView) from homeassistant.const import ( - CONF_TIME_ZONE, CONTENT_TYPE_TEXT_PLAIN, SERVER_PORT, SERVICE_HOMEASSISTANT_RESTART, SERVICE_HOMEASSISTANT_STOP) from homeassistant.core import DOMAIN as HASS_DOMAIN from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.loader import bind_hass from homeassistant.util.dt import utcnow +from .handler import HassIO +from .http import HassIOView _LOGGER = logging.getLogger(__name__) DOMAIN = 'hassio' DEPENDENCIES = ['http'] -X_HASSIO = 'X-HASSIO-KEY' - DATA_HOMEASSISTANT_VERSION = 'hassio_hass_version' HASSIO_UPDATE_INTERVAL = timedelta(minutes=55) @@ -60,22 +50,6 @@ ATTR_HOMEASSISTANT = 'homeassistant' ATTR_NAME = 'name' ATTR_PASSWORD = 'password' -NO_TIMEOUT = { - re.compile(r'^homeassistant/update$'), - re.compile(r'^host/update$'), - re.compile(r'^supervisor/update$'), - re.compile(r'^addons/[^/]*/update$'), - re.compile(r'^addons/[^/]*/install$'), - re.compile(r'^addons/[^/]*/rebuild$'), - re.compile(r'^snapshots/.*/full$'), - re.compile(r'^snapshots/.*/partial$'), -} - -NO_AUTH = { - re.compile(r'^app-(es5|latest)/(index|hassio-app).html$'), - re.compile(r'^addons/[^/]*/logo$') -} - SCHEMA_NO_DATA = vol.Schema({}) SCHEMA_ADDON = vol.Schema({ @@ -178,7 +152,7 @@ def async_setup(hass, config): _LOGGER.error("Not connected with Hass.io") return False - hass.http.register_view(HassIOView(hassio)) + hass.http.register_view(HassIOView(host, websession)) if 'frontend' in hass.config.components: yield from hass.components.frontend.async_register_built_in_panel( @@ -257,198 +231,3 @@ def async_setup(hass, config): HASS_DOMAIN, service, async_handle_core_service) return True - - -def _api_bool(funct): - """Return a boolean.""" - @asyncio.coroutine - def _wrapper(*argv, **kwargs): - """Wrap function.""" - data = yield from funct(*argv, **kwargs) - return data and data['result'] == "ok" - - return _wrapper - - -class HassIO(object): - """Small API wrapper for Hass.io.""" - - def __init__(self, loop, websession, ip): - """Initialize Hass.io API.""" - self.loop = loop - self.websession = websession - self._ip = ip - - @_api_bool - def is_connected(self): - """Return true if it connected to Hass.io supervisor. - - This method return a coroutine. - """ - return self.send_command("/supervisor/ping", method="get") - - def get_homeassistant_info(self): - """Return data for Home Assistant. - - This method return a coroutine. - """ - return self.send_command("/homeassistant/info", method="get") - - @_api_bool - def update_hass_api(self, http_config): - """Update Home Assistant API data on Hass.io. - - This method return a coroutine. - """ - port = http_config.get(CONF_SERVER_PORT) or SERVER_PORT - options = { - 'ssl': CONF_SSL_CERTIFICATE in http_config, - 'port': port, - 'password': http_config.get(CONF_API_PASSWORD), - 'watchdog': True, - } - - if CONF_SERVER_HOST in http_config: - options['watchdog'] = False - _LOGGER.warning("Don't use 'server_host' options with Hass.io") - - return self.send_command("/homeassistant/options", payload=options) - - @_api_bool - def update_hass_timezone(self, core_config): - """Update Home-Assistant timezone data on Hass.io. - - This method return a coroutine. - """ - return self.send_command("/supervisor/options", payload={ - 'timezone': core_config.get(CONF_TIME_ZONE) - }) - - @asyncio.coroutine - def send_command(self, command, method="post", payload=None, timeout=10): - """Send API command to Hass.io. - - This method is a coroutine. - """ - try: - with async_timeout.timeout(timeout, loop=self.loop): - request = yield from self.websession.request( - method, "http://{}{}".format(self._ip, command), - json=payload, headers={ - X_HASSIO: os.environ.get('HASSIO_TOKEN', "") - }) - - if request.status not in (200, 400): - _LOGGER.error( - "%s return code %d.", command, request.status) - return None - - answer = yield from request.json() - return answer - - except asyncio.TimeoutError: - _LOGGER.error("Timeout on %s request", command) - - except aiohttp.ClientError as err: - _LOGGER.error("Client error on %s request %s", command, err) - - return None - - @asyncio.coroutine - def command_proxy(self, path, request): - """Return a client request with proxy origin for Hass.io supervisor. - - This method is a coroutine. - """ - read_timeout = _get_timeout(path) - - try: - data = None - headers = {X_HASSIO: os.environ.get('HASSIO_TOKEN', "")} - with async_timeout.timeout(10, loop=self.loop): - data = yield from request.read() - if data: - headers[CONTENT_TYPE] = request.content_type - else: - data = None - - method = getattr(self.websession, request.method.lower()) - client = yield from method( - "http://{}/{}".format(self._ip, path), data=data, - headers=headers, timeout=read_timeout - ) - - return client - - except aiohttp.ClientError as err: - _LOGGER.error("Client error on api %s request %s", path, err) - - except asyncio.TimeoutError: - _LOGGER.error("Client timeout error on API request %s", path) - - raise HTTPBadGateway() - - -class HassIOView(HomeAssistantView): - """Hass.io view to handle base part.""" - - name = "api:hassio" - url = "/api/hassio/{path:.+}" - requires_auth = False - - def __init__(self, hassio): - """Initialize a Hass.io base view.""" - self.hassio = hassio - - @asyncio.coroutine - def _handle(self, request, path): - """Route data to Hass.io.""" - if _need_auth(path) and not request[KEY_AUTHENTICATED]: - return web.Response(status=401) - - client = yield from self.hassio.command_proxy(path, request) - - data = yield from client.read() - if path.endswith('/logs'): - return _create_response_log(client, data) - return _create_response(client, data) - - get = _handle - post = _handle - - -def _create_response(client, data): - """Convert a response from client request.""" - return web.Response( - body=data, - status=client.status, - content_type=client.content_type, - ) - - -def _create_response_log(client, data): - """Convert a response from client request.""" - # Remove color codes - log = re.sub(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))", "", data.decode()) - - return web.Response( - text=log, - status=client.status, - content_type=CONTENT_TYPE_TEXT_PLAIN, - ) - - -def _get_timeout(path): - """Return timeout for a URL path.""" - for re_path in NO_TIMEOUT: - if re_path.match(path): - return 0 - return 300 - - -def _need_auth(path): - """Return if a path need authentication.""" - for re_path in NO_AUTH: - if re_path.match(path): - return False - return True diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py new file mode 100644 index 00000000000..125622f063c --- /dev/null +++ b/homeassistant/components/hassio/handler.py @@ -0,0 +1,117 @@ +""" +Exposes regular REST commands as services. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/hassio/ +""" +import asyncio +import logging +import os + +import aiohttp +import async_timeout + +from homeassistant.components.http import ( + CONF_API_PASSWORD, CONF_SERVER_HOST, CONF_SERVER_PORT, + CONF_SSL_CERTIFICATE) +from homeassistant.const import CONF_TIME_ZONE, SERVER_PORT + +_LOGGER = logging.getLogger(__name__) + +X_HASSIO = 'X-HASSIO-KEY' + + +def _api_bool(funct): + """Return a boolean.""" + @asyncio.coroutine + def _wrapper(*argv, **kwargs): + """Wrap function.""" + data = yield from funct(*argv, **kwargs) + return data and data['result'] == "ok" + + return _wrapper + + +class HassIO(object): + """Small API wrapper for Hass.io.""" + + def __init__(self, loop, websession, ip): + """Initialize Hass.io API.""" + self.loop = loop + self.websession = websession + self._ip = ip + + @_api_bool + def is_connected(self): + """Return true if it connected to Hass.io supervisor. + + This method return a coroutine. + """ + return self.send_command("/supervisor/ping", method="get") + + def get_homeassistant_info(self): + """Return data for Home Assistant. + + This method return a coroutine. + """ + return self.send_command("/homeassistant/info", method="get") + + @_api_bool + def update_hass_api(self, http_config): + """Update Home Assistant API data on Hass.io. + + This method return a coroutine. + """ + port = http_config.get(CONF_SERVER_PORT) or SERVER_PORT + options = { + 'ssl': CONF_SSL_CERTIFICATE in http_config, + 'port': port, + 'password': http_config.get(CONF_API_PASSWORD), + 'watchdog': True, + } + + if CONF_SERVER_HOST in http_config: + options['watchdog'] = False + _LOGGER.warning("Don't use 'server_host' options with Hass.io") + + return self.send_command("/homeassistant/options", payload=options) + + @_api_bool + def update_hass_timezone(self, core_config): + """Update Home-Assistant timezone data on Hass.io. + + This method return a coroutine. + """ + return self.send_command("/supervisor/options", payload={ + 'timezone': core_config.get(CONF_TIME_ZONE) + }) + + @asyncio.coroutine + def send_command(self, command, method="post", payload=None, timeout=10): + """Send API command to Hass.io. + + This method is a coroutine. + """ + try: + with async_timeout.timeout(timeout, loop=self.loop): + request = yield from self.websession.request( + method, "http://{}{}".format(self._ip, command), + json=payload, headers={ + X_HASSIO: os.environ.get('HASSIO_TOKEN', "") + }) + + if request.status not in (200, 400): + _LOGGER.error( + "%s return code %d.", command, request.status) + return None + + answer = yield from request.json() + return answer + + except asyncio.TimeoutError: + _LOGGER.error("Timeout on %s request", command) + + except aiohttp.ClientError as err: + _LOGGER.error("Client error on %s request %s", command, err) + + return None diff --git a/homeassistant/components/hassio/http.py b/homeassistant/components/hassio/http.py new file mode 100644 index 00000000000..d94826653e8 --- /dev/null +++ b/homeassistant/components/hassio/http.py @@ -0,0 +1,140 @@ +""" +Exposes regular REST commands as services. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/hassio/ +""" +import asyncio +import logging +import os +import re + +import async_timeout +import aiohttp +from aiohttp import web +from aiohttp.hdrs import CONTENT_TYPE +from aiohttp.web_exceptions import HTTPBadGateway + +from homeassistant.const import CONTENT_TYPE_TEXT_PLAIN +from homeassistant.components.http import KEY_AUTHENTICATED, HomeAssistantView + +_LOGGER = logging.getLogger(__name__) + +X_HASSIO = 'X-HASSIO-KEY' + +NO_TIMEOUT = { + re.compile(r'^homeassistant/update$'), + re.compile(r'^host/update$'), + re.compile(r'^supervisor/update$'), + re.compile(r'^addons/[^/]*/update$'), + re.compile(r'^addons/[^/]*/install$'), + re.compile(r'^addons/[^/]*/rebuild$'), + re.compile(r'^snapshots/.*/full$'), + re.compile(r'^snapshots/.*/partial$'), +} + +NO_AUTH = { + re.compile(r'^app-(es5|latest)/(index|hassio-app).html$'), + re.compile(r'^addons/[^/]*/logo$') +} + + +class HassIOView(HomeAssistantView): + """Hass.io view to handle base part.""" + + name = "api:hassio" + url = "/api/hassio/{path:.+}" + requires_auth = False + + def __init__(self, host, websession): + """Initialize a Hass.io base view.""" + self._host = host + self._websession = websession + + @asyncio.coroutine + def _handle(self, request, path): + """Route data to Hass.io.""" + if _need_auth(path) and not request[KEY_AUTHENTICATED]: + return web.Response(status=401) + + client = yield from self._command_proxy(path, request) + + data = yield from client.read() + if path.endswith('/logs'): + return _create_response_log(client, data) + return _create_response(client, data) + + get = _handle + post = _handle + + @asyncio.coroutine + def _command_proxy(self, path, request): + """Return a client request with proxy origin for Hass.io supervisor. + + This method is a coroutine. + """ + read_timeout = _get_timeout(path) + hass = request.app['hass'] + + try: + data = None + headers = {X_HASSIO: os.environ.get('HASSIO_TOKEN', "")} + with async_timeout.timeout(10, loop=hass.loop): + data = yield from request.read() + if data: + headers[CONTENT_TYPE] = request.content_type + else: + data = None + + method = getattr(self._websession, request.method.lower()) + client = yield from method( + "http://{}/{}".format(self._host, path), data=data, + headers=headers, timeout=read_timeout + ) + + return client + + except aiohttp.ClientError as err: + _LOGGER.error("Client error on api %s request %s", path, err) + + except asyncio.TimeoutError: + _LOGGER.error("Client timeout error on API request %s", path) + + raise HTTPBadGateway() + + +def _create_response(client, data): + """Convert a response from client request.""" + return web.Response( + body=data, + status=client.status, + content_type=client.content_type, + ) + + +def _create_response_log(client, data): + """Convert a response from client request.""" + # Remove color codes + log = re.sub(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))", "", data.decode()) + + return web.Response( + text=log, + status=client.status, + content_type=CONTENT_TYPE_TEXT_PLAIN, + ) + + +def _get_timeout(path): + """Return timeout for a URL path.""" + for re_path in NO_TIMEOUT: + if re_path.match(path): + return 0 + return 300 + + +def _need_auth(path): + """Return if a path need authentication.""" + for re_path in NO_AUTH: + if re_path.match(path): + return False + return True diff --git a/tests/components/hassio/__init__.py b/tests/components/hassio/__init__.py new file mode 100644 index 00000000000..34fd4ad23e5 --- /dev/null +++ b/tests/components/hassio/__init__.py @@ -0,0 +1,3 @@ +"""Tests for Hassio component.""" + +API_PASSWORD = 'pass1234' diff --git a/tests/components/hassio/conftest.py b/tests/components/hassio/conftest.py new file mode 100644 index 00000000000..852ec1aaa15 --- /dev/null +++ b/tests/components/hassio/conftest.py @@ -0,0 +1,40 @@ +"""Fixtures for Hass.io.""" +import os +from unittest.mock import patch, Mock + +import pytest + +from homeassistant.setup import async_setup_component + +from tests.common import mock_coro +from . import API_PASSWORD + + +@pytest.fixture +def hassio_env(): + """Fixture to inject hassio env.""" + with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}), \ + patch('homeassistant.components.hassio.HassIO.is_connected', + Mock(return_value=mock_coro( + {"result": "ok", "data": {}}))), \ + patch.dict(os.environ, {'HASSIO_TOKEN': "123456"}), \ + patch('homeassistant.components.hassio.HassIO.' + 'get_homeassistant_info', + Mock(return_value=mock_coro(None))): + yield + + +@pytest.fixture +def hassio_client(hassio_env, hass, test_client): + """Create mock hassio http client.""" + with patch('homeassistant.components.hassio.HassIO.update_hass_api', + Mock(return_value=mock_coro({"result": "ok"}))), \ + patch('homeassistant.components.hassio.HassIO.' + 'get_homeassistant_info', + Mock(return_value=mock_coro(None))): + hass.loop.run_until_complete(async_setup_component(hass, 'hassio', { + 'http': { + 'api_password': API_PASSWORD + } + })) + yield hass.loop.run_until_complete(test_client(hass.http.app)) diff --git a/tests/components/hassio/test_handler.py b/tests/components/hassio/test_handler.py new file mode 100644 index 00000000000..39cfa689c59 --- /dev/null +++ b/tests/components/hassio/test_handler.py @@ -0,0 +1,151 @@ +"""The tests for the hassio component.""" +import asyncio +import os +from unittest.mock import patch + +from homeassistant.setup import async_setup_component + + +@asyncio.coroutine +def test_setup_api_ping(hass, aioclient_mock): + """Test setup with API ping.""" + aioclient_mock.get( + "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) + aioclient_mock.get( + "http://127.0.0.1/homeassistant/info", json={ + 'result': 'ok', 'data': {'last_version': '10.0'}}) + + with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}): + result = yield from async_setup_component(hass, 'hassio', {}) + assert result + + assert aioclient_mock.call_count == 2 + assert hass.components.hassio.get_homeassistant_version() == "10.0" + assert hass.components.hassio.is_hassio() + + +@asyncio.coroutine +def test_setup_api_push_api_data(hass, aioclient_mock): + """Test setup with API push.""" + aioclient_mock.get( + "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) + aioclient_mock.get( + "http://127.0.0.1/homeassistant/info", json={ + 'result': 'ok', 'data': {'last_version': '10.0'}}) + aioclient_mock.post( + "http://127.0.0.1/homeassistant/options", json={'result': 'ok'}) + + with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}): + result = yield from async_setup_component(hass, 'hassio', { + 'http': { + 'api_password': "123456", + 'server_port': 9999 + }, + 'hassio': {} + }) + assert result + + assert aioclient_mock.call_count == 3 + assert not aioclient_mock.mock_calls[1][2]['ssl'] + assert aioclient_mock.mock_calls[1][2]['password'] == "123456" + assert aioclient_mock.mock_calls[1][2]['port'] == 9999 + assert aioclient_mock.mock_calls[1][2]['watchdog'] + + +@asyncio.coroutine +def test_setup_api_push_api_data_server_host(hass, aioclient_mock): + """Test setup with API push with active server host.""" + aioclient_mock.get( + "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) + aioclient_mock.get( + "http://127.0.0.1/homeassistant/info", json={ + 'result': 'ok', 'data': {'last_version': '10.0'}}) + aioclient_mock.post( + "http://127.0.0.1/homeassistant/options", json={'result': 'ok'}) + + with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}): + result = yield from async_setup_component(hass, 'hassio', { + 'http': { + 'api_password': "123456", + 'server_port': 9999, + 'server_host': "127.0.0.1" + }, + 'hassio': {} + }) + assert result + + assert aioclient_mock.call_count == 3 + assert not aioclient_mock.mock_calls[1][2]['ssl'] + assert aioclient_mock.mock_calls[1][2]['password'] == "123456" + assert aioclient_mock.mock_calls[1][2]['port'] == 9999 + assert not aioclient_mock.mock_calls[1][2]['watchdog'] + + +@asyncio.coroutine +def test_setup_api_push_api_data_default(hass, aioclient_mock): + """Test setup with API push default data.""" + aioclient_mock.get( + "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) + aioclient_mock.get( + "http://127.0.0.1/homeassistant/info", json={ + 'result': 'ok', 'data': {'last_version': '10.0'}}) + aioclient_mock.post( + "http://127.0.0.1/homeassistant/options", json={'result': 'ok'}) + + with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}): + result = yield from async_setup_component(hass, 'hassio', { + 'http': {}, + 'hassio': {} + }) + assert result + + assert aioclient_mock.call_count == 3 + assert not aioclient_mock.mock_calls[1][2]['ssl'] + assert aioclient_mock.mock_calls[1][2]['password'] is None + assert aioclient_mock.mock_calls[1][2]['port'] == 8123 + + +@asyncio.coroutine +def test_setup_core_push_timezone(hass, aioclient_mock): + """Test setup with API push default data.""" + aioclient_mock.get( + "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) + aioclient_mock.get( + "http://127.0.0.1/homeassistant/info", json={ + 'result': 'ok', 'data': {'last_version': '10.0'}}) + aioclient_mock.post( + "http://127.0.0.1/supervisor/options", json={'result': 'ok'}) + + with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}): + result = yield from async_setup_component(hass, 'hassio', { + 'hassio': {}, + 'homeassistant': { + 'time_zone': 'testzone', + }, + }) + assert result + + assert aioclient_mock.call_count == 3 + assert aioclient_mock.mock_calls[1][2]['timezone'] == "testzone" + + +@asyncio.coroutine +def test_setup_hassio_no_additional_data(hass, aioclient_mock): + """Test setup with API push default data.""" + aioclient_mock.get( + "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) + aioclient_mock.get( + "http://127.0.0.1/homeassistant/info", json={ + 'result': 'ok', 'data': {'last_version': '10.0'}}) + aioclient_mock.get( + "http://127.0.0.1/homeassistant/info", json={'result': 'ok'}) + + with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}), \ + patch.dict(os.environ, {'HASSIO_TOKEN': "123456"}): + result = yield from async_setup_component(hass, 'hassio', { + 'hassio': {}, + }) + assert result + + assert aioclient_mock.call_count == 2 + assert aioclient_mock.mock_calls[-1][3]['X-HASSIO-KEY'] == "123456" diff --git a/tests/components/hassio/test_http.py b/tests/components/hassio/test_http.py new file mode 100644 index 00000000000..ed425ad8cca --- /dev/null +++ b/tests/components/hassio/test_http.py @@ -0,0 +1,133 @@ +"""The tests for the hassio component.""" +import asyncio +from unittest.mock import patch, Mock, MagicMock + +import pytest + +from homeassistant.const import HTTP_HEADER_HA_AUTH + +from tests.common import mock_coro +from . import API_PASSWORD + + +@asyncio.coroutine +def test_forward_request(hassio_client): + """Test fetching normal path.""" + response = MagicMock() + response.read.return_value = mock_coro('data') + + with patch('homeassistant.components.hassio.HassIOView._command_proxy', + Mock(return_value=mock_coro(response))), \ + patch('homeassistant.components.hassio.http' + '._create_response') as mresp: + mresp.return_value = 'response' + resp = yield from hassio_client.post('/api/hassio/beer', headers={ + HTTP_HEADER_HA_AUTH: API_PASSWORD + }) + + # Check we got right response + assert resp.status == 200 + body = yield from resp.text() + assert body == 'response' + + # Check we forwarded command + assert len(mresp.mock_calls) == 1 + assert mresp.mock_calls[0][1] == (response, 'data') + + +@asyncio.coroutine +def test_auth_required_forward_request(hassio_client): + """Test auth required for normal request.""" + resp = yield from hassio_client.post('/api/hassio/beer') + + # Check we got right response + assert resp.status == 401 + + +@asyncio.coroutine +@pytest.mark.parametrize( + 'build_type', [ + 'es5/index.html', 'es5/hassio-app.html', 'latest/index.html', + 'latest/hassio-app.html' + ]) +def test_forward_request_no_auth_for_panel(hassio_client, build_type): + """Test no auth needed for .""" + response = MagicMock() + response.read.return_value = mock_coro('data') + + with patch('homeassistant.components.hassio.HassIOView._command_proxy', + Mock(return_value=mock_coro(response))), \ + patch('homeassistant.components.hassio.http.' + '_create_response') as mresp: + mresp.return_value = 'response' + resp = yield from hassio_client.get( + '/api/hassio/app-{}'.format(build_type)) + + # Check we got right response + assert resp.status == 200 + body = yield from resp.text() + assert body == 'response' + + # Check we forwarded command + assert len(mresp.mock_calls) == 1 + assert mresp.mock_calls[0][1] == (response, 'data') + + +@asyncio.coroutine +def test_forward_request_no_auth_for_logo(hassio_client): + """Test no auth needed for .""" + response = MagicMock() + response.read.return_value = mock_coro('data') + + with patch('homeassistant.components.hassio.HassIOView._command_proxy', + Mock(return_value=mock_coro(response))), \ + patch('homeassistant.components.hassio.http.' + '_create_response') as mresp: + mresp.return_value = 'response' + resp = yield from hassio_client.get('/api/hassio/addons/bl_b392/logo') + + # Check we got right response + assert resp.status == 200 + body = yield from resp.text() + assert body == 'response' + + # Check we forwarded command + assert len(mresp.mock_calls) == 1 + assert mresp.mock_calls[0][1] == (response, 'data') + + +@asyncio.coroutine +def test_forward_log_request(hassio_client): + """Test fetching normal log path.""" + response = MagicMock() + response.read.return_value = mock_coro('data') + + with patch('homeassistant.components.hassio.HassIOView._command_proxy', + Mock(return_value=mock_coro(response))), \ + patch('homeassistant.components.hassio.http.' + '_create_response_log') as mresp: + mresp.return_value = 'response' + resp = yield from hassio_client.get('/api/hassio/beer/logs', headers={ + HTTP_HEADER_HA_AUTH: API_PASSWORD + }) + + # Check we got right response + assert resp.status == 200 + body = yield from resp.text() + assert body == 'response' + + # Check we forwarded command + assert len(mresp.mock_calls) == 1 + assert mresp.mock_calls[0][1] == (response, 'data') + + +@asyncio.coroutine +def test_bad_gateway_when_cannot_find_supervisor(hassio_client): + """Test we get a bad gateway error if we can't find supervisor.""" + with patch('homeassistant.components.hassio.http.async_timeout.timeout', + side_effect=asyncio.TimeoutError): + resp = yield from hassio_client.get( + '/api/hassio/addons/test/info', headers={ + HTTP_HEADER_HA_AUTH: API_PASSWORD + }) + assert resp.status == 502 diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py new file mode 100644 index 00000000000..313623bc40d --- /dev/null +++ b/tests/components/hassio/test_init.py @@ -0,0 +1,174 @@ +"""The tests for the hassio component.""" +import asyncio +import os +from unittest.mock import patch, Mock + +from homeassistant.setup import async_setup_component +from homeassistant.components.hassio import async_check_config + +from tests.common import mock_coro + + +@asyncio.coroutine +def test_fail_setup_without_environ_var(hass): + """Fail setup if no environ variable set.""" + with patch.dict(os.environ, {}, clear=True): + result = yield from async_setup_component(hass, 'hassio', {}) + assert not result + + +@asyncio.coroutine +def test_fail_setup_cannot_connect(hass): + """Fail setup if cannot connect.""" + with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}), \ + patch('homeassistant.components.hassio.HassIO.is_connected', + Mock(return_value=mock_coro(None))): + result = yield from async_setup_component(hass, 'hassio', {}) + assert not result + + assert not hass.components.hassio.is_hassio() + + +@asyncio.coroutine +def test_service_register(hassio_env, hass): + """Check if service will be setup.""" + assert (yield from async_setup_component(hass, 'hassio', {})) + assert hass.services.has_service('hassio', 'addon_start') + assert hass.services.has_service('hassio', 'addon_stop') + assert hass.services.has_service('hassio', 'addon_restart') + assert hass.services.has_service('hassio', 'addon_stdin') + assert hass.services.has_service('hassio', 'host_shutdown') + assert hass.services.has_service('hassio', 'host_reboot') + assert hass.services.has_service('hassio', 'host_reboot') + assert hass.services.has_service('hassio', 'snapshot_full') + assert hass.services.has_service('hassio', 'snapshot_partial') + assert hass.services.has_service('hassio', 'restore_full') + assert hass.services.has_service('hassio', 'restore_partial') + + +@asyncio.coroutine +def test_service_calls(hassio_env, hass, aioclient_mock): + """Call service and check the API calls behind that.""" + assert (yield from async_setup_component(hass, 'hassio', {})) + + aioclient_mock.post( + "http://127.0.0.1/addons/test/start", json={'result': 'ok'}) + aioclient_mock.post( + "http://127.0.0.1/addons/test/stop", json={'result': 'ok'}) + aioclient_mock.post( + "http://127.0.0.1/addons/test/restart", json={'result': 'ok'}) + aioclient_mock.post( + "http://127.0.0.1/addons/test/stdin", json={'result': 'ok'}) + aioclient_mock.post( + "http://127.0.0.1/host/shutdown", json={'result': 'ok'}) + aioclient_mock.post( + "http://127.0.0.1/host/reboot", json={'result': 'ok'}) + aioclient_mock.post( + "http://127.0.0.1/snapshots/new/full", json={'result': 'ok'}) + aioclient_mock.post( + "http://127.0.0.1/snapshots/new/partial", json={'result': 'ok'}) + aioclient_mock.post( + "http://127.0.0.1/snapshots/test/restore/full", json={'result': 'ok'}) + aioclient_mock.post( + "http://127.0.0.1/snapshots/test/restore/partial", + json={'result': 'ok'}) + + yield from hass.services.async_call( + 'hassio', 'addon_start', {'addon': 'test'}) + yield from hass.services.async_call( + 'hassio', 'addon_stop', {'addon': 'test'}) + yield from hass.services.async_call( + 'hassio', 'addon_restart', {'addon': 'test'}) + yield from hass.services.async_call( + 'hassio', 'addon_stdin', {'addon': 'test', 'input': 'test'}) + yield from hass.async_block_till_done() + + assert aioclient_mock.call_count == 4 + assert aioclient_mock.mock_calls[-1][2] == 'test' + + yield from hass.services.async_call('hassio', 'host_shutdown', {}) + yield from hass.services.async_call('hassio', 'host_reboot', {}) + yield from hass.async_block_till_done() + + assert aioclient_mock.call_count == 6 + + yield from hass.services.async_call('hassio', 'snapshot_full', {}) + yield from hass.services.async_call('hassio', 'snapshot_partial', { + 'addons': ['test'], + 'folders': ['ssl'], + 'password': "123456", + }) + yield from hass.async_block_till_done() + + assert aioclient_mock.call_count == 8 + assert aioclient_mock.mock_calls[-1][2] == { + 'addons': ['test'], 'folders': ['ssl'], 'password': "123456"} + + yield from hass.services.async_call('hassio', 'restore_full', { + 'snapshot': 'test', + }) + yield from hass.services.async_call('hassio', 'restore_partial', { + 'snapshot': 'test', + 'homeassistant': False, + 'addons': ['test'], + 'folders': ['ssl'], + 'password': "123456", + }) + yield from hass.async_block_till_done() + + assert aioclient_mock.call_count == 10 + assert aioclient_mock.mock_calls[-1][2] == { + 'addons': ['test'], 'folders': ['ssl'], 'homeassistant': False, + 'password': "123456" + } + + +@asyncio.coroutine +def test_service_calls_core(hassio_env, hass, aioclient_mock): + """Call core service and check the API calls behind that.""" + assert (yield from async_setup_component(hass, 'hassio', {})) + + aioclient_mock.post( + "http://127.0.0.1/homeassistant/restart", json={'result': 'ok'}) + aioclient_mock.post( + "http://127.0.0.1/homeassistant/stop", json={'result': 'ok'}) + aioclient_mock.post( + "http://127.0.0.1/homeassistant/check", json={'result': 'ok'}) + + yield from hass.services.async_call('homeassistant', 'stop') + yield from hass.async_block_till_done() + + assert aioclient_mock.call_count == 1 + + yield from hass.services.async_call('homeassistant', 'check_config') + yield from hass.async_block_till_done() + + assert aioclient_mock.call_count == 2 + + yield from hass.services.async_call('homeassistant', 'restart') + yield from hass.async_block_till_done() + + assert aioclient_mock.call_count == 4 + + +@asyncio.coroutine +def test_check_config_ok(hassio_env, hass, aioclient_mock): + """Check Config that is okay.""" + assert (yield from async_setup_component(hass, 'hassio', {})) + + aioclient_mock.post( + "http://127.0.0.1/homeassistant/check", json={'result': 'ok'}) + + assert (yield from async_check_config(hass)) is None + + +@asyncio.coroutine +def test_check_config_fail(hassio_env, hass, aioclient_mock): + """Check Config that is wrong.""" + assert (yield from async_setup_component(hass, 'hassio', {})) + + aioclient_mock.post( + "http://127.0.0.1/homeassistant/check", json={ + 'result': 'error', 'message': "Error"}) + + assert (yield from async_check_config(hass)) == "Error" diff --git a/tests/components/test_hassio.py b/tests/components/test_hassio.py deleted file mode 100644 index 4511930a6df..00000000000 --- a/tests/components/test_hassio.py +++ /dev/null @@ -1,474 +0,0 @@ -"""The tests for the hassio component.""" -import asyncio -import os -from unittest.mock import patch, Mock, MagicMock - -import pytest - -from homeassistant.const import HTTP_HEADER_HA_AUTH -from homeassistant.setup import async_setup_component -from homeassistant.components.hassio import async_check_config - -from tests.common import mock_coro - -API_PASSWORD = 'pass1234' - - -@pytest.fixture -def hassio_env(): - """Fixture to inject hassio env.""" - with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}), \ - patch('homeassistant.components.hassio.HassIO.is_connected', - Mock(return_value=mock_coro( - {"result": "ok", "data": {}}))), \ - patch.dict(os.environ, {'HASSIO_TOKEN': "123456"}), \ - patch('homeassistant.components.hassio.HassIO.' - 'get_homeassistant_info', - Mock(return_value=mock_coro(None))): - yield - - -@pytest.fixture -def hassio_client(hassio_env, hass, test_client): - """Create mock hassio http client.""" - with patch('homeassistant.components.hassio.HassIO.update_hass_api', - Mock(return_value=mock_coro({"result": "ok"}))), \ - patch('homeassistant.components.hassio.HassIO.' - 'get_homeassistant_info', - Mock(return_value=mock_coro(None))): - hass.loop.run_until_complete(async_setup_component(hass, 'hassio', { - 'http': { - 'api_password': API_PASSWORD - } - })) - yield hass.loop.run_until_complete(test_client(hass.http.app)) - - -@asyncio.coroutine -def test_fail_setup_without_environ_var(hass): - """Fail setup if no environ variable set.""" - with patch.dict(os.environ, {}, clear=True): - result = yield from async_setup_component(hass, 'hassio', {}) - assert not result - - -@asyncio.coroutine -def test_fail_setup_cannot_connect(hass): - """Fail setup if cannot connect.""" - with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}), \ - patch('homeassistant.components.hassio.HassIO.is_connected', - Mock(return_value=mock_coro(None))): - result = yield from async_setup_component(hass, 'hassio', {}) - assert not result - - assert not hass.components.hassio.is_hassio() - - -@asyncio.coroutine -def test_setup_api_ping(hass, aioclient_mock): - """Test setup with API ping.""" - aioclient_mock.get( - "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) - aioclient_mock.get( - "http://127.0.0.1/homeassistant/info", json={ - 'result': 'ok', 'data': {'last_version': '10.0'}}) - - with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}): - result = yield from async_setup_component(hass, 'hassio', {}) - assert result - - assert aioclient_mock.call_count == 2 - assert hass.components.hassio.get_homeassistant_version() == "10.0" - assert hass.components.hassio.is_hassio() - - -@asyncio.coroutine -def test_setup_api_push_api_data(hass, aioclient_mock): - """Test setup with API push.""" - aioclient_mock.get( - "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) - aioclient_mock.get( - "http://127.0.0.1/homeassistant/info", json={ - 'result': 'ok', 'data': {'last_version': '10.0'}}) - aioclient_mock.post( - "http://127.0.0.1/homeassistant/options", json={'result': 'ok'}) - - with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}): - result = yield from async_setup_component(hass, 'hassio', { - 'http': { - 'api_password': "123456", - 'server_port': 9999 - }, - 'hassio': {} - }) - assert result - - assert aioclient_mock.call_count == 3 - assert not aioclient_mock.mock_calls[1][2]['ssl'] - assert aioclient_mock.mock_calls[1][2]['password'] == "123456" - assert aioclient_mock.mock_calls[1][2]['port'] == 9999 - assert aioclient_mock.mock_calls[1][2]['watchdog'] - - -@asyncio.coroutine -def test_setup_api_push_api_data_server_host(hass, aioclient_mock): - """Test setup with API push with active server host.""" - aioclient_mock.get( - "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) - aioclient_mock.get( - "http://127.0.0.1/homeassistant/info", json={ - 'result': 'ok', 'data': {'last_version': '10.0'}}) - aioclient_mock.post( - "http://127.0.0.1/homeassistant/options", json={'result': 'ok'}) - - with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}): - result = yield from async_setup_component(hass, 'hassio', { - 'http': { - 'api_password': "123456", - 'server_port': 9999, - 'server_host': "127.0.0.1" - }, - 'hassio': {} - }) - assert result - - assert aioclient_mock.call_count == 3 - assert not aioclient_mock.mock_calls[1][2]['ssl'] - assert aioclient_mock.mock_calls[1][2]['password'] == "123456" - assert aioclient_mock.mock_calls[1][2]['port'] == 9999 - assert not aioclient_mock.mock_calls[1][2]['watchdog'] - - -@asyncio.coroutine -def test_setup_api_push_api_data_default(hass, aioclient_mock): - """Test setup with API push default data.""" - aioclient_mock.get( - "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) - aioclient_mock.get( - "http://127.0.0.1/homeassistant/info", json={ - 'result': 'ok', 'data': {'last_version': '10.0'}}) - aioclient_mock.post( - "http://127.0.0.1/homeassistant/options", json={'result': 'ok'}) - - with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}): - result = yield from async_setup_component(hass, 'hassio', { - 'http': {}, - 'hassio': {} - }) - assert result - - assert aioclient_mock.call_count == 3 - assert not aioclient_mock.mock_calls[1][2]['ssl'] - assert aioclient_mock.mock_calls[1][2]['password'] is None - assert aioclient_mock.mock_calls[1][2]['port'] == 8123 - - -@asyncio.coroutine -def test_setup_core_push_timezone(hass, aioclient_mock): - """Test setup with API push default data.""" - aioclient_mock.get( - "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) - aioclient_mock.get( - "http://127.0.0.1/homeassistant/info", json={ - 'result': 'ok', 'data': {'last_version': '10.0'}}) - aioclient_mock.post( - "http://127.0.0.1/supervisor/options", json={'result': 'ok'}) - - with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}): - result = yield from async_setup_component(hass, 'hassio', { - 'hassio': {}, - 'homeassistant': { - 'time_zone': 'testzone', - }, - }) - assert result - - assert aioclient_mock.call_count == 3 - assert aioclient_mock.mock_calls[1][2]['timezone'] == "testzone" - - -@asyncio.coroutine -def test_setup_hassio_no_additional_data(hass, aioclient_mock): - """Test setup with API push default data.""" - aioclient_mock.get( - "http://127.0.0.1/supervisor/ping", json={'result': 'ok'}) - aioclient_mock.get( - "http://127.0.0.1/homeassistant/info", json={ - 'result': 'ok', 'data': {'last_version': '10.0'}}) - aioclient_mock.get( - "http://127.0.0.1/homeassistant/info", json={'result': 'ok'}) - - with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}), \ - patch.dict(os.environ, {'HASSIO_TOKEN': "123456"}): - result = yield from async_setup_component(hass, 'hassio', { - 'hassio': {}, - }) - assert result - - assert aioclient_mock.call_count == 2 - assert aioclient_mock.mock_calls[-1][3]['X-HASSIO-KEY'] == "123456" - - -@asyncio.coroutine -def test_service_register(hassio_env, hass): - """Check if service will be setup.""" - assert (yield from async_setup_component(hass, 'hassio', {})) - assert hass.services.has_service('hassio', 'addon_start') - assert hass.services.has_service('hassio', 'addon_stop') - assert hass.services.has_service('hassio', 'addon_restart') - assert hass.services.has_service('hassio', 'addon_stdin') - assert hass.services.has_service('hassio', 'host_shutdown') - assert hass.services.has_service('hassio', 'host_reboot') - assert hass.services.has_service('hassio', 'host_reboot') - assert hass.services.has_service('hassio', 'snapshot_full') - assert hass.services.has_service('hassio', 'snapshot_partial') - assert hass.services.has_service('hassio', 'restore_full') - assert hass.services.has_service('hassio', 'restore_partial') - - -@asyncio.coroutine -def test_service_calls(hassio_env, hass, aioclient_mock): - """Call service and check the API calls behind that.""" - assert (yield from async_setup_component(hass, 'hassio', {})) - - aioclient_mock.post( - "http://127.0.0.1/addons/test/start", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/addons/test/stop", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/addons/test/restart", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/addons/test/stdin", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/host/shutdown", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/host/reboot", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/snapshots/new/full", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/snapshots/new/partial", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/snapshots/test/restore/full", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/snapshots/test/restore/partial", - json={'result': 'ok'}) - - yield from hass.services.async_call( - 'hassio', 'addon_start', {'addon': 'test'}) - yield from hass.services.async_call( - 'hassio', 'addon_stop', {'addon': 'test'}) - yield from hass.services.async_call( - 'hassio', 'addon_restart', {'addon': 'test'}) - yield from hass.services.async_call( - 'hassio', 'addon_stdin', {'addon': 'test', 'input': 'test'}) - yield from hass.async_block_till_done() - - assert aioclient_mock.call_count == 4 - assert aioclient_mock.mock_calls[-1][2] == 'test' - - yield from hass.services.async_call('hassio', 'host_shutdown', {}) - yield from hass.services.async_call('hassio', 'host_reboot', {}) - yield from hass.async_block_till_done() - - assert aioclient_mock.call_count == 6 - - yield from hass.services.async_call('hassio', 'snapshot_full', {}) - yield from hass.services.async_call('hassio', 'snapshot_partial', { - 'addons': ['test'], - 'folders': ['ssl'], - 'password': "123456", - }) - yield from hass.async_block_till_done() - - assert aioclient_mock.call_count == 8 - assert aioclient_mock.mock_calls[-1][2] == { - 'addons': ['test'], 'folders': ['ssl'], 'password': "123456"} - - yield from hass.services.async_call('hassio', 'restore_full', { - 'snapshot': 'test', - }) - yield from hass.services.async_call('hassio', 'restore_partial', { - 'snapshot': 'test', - 'homeassistant': False, - 'addons': ['test'], - 'folders': ['ssl'], - 'password': "123456", - }) - yield from hass.async_block_till_done() - - assert aioclient_mock.call_count == 10 - assert aioclient_mock.mock_calls[-1][2] == { - 'addons': ['test'], 'folders': ['ssl'], 'homeassistant': False, - 'password': "123456" - } - - -@asyncio.coroutine -def test_service_calls_core(hassio_env, hass, aioclient_mock): - """Call core service and check the API calls behind that.""" - assert (yield from async_setup_component(hass, 'hassio', {})) - - aioclient_mock.post( - "http://127.0.0.1/homeassistant/restart", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/homeassistant/stop", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/homeassistant/check", json={'result': 'ok'}) - - yield from hass.services.async_call('homeassistant', 'stop') - yield from hass.async_block_till_done() - - assert aioclient_mock.call_count == 1 - - yield from hass.services.async_call('homeassistant', 'check_config') - yield from hass.async_block_till_done() - - assert aioclient_mock.call_count == 2 - - yield from hass.services.async_call('homeassistant', 'restart') - yield from hass.async_block_till_done() - - assert aioclient_mock.call_count == 4 - - -@asyncio.coroutine -def test_check_config_ok(hassio_env, hass, aioclient_mock): - """Check Config that is okay.""" - assert (yield from async_setup_component(hass, 'hassio', {})) - - aioclient_mock.post( - "http://127.0.0.1/homeassistant/check", json={'result': 'ok'}) - - assert (yield from async_check_config(hass)) is None - - -@asyncio.coroutine -def test_check_config_fail(hassio_env, hass, aioclient_mock): - """Check Config that is wrong.""" - assert (yield from async_setup_component(hass, 'hassio', {})) - - aioclient_mock.post( - "http://127.0.0.1/homeassistant/check", json={ - 'result': 'error', 'message': "Error"}) - - assert (yield from async_check_config(hass)) == "Error" - - -@asyncio.coroutine -def test_forward_request(hassio_client): - """Test fetching normal path.""" - response = MagicMock() - response.read.return_value = mock_coro('data') - - with patch('homeassistant.components.hassio.HassIO.command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio._create_response') as mresp: - mresp.return_value = 'response' - resp = yield from hassio_client.post('/api/hassio/beer', headers={ - HTTP_HEADER_HA_AUTH: API_PASSWORD - }) - - # Check we got right response - assert resp.status == 200 - body = yield from resp.text() - assert body == 'response' - - # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') - - -@asyncio.coroutine -def test_auth_required_forward_request(hassio_client): - """Test auth required for normal request.""" - resp = yield from hassio_client.post('/api/hassio/beer') - - # Check we got right response - assert resp.status == 401 - - -@asyncio.coroutine -@pytest.mark.parametrize( - 'build_type', [ - 'es5/index.html', 'es5/hassio-app.html', 'latest/index.html', - 'latest/hassio-app.html' - ]) -def test_forward_request_no_auth_for_panel(hassio_client, build_type): - """Test no auth needed for .""" - response = MagicMock() - response.read.return_value = mock_coro('data') - - with patch('homeassistant.components.hassio.HassIO.command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio._create_response') as mresp: - mresp.return_value = 'response' - resp = yield from hassio_client.get( - '/api/hassio/app-{}'.format(build_type)) - - # Check we got right response - assert resp.status == 200 - body = yield from resp.text() - assert body == 'response' - - # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') - - -@asyncio.coroutine -def test_forward_request_no_auth_for_logo(hassio_client): - """Test no auth needed for .""" - response = MagicMock() - response.read.return_value = mock_coro('data') - - with patch('homeassistant.components.hassio.HassIO.command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio._create_response') as mresp: - mresp.return_value = 'response' - resp = yield from hassio_client.get('/api/hassio/addons/bl_b392/logo') - - # Check we got right response - assert resp.status == 200 - body = yield from resp.text() - assert body == 'response' - - # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') - - -@asyncio.coroutine -def test_forward_log_request(hassio_client): - """Test fetching normal log path.""" - response = MagicMock() - response.read.return_value = mock_coro('data') - - with patch('homeassistant.components.hassio.HassIO.command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio.' - '_create_response_log') as mresp: - mresp.return_value = 'response' - resp = yield from hassio_client.get('/api/hassio/beer/logs', headers={ - HTTP_HEADER_HA_AUTH: API_PASSWORD - }) - - # Check we got right response - assert resp.status == 200 - body = yield from resp.text() - assert body == 'response' - - # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') - - -@asyncio.coroutine -def test_bad_gateway_when_cannot_find_supervisor(hassio_client): - """Test we get a bad gateway error if we can't find supervisor.""" - with patch('homeassistant.components.hassio.async_timeout.timeout', - side_effect=asyncio.TimeoutError): - resp = yield from hassio_client.get( - '/api/hassio/addons/test/info', headers={ - HTTP_HEADER_HA_AUTH: API_PASSWORD - }) - assert resp.status == 502