mirror of
https://github.com/home-assistant/core.git
synced 2025-04-22 16:27:56 +00:00
Legacy api fix (#18733)
* Set user for API password requests * Fix tests * Fix typing
This commit is contained in:
parent
9d7b1fc3a7
commit
c2f8dfcb9f
@ -4,16 +4,19 @@ Support Legacy API password auth provider.
|
||||
It will be removed when auth system production ready
|
||||
"""
|
||||
import hmac
|
||||
from typing import Any, Dict, Optional, cast
|
||||
from typing import Any, Dict, Optional, cast, TYPE_CHECKING
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.http import HomeAssistantHTTP # noqa: F401
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, LoginFlow
|
||||
from ..models import Credentials, UserMeta
|
||||
from .. import AuthManager
|
||||
from ..models import Credentials, UserMeta, User
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.components.http import HomeAssistantHTTP # noqa: F401
|
||||
|
||||
|
||||
USER_SCHEMA = vol.Schema({
|
||||
@ -31,6 +34,24 @@ class InvalidAuthError(HomeAssistantError):
|
||||
"""Raised when submitting invalid authentication."""
|
||||
|
||||
|
||||
async def async_get_user(hass: HomeAssistant) -> User:
|
||||
"""Return the legacy API password user."""
|
||||
auth = cast(AuthManager, hass.auth) # type: ignore
|
||||
found = None
|
||||
|
||||
for prv in auth.auth_providers:
|
||||
if prv.type == 'legacy_api_password':
|
||||
found = prv
|
||||
break
|
||||
|
||||
if found is None:
|
||||
raise ValueError('Legacy API password provider not found')
|
||||
|
||||
return await auth.async_get_or_create_user(
|
||||
await found.async_get_or_create_credentials({})
|
||||
)
|
||||
|
||||
|
||||
@AUTH_PROVIDERS.register('legacy_api_password')
|
||||
class LegacyApiPasswordAuthProvider(AuthProvider):
|
||||
"""Example auth provider based on hardcoded usernames and passwords."""
|
||||
|
@ -10,6 +10,7 @@ import jwt
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.const import HTTP_HEADER_HA_AUTH
|
||||
from homeassistant.auth.providers import legacy_api_password
|
||||
from homeassistant.auth.util import generate_secret
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
@ -78,12 +79,16 @@ def setup_auth(app, trusted_networks, use_auth,
|
||||
request.headers[HTTP_HEADER_HA_AUTH].encode('utf-8'))):
|
||||
# A valid auth header has been set
|
||||
authenticated = True
|
||||
request['hass_user'] = await legacy_api_password.async_get_user(
|
||||
app['hass'])
|
||||
|
||||
elif (legacy_auth and DATA_API_PASSWORD in request.query and
|
||||
hmac.compare_digest(
|
||||
api_password.encode('utf-8'),
|
||||
request.query[DATA_API_PASSWORD].encode('utf-8'))):
|
||||
authenticated = True
|
||||
request['hass_user'] = await legacy_api_password.async_get_user(
|
||||
app['hass'])
|
||||
|
||||
elif _is_trusted_ip(request, trusted_networks):
|
||||
authenticated = True
|
||||
|
@ -23,7 +23,7 @@ NPR_NEWS_MP3_URL = "https://pd.npr.org/anon.npr-mp3/npr/news/newscast.mp3"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def alexa_client(loop, hass, aiohttp_client):
|
||||
def alexa_client(loop, hass, hass_client):
|
||||
"""Initialize a Home Assistant server for testing this module."""
|
||||
@callback
|
||||
def mock_service(call):
|
||||
@ -95,7 +95,7 @@ def alexa_client(loop, hass, aiohttp_client):
|
||||
},
|
||||
}
|
||||
}))
|
||||
return loop.run_until_complete(aiohttp_client(hass.http.app))
|
||||
return loop.run_until_complete(hass_client())
|
||||
|
||||
|
||||
def _intent_req(client, data=None):
|
||||
|
@ -1437,10 +1437,10 @@ async def test_unsupported_domain(hass):
|
||||
assert not msg['payload']['endpoints']
|
||||
|
||||
|
||||
async def do_http_discovery(config, hass, aiohttp_client):
|
||||
async def do_http_discovery(config, hass, hass_client):
|
||||
"""Submit a request to the Smart Home HTTP API."""
|
||||
await async_setup_component(hass, alexa.DOMAIN, config)
|
||||
http_client = await aiohttp_client(hass.http.app)
|
||||
http_client = await hass_client()
|
||||
|
||||
request = get_new_request('Alexa.Discovery', 'Discover')
|
||||
response = await http_client.post(
|
||||
@ -1450,7 +1450,7 @@ async def do_http_discovery(config, hass, aiohttp_client):
|
||||
return response
|
||||
|
||||
|
||||
async def test_http_api(hass, aiohttp_client):
|
||||
async def test_http_api(hass, hass_client):
|
||||
"""With `smart_home:` HTTP API is exposed."""
|
||||
config = {
|
||||
'alexa': {
|
||||
@ -1458,7 +1458,7 @@ async def test_http_api(hass, aiohttp_client):
|
||||
}
|
||||
}
|
||||
|
||||
response = await do_http_discovery(config, hass, aiohttp_client)
|
||||
response = await do_http_discovery(config, hass, hass_client)
|
||||
response_data = await response.json()
|
||||
|
||||
# Here we're testing just the HTTP view glue -- details of discovery are
|
||||
@ -1466,12 +1466,12 @@ async def test_http_api(hass, aiohttp_client):
|
||||
assert response_data['event']['header']['name'] == 'Discover.Response'
|
||||
|
||||
|
||||
async def test_http_api_disabled(hass, aiohttp_client):
|
||||
async def test_http_api_disabled(hass, hass_client):
|
||||
"""Without `smart_home:`, the HTTP API is disabled."""
|
||||
config = {
|
||||
'alexa': {}
|
||||
}
|
||||
response = await do_http_discovery(config, hass, aiohttp_client)
|
||||
response = await do_http_discovery(config, hass, hass_client)
|
||||
|
||||
assert response.status == 404
|
||||
|
||||
|
@ -4,6 +4,7 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
|
||||
from homeassistant.auth.const import GROUP_ID_ADMIN, GROUP_ID_READ_ONLY
|
||||
from homeassistant.auth.providers import legacy_api_password, homeassistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components.websocket_api.http import URL
|
||||
from homeassistant.components.websocket_api.auth import (
|
||||
@ -88,7 +89,7 @@ def hass_access_token(hass, hass_admin_user):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass_admin_user(hass):
|
||||
def hass_admin_user(hass, local_auth):
|
||||
"""Return a Home Assistant admin user."""
|
||||
admin_group = hass.loop.run_until_complete(hass.auth.async_get_group(
|
||||
GROUP_ID_ADMIN))
|
||||
@ -96,8 +97,42 @@ def hass_admin_user(hass):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass_read_only_user(hass):
|
||||
def hass_read_only_user(hass, local_auth):
|
||||
"""Return a Home Assistant read only user."""
|
||||
read_only_group = hass.loop.run_until_complete(hass.auth.async_get_group(
|
||||
GROUP_ID_READ_ONLY))
|
||||
return MockUser(groups=[read_only_group]).add_to_hass(hass)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def legacy_auth(hass):
|
||||
"""Load legacy API password provider."""
|
||||
prv = legacy_api_password.LegacyApiPasswordAuthProvider(
|
||||
hass, hass.auth._store, {
|
||||
'type': 'legacy_api_password'
|
||||
}
|
||||
)
|
||||
hass.auth._providers[(prv.type, prv.id)] = prv
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def local_auth(hass):
|
||||
"""Load local auth provider."""
|
||||
prv = homeassistant.HassAuthProvider(
|
||||
hass, hass.auth._store, {
|
||||
'type': 'homeassistant'
|
||||
}
|
||||
)
|
||||
hass.auth._providers[(prv.type, prv.id)] = prv
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hass_client(hass, aiohttp_client, hass_access_token):
|
||||
"""Return an authenticated HTTP client."""
|
||||
async def auth_client():
|
||||
"""Return an authenticated client."""
|
||||
return await aiohttp_client(hass.http.app, headers={
|
||||
'Authorization': "Bearer {}".format(hass_access_token)
|
||||
})
|
||||
|
||||
return auth_client
|
||||
|
@ -27,7 +27,7 @@ def hassio_env():
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hassio_client(hassio_env, hass, aiohttp_client):
|
||||
def hassio_client(hassio_env, hass, aiohttp_client, legacy_auth):
|
||||
"""Create mock hassio http client."""
|
||||
with patch('homeassistant.components.hassio.HassIO.update_hass_api',
|
||||
Mock(return_value=mock_coro({"result": "ok"}))), \
|
||||
|
@ -83,7 +83,8 @@ async def test_access_without_password(app, aiohttp_client):
|
||||
assert resp.status == 200
|
||||
|
||||
|
||||
async def test_access_with_password_in_header(app, aiohttp_client):
|
||||
async def test_access_with_password_in_header(app, aiohttp_client,
|
||||
legacy_auth):
|
||||
"""Test access with password in header."""
|
||||
setup_auth(app, [], False, api_password=API_PASSWORD)
|
||||
client = await aiohttp_client(app)
|
||||
@ -97,7 +98,7 @@ async def test_access_with_password_in_header(app, aiohttp_client):
|
||||
assert req.status == 401
|
||||
|
||||
|
||||
async def test_access_with_password_in_query(app, aiohttp_client):
|
||||
async def test_access_with_password_in_query(app, aiohttp_client, legacy_auth):
|
||||
"""Test access with password in URL."""
|
||||
setup_auth(app, [], False, api_password=API_PASSWORD)
|
||||
client = await aiohttp_client(app)
|
||||
@ -219,7 +220,8 @@ async def test_auth_active_access_with_trusted_ip(app2, aiohttp_client):
|
||||
"{} should be trusted".format(remote_addr)
|
||||
|
||||
|
||||
async def test_auth_active_blocked_api_password_access(app, aiohttp_client):
|
||||
async def test_auth_active_blocked_api_password_access(
|
||||
app, aiohttp_client, legacy_auth):
|
||||
"""Test access using api_password should be blocked when auth.active."""
|
||||
setup_auth(app, [], True, api_password=API_PASSWORD)
|
||||
client = await aiohttp_client(app)
|
||||
@ -239,7 +241,8 @@ async def test_auth_active_blocked_api_password_access(app, aiohttp_client):
|
||||
assert req.status == 401
|
||||
|
||||
|
||||
async def test_auth_legacy_support_api_password_access(app, aiohttp_client):
|
||||
async def test_auth_legacy_support_api_password_access(
|
||||
app, aiohttp_client, legacy_auth):
|
||||
"""Test access using api_password if auth.support_legacy."""
|
||||
setup_auth(app, [], True, support_legacy=True, api_password=API_PASSWORD)
|
||||
client = await aiohttp_client(app)
|
||||
|
@ -124,7 +124,7 @@ async def test_api_no_base_url(hass):
|
||||
assert hass.config.api.base_url == 'http://127.0.0.1:8123'
|
||||
|
||||
|
||||
async def test_not_log_password(hass, aiohttp_client, caplog):
|
||||
async def test_not_log_password(hass, aiohttp_client, caplog, legacy_auth):
|
||||
"""Test access with password doesn't get logged."""
|
||||
assert await async_setup_component(hass, 'api', {
|
||||
'http': {
|
||||
|
@ -16,12 +16,10 @@ from tests.common import async_mock_service
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_api_client(hass, aiohttp_client, hass_access_token):
|
||||
def mock_api_client(hass, hass_client):
|
||||
"""Start the Hass HTTP component and return admin API client."""
|
||||
hass.loop.run_until_complete(async_setup_component(hass, 'api', {}))
|
||||
return hass.loop.run_until_complete(aiohttp_client(hass.http.app, headers={
|
||||
'Authorization': 'Bearer {}'.format(hass_access_token)
|
||||
}))
|
||||
return hass.loop.run_until_complete(hass_client())
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
@ -408,7 +406,7 @@ def _listen_count(hass):
|
||||
|
||||
|
||||
async def test_api_error_log(hass, aiohttp_client, hass_access_token,
|
||||
hass_admin_user):
|
||||
hass_admin_user, legacy_auth):
|
||||
"""Test if we can fetch the error log."""
|
||||
hass.data[DATA_LOGGING] = '/some/path'
|
||||
await async_setup_component(hass, 'api', {
|
||||
@ -566,5 +564,17 @@ async def test_rendering_template_admin(hass, mock_api_client,
|
||||
hass_admin_user):
|
||||
"""Test rendering a template requires admin."""
|
||||
hass_admin_user.groups = []
|
||||
resp = await mock_api_client.post('/api/template')
|
||||
resp = await mock_api_client.post(const.URL_API_TEMPLATE)
|
||||
assert resp.status == 401
|
||||
|
||||
|
||||
async def test_rendering_template_legacy_user(
|
||||
hass, mock_api_client, aiohttp_client, legacy_auth):
|
||||
"""Test rendering a template with legacy API password."""
|
||||
hass.states.async_set('sensor.temperature', 10)
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
resp = await client.post(
|
||||
const.URL_API_TEMPLATE,
|
||||
json={"template": '{{ states.sensor.temperature.state }}'}
|
||||
)
|
||||
assert resp.status == 401
|
||||
|
@ -90,7 +90,7 @@ async def test_register_before_setup(hass):
|
||||
assert intent.text_input == 'I would like the Grolsch beer'
|
||||
|
||||
|
||||
async def test_http_processing_intent(hass, aiohttp_client):
|
||||
async def test_http_processing_intent(hass, hass_client):
|
||||
"""Test processing intent via HTTP API."""
|
||||
class TestIntentHandler(intent.IntentHandler):
|
||||
"""Test Intent Handler."""
|
||||
@ -120,7 +120,7 @@ async def test_http_processing_intent(hass, aiohttp_client):
|
||||
})
|
||||
assert result
|
||||
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
client = await hass_client()
|
||||
resp = await client.post('/api/conversation/process', json={
|
||||
'text': 'I would like the Grolsch beer'
|
||||
})
|
||||
@ -244,7 +244,7 @@ async def test_toggle_intent(hass, sentence):
|
||||
assert call.data == {'entity_id': 'light.kitchen'}
|
||||
|
||||
|
||||
async def test_http_api(hass, aiohttp_client):
|
||||
async def test_http_api(hass, hass_client):
|
||||
"""Test the HTTP conversation API."""
|
||||
result = await component.async_setup(hass, {})
|
||||
assert result
|
||||
@ -252,7 +252,7 @@ async def test_http_api(hass, aiohttp_client):
|
||||
result = await async_setup_component(hass, 'conversation', {})
|
||||
assert result
|
||||
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
client = await hass_client()
|
||||
hass.states.async_set('light.kitchen', 'off')
|
||||
calls = async_mock_service(hass, HASS_DOMAIN, 'turn_on')
|
||||
|
||||
@ -268,7 +268,7 @@ async def test_http_api(hass, aiohttp_client):
|
||||
assert call.data == {'entity_id': 'light.kitchen'}
|
||||
|
||||
|
||||
async def test_http_api_wrong_data(hass, aiohttp_client):
|
||||
async def test_http_api_wrong_data(hass, hass_client):
|
||||
"""Test the HTTP conversation API."""
|
||||
result = await component.async_setup(hass, {})
|
||||
assert result
|
||||
@ -276,7 +276,7 @@ async def test_http_api_wrong_data(hass, aiohttp_client):
|
||||
result = await async_setup_component(hass, 'conversation', {})
|
||||
assert result
|
||||
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
client = await hass_client()
|
||||
|
||||
resp = await client.post('/api/conversation/process', json={
|
||||
'text': 123
|
||||
|
@ -515,13 +515,13 @@ class TestComponentHistory(unittest.TestCase):
|
||||
return zero, four, states
|
||||
|
||||
|
||||
async def test_fetch_period_api(hass, aiohttp_client):
|
||||
async def test_fetch_period_api(hass, hass_client):
|
||||
"""Test the fetch period view for history."""
|
||||
await hass.async_add_job(init_recorder_component, hass)
|
||||
await async_setup_component(hass, 'history', {})
|
||||
await hass.components.recorder.wait_connection_ready()
|
||||
await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
client = await hass_client()
|
||||
response = await client.get(
|
||||
'/api/history/period/{}'.format(dt_util.utcnow().isoformat()))
|
||||
assert response.status == 200
|
||||
|
@ -55,7 +55,7 @@ def test_recent_items_intent(hass):
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_deprecated_api_get_all(hass, aiohttp_client):
|
||||
def test_deprecated_api_get_all(hass, hass_client):
|
||||
"""Test the API."""
|
||||
yield from async_setup_component(hass, 'shopping_list', {})
|
||||
|
||||
@ -66,7 +66,7 @@ def test_deprecated_api_get_all(hass, aiohttp_client):
|
||||
hass, 'test', 'HassShoppingListAddItem', {'item': {'value': 'wine'}}
|
||||
)
|
||||
|
||||
client = yield from aiohttp_client(hass.http.app)
|
||||
client = yield from hass_client()
|
||||
resp = yield from client.get('/api/shopping_list')
|
||||
|
||||
assert resp.status == 200
|
||||
@ -110,7 +110,7 @@ async def test_ws_get_items(hass, hass_ws_client):
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_deprecated_api_update(hass, aiohttp_client):
|
||||
def test_deprecated_api_update(hass, hass_client):
|
||||
"""Test the API."""
|
||||
yield from async_setup_component(hass, 'shopping_list', {})
|
||||
|
||||
@ -124,7 +124,7 @@ def test_deprecated_api_update(hass, aiohttp_client):
|
||||
beer_id = hass.data['shopping_list'].items[0]['id']
|
||||
wine_id = hass.data['shopping_list'].items[1]['id']
|
||||
|
||||
client = yield from aiohttp_client(hass.http.app)
|
||||
client = yield from hass_client()
|
||||
resp = yield from client.post(
|
||||
'/api/shopping_list/item/{}'.format(beer_id), json={
|
||||
'name': 'soda'
|
||||
@ -220,7 +220,7 @@ async def test_ws_update_item(hass, hass_ws_client):
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_api_update_fails(hass, aiohttp_client):
|
||||
def test_api_update_fails(hass, hass_client):
|
||||
"""Test the API."""
|
||||
yield from async_setup_component(hass, 'shopping_list', {})
|
||||
|
||||
@ -228,7 +228,7 @@ def test_api_update_fails(hass, aiohttp_client):
|
||||
hass, 'test', 'HassShoppingListAddItem', {'item': {'value': 'beer'}}
|
||||
)
|
||||
|
||||
client = yield from aiohttp_client(hass.http.app)
|
||||
client = yield from hass_client()
|
||||
resp = yield from client.post(
|
||||
'/api/shopping_list/non_existing', json={
|
||||
'name': 'soda'
|
||||
@ -275,7 +275,7 @@ async def test_ws_update_item_fail(hass, hass_ws_client):
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_api_clear_completed(hass, aiohttp_client):
|
||||
def test_api_clear_completed(hass, hass_client):
|
||||
"""Test the API."""
|
||||
yield from async_setup_component(hass, 'shopping_list', {})
|
||||
|
||||
@ -289,7 +289,7 @@ def test_api_clear_completed(hass, aiohttp_client):
|
||||
beer_id = hass.data['shopping_list'].items[0]['id']
|
||||
wine_id = hass.data['shopping_list'].items[1]['id']
|
||||
|
||||
client = yield from aiohttp_client(hass.http.app)
|
||||
client = yield from hass_client()
|
||||
|
||||
# Mark beer as completed
|
||||
resp = yield from client.post(
|
||||
@ -312,11 +312,11 @@ def test_api_clear_completed(hass, aiohttp_client):
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_deprecated_api_create(hass, aiohttp_client):
|
||||
def test_deprecated_api_create(hass, hass_client):
|
||||
"""Test the API."""
|
||||
yield from async_setup_component(hass, 'shopping_list', {})
|
||||
|
||||
client = yield from aiohttp_client(hass.http.app)
|
||||
client = yield from hass_client()
|
||||
resp = yield from client.post('/api/shopping_list/item', json={
|
||||
'name': 'soda'
|
||||
})
|
||||
@ -333,11 +333,11 @@ def test_deprecated_api_create(hass, aiohttp_client):
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_deprecated_api_create_fail(hass, aiohttp_client):
|
||||
def test_deprecated_api_create_fail(hass, hass_client):
|
||||
"""Test the API."""
|
||||
yield from async_setup_component(hass, 'shopping_list', {})
|
||||
|
||||
client = yield from aiohttp_client(hass.http.app)
|
||||
client = yield from hass_client()
|
||||
resp = yield from client.post('/api/shopping_list/item', json={
|
||||
'name': 1234
|
||||
})
|
||||
|
@ -56,7 +56,7 @@ SENSOR_OUTPUT = {
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_client(hass, aiohttp_client):
|
||||
def mock_client(hass, hass_client):
|
||||
"""Start the Home Assistant HTTP component."""
|
||||
with patch('homeassistant.components.spaceapi',
|
||||
return_value=mock_coro(True)):
|
||||
@ -70,7 +70,7 @@ def mock_client(hass, aiohttp_client):
|
||||
hass.states.async_set('test.hum1', 88,
|
||||
attributes={'unit_of_measurement': '%'})
|
||||
|
||||
return hass.loop.run_until_complete(aiohttp_client(hass.http.app))
|
||||
return hass.loop.run_until_complete(hass_client())
|
||||
|
||||
|
||||
async def test_spaceapi_get(hass, mock_client):
|
||||
|
@ -14,9 +14,9 @@ BASIC_CONFIG = {
|
||||
}
|
||||
|
||||
|
||||
async def get_error_log(hass, aiohttp_client, expected_count):
|
||||
async def get_error_log(hass, hass_client, expected_count):
|
||||
"""Fetch all entries from system_log via the API."""
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
client = await hass_client()
|
||||
resp = await client.get('/api/error/all')
|
||||
assert resp.status == 200
|
||||
|
||||
@ -45,37 +45,37 @@ def get_frame(name):
|
||||
return (name, None, None, None)
|
||||
|
||||
|
||||
async def test_normal_logs(hass, aiohttp_client):
|
||||
async def test_normal_logs(hass, hass_client):
|
||||
"""Test that debug and info are not logged."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.debug('debug')
|
||||
_LOGGER.info('info')
|
||||
|
||||
# Assert done by get_error_log
|
||||
await get_error_log(hass, aiohttp_client, 0)
|
||||
await get_error_log(hass, hass_client, 0)
|
||||
|
||||
|
||||
async def test_exception(hass, aiohttp_client):
|
||||
async def test_exception(hass, hass_client):
|
||||
"""Test that exceptions are logged and retrieved correctly."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_generate_and_log_exception('exception message', 'log message')
|
||||
log = (await get_error_log(hass, aiohttp_client, 1))[0]
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
assert_log(log, 'exception message', 'log message', 'ERROR')
|
||||
|
||||
|
||||
async def test_warning(hass, aiohttp_client):
|
||||
async def test_warning(hass, hass_client):
|
||||
"""Test that warning are logged and retrieved correctly."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.warning('warning message')
|
||||
log = (await get_error_log(hass, aiohttp_client, 1))[0]
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
assert_log(log, '', 'warning message', 'WARNING')
|
||||
|
||||
|
||||
async def test_error(hass, aiohttp_client):
|
||||
async def test_error(hass, hass_client):
|
||||
"""Test that errors are logged and retrieved correctly."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.error('error message')
|
||||
log = (await get_error_log(hass, aiohttp_client, 1))[0]
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
assert_log(log, '', 'error message', 'ERROR')
|
||||
|
||||
|
||||
@ -121,26 +121,26 @@ async def test_error_posted_as_event(hass):
|
||||
assert_log(events[0].data, '', 'error message', 'ERROR')
|
||||
|
||||
|
||||
async def test_critical(hass, aiohttp_client):
|
||||
async def test_critical(hass, hass_client):
|
||||
"""Test that critical are logged and retrieved correctly."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.critical('critical message')
|
||||
log = (await get_error_log(hass, aiohttp_client, 1))[0]
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
assert_log(log, '', 'critical message', 'CRITICAL')
|
||||
|
||||
|
||||
async def test_remove_older_logs(hass, aiohttp_client):
|
||||
async def test_remove_older_logs(hass, hass_client):
|
||||
"""Test that older logs are rotated out."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.error('error message 1')
|
||||
_LOGGER.error('error message 2')
|
||||
_LOGGER.error('error message 3')
|
||||
log = await get_error_log(hass, aiohttp_client, 2)
|
||||
log = await get_error_log(hass, hass_client, 2)
|
||||
assert_log(log[0], '', 'error message 3', 'ERROR')
|
||||
assert_log(log[1], '', 'error message 2', 'ERROR')
|
||||
|
||||
|
||||
async def test_clear_logs(hass, aiohttp_client):
|
||||
async def test_clear_logs(hass, hass_client):
|
||||
"""Test that the log can be cleared via a service call."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.error('error message')
|
||||
@ -151,7 +151,7 @@ async def test_clear_logs(hass, aiohttp_client):
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Assert done by get_error_log
|
||||
await get_error_log(hass, aiohttp_client, 0)
|
||||
await get_error_log(hass, hass_client, 0)
|
||||
|
||||
|
||||
async def test_write_log(hass):
|
||||
@ -197,13 +197,13 @@ async def test_write_choose_level(hass):
|
||||
assert logger.method_calls[0] == ('debug', ('test_message',))
|
||||
|
||||
|
||||
async def test_unknown_path(hass, aiohttp_client):
|
||||
async def test_unknown_path(hass, hass_client):
|
||||
"""Test error logged from unknown path."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.findCaller = MagicMock(
|
||||
return_value=('unknown_path', 0, None, None))
|
||||
_LOGGER.error('error message')
|
||||
log = (await get_error_log(hass, aiohttp_client, 1))[0]
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
assert log['source'] == 'unknown_path'
|
||||
|
||||
|
||||
@ -222,31 +222,31 @@ def log_error_from_test_path(path):
|
||||
_LOGGER.error('error message')
|
||||
|
||||
|
||||
async def test_homeassistant_path(hass, aiohttp_client):
|
||||
async def test_homeassistant_path(hass, hass_client):
|
||||
"""Test error logged from homeassistant path."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
with patch('homeassistant.components.system_log.HOMEASSISTANT_PATH',
|
||||
new=['venv_path/homeassistant']):
|
||||
log_error_from_test_path(
|
||||
'venv_path/homeassistant/component/component.py')
|
||||
log = (await get_error_log(hass, aiohttp_client, 1))[0]
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
assert log['source'] == 'component/component.py'
|
||||
|
||||
|
||||
async def test_config_path(hass, aiohttp_client):
|
||||
async def test_config_path(hass, hass_client):
|
||||
"""Test error logged from config path."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
with patch.object(hass.config, 'config_dir', new='config'):
|
||||
log_error_from_test_path('config/custom_component/test.py')
|
||||
log = (await get_error_log(hass, aiohttp_client, 1))[0]
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
assert log['source'] == 'custom_component/test.py'
|
||||
|
||||
|
||||
async def test_netdisco_path(hass, aiohttp_client):
|
||||
async def test_netdisco_path(hass, hass_client):
|
||||
"""Test error logged from netdisco path."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
with patch.dict('sys.modules',
|
||||
netdisco=MagicMock(__path__=['venv_path/netdisco'])):
|
||||
log_error_from_test_path('venv_path/netdisco/disco_component.py')
|
||||
log = (await get_error_log(hass, aiohttp_client, 1))[0]
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
assert log['source'] == 'disco_component.py'
|
||||
|
@ -7,10 +7,10 @@ from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_client(hass, aiohttp_client):
|
||||
def mock_client(hass, hass_client):
|
||||
"""Create http client for webhooks."""
|
||||
hass.loop.run_until_complete(async_setup_component(hass, 'webhook', {}))
|
||||
return hass.loop.run_until_complete(aiohttp_client(hass.http.app))
|
||||
return hass.loop.run_until_complete(hass_client())
|
||||
|
||||
|
||||
async def test_unregistering_webhook(hass, mock_client):
|
||||
|
Loading…
x
Reference in New Issue
Block a user