mirror of
https://github.com/home-assistant/core.git
synced 2025-04-26 02:07:54 +00:00
Block tests from opening sockets (#55516)
This commit is contained in:
parent
a8b7c521f6
commit
f6682ba99d
@ -18,6 +18,7 @@ pipdeptree==2.1.0
|
|||||||
pylint-strict-informational==0.1
|
pylint-strict-informational==0.1
|
||||||
pytest-aiohttp==0.3.0
|
pytest-aiohttp==0.3.0
|
||||||
pytest-cov==2.12.1
|
pytest-cov==2.12.1
|
||||||
|
pytest-socket==0.4.1
|
||||||
pytest-test-groups==1.0.3
|
pytest-test-groups==1.0.3
|
||||||
pytest-sugar==0.9.4
|
pytest-sugar==0.9.4
|
||||||
pytest-timeout==1.4.2
|
pytest-timeout==1.4.2
|
||||||
|
8
tests/components/auth/conftest.py
Normal file
8
tests/components/auth/conftest.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
"""Test configuration for auth."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def aiohttp_client(loop, aiohttp_client, socket_enabled):
|
||||||
|
"""Return aiohttp_client and allow opening sockets."""
|
||||||
|
return aiohttp_client
|
@ -28,6 +28,12 @@ class MockTransport:
|
|||||||
self.sends.append((response, addr))
|
self.sends.append((response, addr))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def aiohttp_client(loop, aiohttp_client, socket_enabled):
|
||||||
|
"""Return aiohttp_client and allow opening sockets."""
|
||||||
|
return aiohttp_client
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def hue_client(aiohttp_client):
|
def hue_client(aiohttp_client):
|
||||||
"""Return a hue API client."""
|
"""Return a hue API client."""
|
||||||
|
@ -79,6 +79,12 @@ async def frontend_themes(hass):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def aiohttp_client(loop, aiohttp_client, socket_enabled):
|
||||||
|
"""Return aiohttp_client and allow opening sockets."""
|
||||||
|
return aiohttp_client
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def mock_http_client(hass, aiohttp_client, frontend):
|
async def mock_http_client(hass, aiohttp_client, frontend):
|
||||||
"""Start the Home Assistant HTTP component."""
|
"""Start the Home Assistant HTTP component."""
|
||||||
|
8
tests/components/http/conftest.py
Normal file
8
tests/components/http/conftest.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
"""Test configuration for http."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def aiohttp_client(loop, aiohttp_client, socket_enabled):
|
||||||
|
"""Return aiohttp_client and allow opening sockets."""
|
||||||
|
return aiohttp_client
|
@ -1,6 +1,8 @@
|
|||||||
"""The tests for the image_processing component."""
|
"""The tests for the image_processing component."""
|
||||||
from unittest.mock import PropertyMock, patch
|
from unittest.mock import PropertyMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
import homeassistant.components.http as http
|
import homeassistant.components.http as http
|
||||||
import homeassistant.components.image_processing as ip
|
import homeassistant.components.image_processing as ip
|
||||||
from homeassistant.const import ATTR_ENTITY_PICTURE
|
from homeassistant.const import ATTR_ENTITY_PICTURE
|
||||||
@ -11,6 +13,12 @@ from tests.common import assert_setup_component, async_capture_events
|
|||||||
from tests.components.image_processing import common
|
from tests.components.image_processing import common
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def aiohttp_unused_port(loop, aiohttp_unused_port, socket_enabled):
|
||||||
|
"""Return aiohttp_unused_port and allow opening sockets."""
|
||||||
|
return aiohttp_unused_port
|
||||||
|
|
||||||
|
|
||||||
def get_url(hass):
|
def get_url(hass):
|
||||||
"""Return camera url."""
|
"""Return camera url."""
|
||||||
state = hass.states.get("camera.demo_camera")
|
state = hass.states.get("camera.demo_camera")
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""Test the motionEye camera."""
|
"""Test the motionEye camera."""
|
||||||
import copy
|
import copy
|
||||||
import logging
|
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
from unittest.mock import AsyncMock, Mock
|
from unittest.mock import AsyncMock, Mock
|
||||||
|
|
||||||
@ -48,7 +47,11 @@ from . import (
|
|||||||
|
|
||||||
from tests.common import async_fire_time_changed
|
from tests.common import async_fire_time_changed
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
@pytest.fixture
|
||||||
|
def aiohttp_server(loop, aiohttp_server, socket_enabled):
|
||||||
|
"""Return aiohttp_server and allow opening sockets."""
|
||||||
|
return aiohttp_server
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_camera(hass: HomeAssistant) -> None:
|
async def test_setup_camera(hass: HomeAssistant) -> None:
|
||||||
|
@ -48,6 +48,12 @@ class FakeAuth(AbstractAuth):
|
|||||||
return aiohttp.web.json_response()
|
return aiohttp.web.json_response()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def aiohttp_client(loop, aiohttp_client, socket_enabled):
|
||||||
|
"""Return aiohttp_client and allow opening sockets."""
|
||||||
|
return aiohttp_client
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def auth(aiohttp_client):
|
async def auth(aiohttp_client):
|
||||||
"""Fixture for an AbstractAuth."""
|
"""Fixture for an AbstractAuth."""
|
||||||
|
@ -3,6 +3,7 @@ import asyncio
|
|||||||
import datetime
|
import datetime
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
|
import socket
|
||||||
import ssl
|
import ssl
|
||||||
import threading
|
import threading
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
@ -10,6 +11,7 @@ from unittest.mock import MagicMock, patch
|
|||||||
from aiohttp.test_utils import make_mocked_request
|
from aiohttp.test_utils import make_mocked_request
|
||||||
import multidict
|
import multidict
|
||||||
import pytest
|
import pytest
|
||||||
|
import pytest_socket
|
||||||
import requests_mock as _requests_mock
|
import requests_mock as _requests_mock
|
||||||
|
|
||||||
from homeassistant import core as ha, loader, runner, util
|
from homeassistant import core as ha, loader, runner, util
|
||||||
@ -61,6 +63,70 @@ def pytest_configure(config):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_runtest_setup():
|
||||||
|
"""Throw if tests attempt to open sockets.
|
||||||
|
|
||||||
|
allow_unix_socket is set to True because it's needed by asyncio.
|
||||||
|
Important: socket_allow_hosts must be called before disable_socket, otherwise all
|
||||||
|
destinations will be allowed.
|
||||||
|
"""
|
||||||
|
pytest_socket.socket_allow_hosts(["127.0.0.1"])
|
||||||
|
disable_socket(allow_unix_socket=True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def socket_disabled(pytestconfig):
|
||||||
|
"""Disable socket.socket for duration of this test function.
|
||||||
|
|
||||||
|
This incorporates changes from https://github.com/miketheman/pytest-socket/pull/76
|
||||||
|
and hardcodes allow_unix_socket to True because it's not passed on the command line.
|
||||||
|
"""
|
||||||
|
socket_was_enabled = socket.socket == pytest_socket._true_socket
|
||||||
|
disable_socket(allow_unix_socket=True)
|
||||||
|
yield
|
||||||
|
if socket_was_enabled:
|
||||||
|
pytest_socket.enable_socket()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def socket_enabled(pytestconfig):
|
||||||
|
"""Enable socket.socket for duration of this test function.
|
||||||
|
|
||||||
|
This incorporates changes from https://github.com/miketheman/pytest-socket/pull/76
|
||||||
|
and hardcodes allow_unix_socket to True because it's not passed on the command line.
|
||||||
|
"""
|
||||||
|
socket_was_disabled = socket.socket != pytest_socket._true_socket
|
||||||
|
pytest_socket.enable_socket()
|
||||||
|
yield
|
||||||
|
if socket_was_disabled:
|
||||||
|
disable_socket(allow_unix_socket=True)
|
||||||
|
|
||||||
|
|
||||||
|
def disable_socket(allow_unix_socket=False):
|
||||||
|
"""Disable socket.socket to disable the Internet. useful in testing.
|
||||||
|
|
||||||
|
This incorporates changes from https://github.com/miketheman/pytest-socket/pull/75
|
||||||
|
"""
|
||||||
|
|
||||||
|
class GuardedSocket(socket.socket):
|
||||||
|
"""socket guard to disable socket creation (from pytest-socket)."""
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
if len(args) > 0:
|
||||||
|
is_unix_socket = args[0] == socket.AF_UNIX
|
||||||
|
else:
|
||||||
|
is_unix_socket = kwargs.get("family") == socket.AF_UNIX
|
||||||
|
except AttributeError:
|
||||||
|
# AF_UNIX not supported on Windows https://bugs.python.org/issue33408
|
||||||
|
is_unix_socket = False
|
||||||
|
if is_unix_socket and allow_unix_socket:
|
||||||
|
return super().__new__(cls, *args, **kwargs)
|
||||||
|
raise pytest_socket.SocketBlockedError()
|
||||||
|
|
||||||
|
socket.socket = GuardedSocket
|
||||||
|
|
||||||
|
|
||||||
def check_real(func):
|
def check_real(func):
|
||||||
"""Force a function to require a keyword _test_real to be passed in."""
|
"""Force a function to require a keyword _test_real to be passed in."""
|
||||||
|
|
||||||
@ -319,7 +385,7 @@ def local_auth(hass):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def hass_client(hass, aiohttp_client, hass_access_token):
|
def hass_client(hass, aiohttp_client, hass_access_token, socket_enabled):
|
||||||
"""Return an authenticated HTTP client."""
|
"""Return an authenticated HTTP client."""
|
||||||
|
|
||||||
async def auth_client():
|
async def auth_client():
|
||||||
@ -332,7 +398,7 @@ def hass_client(hass, aiohttp_client, hass_access_token):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def hass_client_no_auth(hass, aiohttp_client):
|
def hass_client_no_auth(hass, aiohttp_client, socket_enabled):
|
||||||
"""Return an unauthenticated HTTP client."""
|
"""Return an unauthenticated HTTP client."""
|
||||||
|
|
||||||
async def client():
|
async def client():
|
||||||
@ -367,7 +433,7 @@ def current_request_with_host(current_request):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def hass_ws_client(aiohttp_client, hass_access_token, hass):
|
def hass_ws_client(aiohttp_client, hass_access_token, hass, socket_enabled):
|
||||||
"""Websocket client fixture connected to websocket server."""
|
"""Websocket client fixture connected to websocket server."""
|
||||||
|
|
||||||
async def create_client(hass=hass, access_token=hass_access_token):
|
async def create_client(hass=hass, access_token=hass_access_token):
|
||||||
|
18
tests/test_test_fixtures.py
Normal file
18
tests/test_test_fixtures.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
"""Test test fixture configuration."""
|
||||||
|
import socket
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import pytest_socket
|
||||||
|
|
||||||
|
|
||||||
|
def test_sockets_disabled():
|
||||||
|
"""Test we can't open sockets."""
|
||||||
|
with pytest.raises(pytest_socket.SocketBlockedError):
|
||||||
|
socket.socket()
|
||||||
|
|
||||||
|
|
||||||
|
def test_sockets_enabled(socket_enabled):
|
||||||
|
"""Test we can't connect to an address different from 127.0.0.1."""
|
||||||
|
mysocket = socket.socket()
|
||||||
|
with pytest.raises(pytest_socket.SocketConnectBlockedError):
|
||||||
|
mysocket.connect(("127.0.0.2", 1234))
|
Loading…
x
Reference in New Issue
Block a user