Block tests from opening sockets (#55516)

This commit is contained in:
Erik Montnemery 2021-10-06 02:46:09 +02:00 committed by GitHub
parent a8b7c521f6
commit f6682ba99d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 135 additions and 5 deletions

View File

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

View 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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

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