2016-11-28 00:26:46 +00:00
|
|
|
"""Test the aiohttp client helper."""
|
2017-01-30 00:15:40 +00:00
|
|
|
import asyncio
|
2021-01-01 21:31:56 +00:00
|
|
|
from unittest.mock import Mock, patch
|
2016-11-28 00:26:46 +00:00
|
|
|
|
|
|
|
import aiohttp
|
2018-03-05 21:28:41 +00:00
|
|
|
import pytest
|
2016-11-28 00:26:46 +00:00
|
|
|
|
2017-02-13 05:24:07 +00:00
|
|
|
from homeassistant.core import EVENT_HOMEASSISTANT_CLOSE
|
2016-11-28 00:26:46 +00:00
|
|
|
import homeassistant.helpers.aiohttp_client as client
|
2019-12-09 15:52:24 +00:00
|
|
|
from homeassistant.setup import async_setup_component
|
2016-11-28 00:26:46 +00:00
|
|
|
|
2020-05-13 07:58:33 +00:00
|
|
|
|
|
|
|
@pytest.fixture(name="camera_client")
|
|
|
|
def camera_client_fixture(hass, hass_client):
|
2018-03-05 21:28:41 +00:00
|
|
|
"""Fixture to fetch camera streams."""
|
2019-07-31 19:25:30 +00:00
|
|
|
assert hass.loop.run_until_complete(
|
|
|
|
async_setup_component(
|
|
|
|
hass,
|
|
|
|
"camera",
|
|
|
|
{
|
|
|
|
"camera": {
|
|
|
|
"name": "config_test",
|
|
|
|
"platform": "mjpeg",
|
|
|
|
"mjpeg_url": "http://example.com/mjpeg_stream",
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
)
|
2020-06-02 15:54:13 +00:00
|
|
|
hass.loop.run_until_complete(hass.async_block_till_done())
|
2018-03-05 21:28:41 +00:00
|
|
|
|
2018-12-02 15:32:53 +00:00
|
|
|
yield hass.loop.run_until_complete(hass_client())
|
2018-03-05 21:28:41 +00:00
|
|
|
|
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
async def test_get_clientsession_with_ssl(hass):
|
|
|
|
"""Test init clientsession with ssl."""
|
|
|
|
client.async_get_clientsession(hass)
|
2016-11-28 00:26:46 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
assert isinstance(hass.data[client.DATA_CLIENTSESSION], aiohttp.ClientSession)
|
|
|
|
assert isinstance(hass.data[client.DATA_CONNECTOR], aiohttp.TCPConnector)
|
2016-11-28 00:26:46 +00:00
|
|
|
|
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
async def test_get_clientsession_without_ssl(hass):
|
|
|
|
"""Test init clientsession without ssl."""
|
|
|
|
client.async_get_clientsession(hass, verify_ssl=False)
|
2019-07-31 19:25:30 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
assert isinstance(
|
|
|
|
hass.data[client.DATA_CLIENTSESSION_NOTVERIFY], aiohttp.ClientSession
|
|
|
|
)
|
|
|
|
assert isinstance(hass.data[client.DATA_CONNECTOR_NOTVERIFY], aiohttp.TCPConnector)
|
2016-11-28 00:26:46 +00:00
|
|
|
|
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
async def test_create_clientsession_with_ssl_and_cookies(hass):
|
|
|
|
"""Test create clientsession with ssl."""
|
|
|
|
session = client.async_create_clientsession(hass, cookies={"bla": True})
|
|
|
|
assert isinstance(session, aiohttp.ClientSession)
|
|
|
|
assert isinstance(hass.data[client.DATA_CONNECTOR], aiohttp.TCPConnector)
|
2016-11-28 00:26:46 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
async def test_create_clientsession_without_ssl_and_cookies(hass):
|
|
|
|
"""Test create clientsession without ssl."""
|
|
|
|
session = client.async_create_clientsession(hass, False, cookies={"bla": True})
|
|
|
|
assert isinstance(session, aiohttp.ClientSession)
|
|
|
|
assert isinstance(hass.data[client.DATA_CONNECTOR_NOTVERIFY], aiohttp.TCPConnector)
|
2016-11-28 00:26:46 +00:00
|
|
|
|
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
async def test_get_clientsession_cleanup(hass):
|
|
|
|
"""Test init clientsession with ssl."""
|
|
|
|
client.async_get_clientsession(hass)
|
2017-01-30 00:15:40 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
assert isinstance(hass.data[client.DATA_CLIENTSESSION], aiohttp.ClientSession)
|
|
|
|
assert isinstance(hass.data[client.DATA_CONNECTOR], aiohttp.TCPConnector)
|
2017-01-30 00:15:40 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_CLOSE)
|
|
|
|
await hass.async_block_till_done()
|
2017-01-30 00:15:40 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
assert hass.data[client.DATA_CLIENTSESSION].closed
|
|
|
|
assert hass.data[client.DATA_CONNECTOR].closed
|
2017-01-30 00:15:40 +00:00
|
|
|
|
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
async def test_get_clientsession_cleanup_without_ssl(hass):
|
|
|
|
"""Test init clientsession with ssl."""
|
|
|
|
client.async_get_clientsession(hass, verify_ssl=False)
|
2017-01-30 12:09:36 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
assert isinstance(
|
|
|
|
hass.data[client.DATA_CLIENTSESSION_NOTVERIFY], aiohttp.ClientSession
|
|
|
|
)
|
|
|
|
assert isinstance(hass.data[client.DATA_CONNECTOR_NOTVERIFY], aiohttp.TCPConnector)
|
2017-01-30 12:09:36 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_CLOSE)
|
|
|
|
await hass.async_block_till_done()
|
2017-01-30 12:09:36 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
assert hass.data[client.DATA_CLIENTSESSION_NOTVERIFY].closed
|
|
|
|
assert hass.data[client.DATA_CONNECTOR_NOTVERIFY].closed
|
2017-01-30 12:09:36 +00:00
|
|
|
|
2017-01-30 00:15:40 +00:00
|
|
|
|
2020-05-13 07:58:33 +00:00
|
|
|
async def test_get_clientsession_patched_close(hass):
|
|
|
|
"""Test closing clientsession does not work."""
|
|
|
|
with patch("aiohttp.ClientSession.close") as mock_close:
|
|
|
|
session = client.async_get_clientsession(hass)
|
|
|
|
|
|
|
|
assert isinstance(hass.data[client.DATA_CLIENTSESSION], aiohttp.ClientSession)
|
|
|
|
assert isinstance(hass.data[client.DATA_CONNECTOR], aiohttp.TCPConnector)
|
|
|
|
|
|
|
|
with pytest.raises(RuntimeError):
|
|
|
|
await session.close()
|
|
|
|
|
|
|
|
assert mock_close.call_count == 0
|
|
|
|
|
|
|
|
|
2021-12-08 09:50:56 +00:00
|
|
|
@patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set())
|
2020-05-13 07:58:33 +00:00
|
|
|
async def test_warning_close_session_integration(hass, caplog):
|
|
|
|
"""Test log warning message when closing the session from integration context."""
|
|
|
|
with patch(
|
|
|
|
"homeassistant.helpers.frame.extract_stack",
|
|
|
|
return_value=[
|
|
|
|
Mock(
|
|
|
|
filename="/home/paulus/homeassistant/core.py",
|
|
|
|
lineno="23",
|
|
|
|
line="do_something()",
|
|
|
|
),
|
|
|
|
Mock(
|
|
|
|
filename="/home/paulus/homeassistant/components/hue/light.py",
|
|
|
|
lineno="23",
|
|
|
|
line="await session.close()",
|
|
|
|
),
|
|
|
|
Mock(
|
|
|
|
filename="/home/paulus/aiohue/lights.py",
|
|
|
|
lineno="2",
|
|
|
|
line="something()",
|
|
|
|
),
|
|
|
|
],
|
|
|
|
):
|
|
|
|
session = client.async_get_clientsession(hass)
|
|
|
|
await session.close()
|
|
|
|
assert (
|
|
|
|
"Detected integration that closes the Home Assistant aiohttp session. "
|
|
|
|
"Please report issue for hue using this method at "
|
|
|
|
"homeassistant/components/hue/light.py, line 23: await session.close()"
|
|
|
|
) in caplog.text
|
|
|
|
|
|
|
|
|
2021-12-08 09:50:56 +00:00
|
|
|
@patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set())
|
2020-05-13 07:58:33 +00:00
|
|
|
async def test_warning_close_session_custom(hass, caplog):
|
|
|
|
"""Test log warning message when closing the session from custom context."""
|
|
|
|
with patch(
|
|
|
|
"homeassistant.helpers.frame.extract_stack",
|
|
|
|
return_value=[
|
|
|
|
Mock(
|
|
|
|
filename="/home/paulus/homeassistant/core.py",
|
|
|
|
lineno="23",
|
|
|
|
line="do_something()",
|
|
|
|
),
|
|
|
|
Mock(
|
|
|
|
filename="/home/paulus/config/custom_components/hue/light.py",
|
|
|
|
lineno="23",
|
|
|
|
line="await session.close()",
|
|
|
|
),
|
|
|
|
Mock(
|
|
|
|
filename="/home/paulus/aiohue/lights.py",
|
|
|
|
lineno="2",
|
|
|
|
line="something()",
|
|
|
|
),
|
|
|
|
],
|
|
|
|
):
|
|
|
|
session = client.async_get_clientsession(hass)
|
|
|
|
await session.close()
|
|
|
|
assert (
|
|
|
|
"Detected integration that closes the Home Assistant aiohttp session. "
|
|
|
|
"Please report issue to the custom component author for hue using this method at "
|
|
|
|
"custom_components/hue/light.py, line 23: await session.close()" in caplog.text
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
async def test_async_aiohttp_proxy_stream(aioclient_mock, camera_client):
|
2017-01-30 00:15:40 +00:00
|
|
|
"""Test that it fetches the given url."""
|
2019-07-31 19:25:30 +00:00
|
|
|
aioclient_mock.get("http://example.com/mjpeg_stream", content=b"Frame1Frame2Frame3")
|
2017-01-30 00:15:40 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
resp = await camera_client.get("/api/camera_proxy_stream/camera.config_test")
|
2017-01-30 00:15:40 +00:00
|
|
|
|
|
|
|
assert resp.status == 200
|
|
|
|
assert aioclient_mock.call_count == 1
|
2020-04-07 16:33:23 +00:00
|
|
|
body = await resp.text()
|
2019-07-31 19:25:30 +00:00
|
|
|
assert body == "Frame1Frame2Frame3"
|
2017-01-30 00:15:40 +00:00
|
|
|
|
2018-03-05 21:28:41 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
async def test_async_aiohttp_proxy_stream_timeout(aioclient_mock, camera_client):
|
2018-03-05 21:28:41 +00:00
|
|
|
"""Test that it fetches the given url."""
|
2019-07-31 19:25:30 +00:00
|
|
|
aioclient_mock.get("http://example.com/mjpeg_stream", exc=asyncio.TimeoutError())
|
2017-01-30 00:15:40 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
resp = await camera_client.get("/api/camera_proxy_stream/camera.config_test")
|
2017-03-30 07:50:53 +00:00
|
|
|
assert resp.status == 504
|
2017-01-30 00:15:40 +00:00
|
|
|
|
2018-03-05 21:28:41 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
async def test_async_aiohttp_proxy_stream_client_err(aioclient_mock, camera_client):
|
2018-03-05 21:28:41 +00:00
|
|
|
"""Test that it fetches the given url."""
|
2019-07-31 19:25:30 +00:00
|
|
|
aioclient_mock.get("http://example.com/mjpeg_stream", exc=aiohttp.ClientError())
|
2017-01-30 00:15:40 +00:00
|
|
|
|
2020-04-07 16:33:23 +00:00
|
|
|
resp = await camera_client.get("/api/camera_proxy_stream/camera.config_test")
|
2017-03-30 07:50:53 +00:00
|
|
|
assert resp.status == 502
|
2021-06-04 16:14:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_client_session_immutable_headers(hass):
|
|
|
|
"""Test we can't mutate headers."""
|
|
|
|
session = client.async_get_clientsession(hass)
|
|
|
|
|
|
|
|
with pytest.raises(TypeError):
|
|
|
|
session.headers["user-agent"] = "bla"
|
|
|
|
|
|
|
|
with pytest.raises(AttributeError):
|
|
|
|
session.headers.update({"user-agent": "bla"})
|