Skip to content

Commit

Permalink
Block tests from opening sockets (#55516)
Browse files Browse the repository at this point in the history
  • Loading branch information
emontnemery committed Oct 6, 2021
1 parent a8b7c52 commit f6682ba
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 5 deletions.
1 change: 1 addition & 0 deletions requirements_test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pipdeptree==2.1.0
pylint-strict-informational==0.1
pytest-aiohttp==0.3.0
pytest-cov==2.12.1
pytest-socket==0.4.1
pytest-test-groups==1.0.3
pytest-sugar==0.9.4
pytest-timeout==1.4.2
Expand Down
8 changes: 8 additions & 0 deletions tests/components/auth/conftest.py
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions tests/components/emulated_hue/test_upnp.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ def sendto(self, 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
def hue_client(aiohttp_client):
"""Return a hue API client."""
Expand Down
6 changes: 6 additions & 0 deletions tests/components/frontend/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
async def mock_http_client(hass, aiohttp_client, frontend):
"""Start the Home Assistant HTTP component."""
Expand Down
8 changes: 8 additions & 0 deletions tests/components/http/conftest.py
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions tests/components/image_processing/test_init.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""The tests for the image_processing component."""
from unittest.mock import PropertyMock, patch

import pytest

import homeassistant.components.http as http
import homeassistant.components.image_processing as ip
from homeassistant.const import ATTR_ENTITY_PICTURE
Expand All @@ -11,6 +13,12 @@
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):
"""Return camera url."""
state = hass.states.get("camera.demo_camera")
Expand Down
7 changes: 5 additions & 2 deletions tests/components/motioneye/test_camera.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Test the motionEye camera."""
import copy
import logging
from typing import Any, cast
from unittest.mock import AsyncMock, Mock

Expand Down Expand Up @@ -48,7 +47,11 @@

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:
Expand Down
6 changes: 6 additions & 0 deletions tests/components/nest/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ async def response_handler(self, request):
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
async def auth(aiohttp_client):
"""Fixture for an AbstractAuth."""
Expand Down
72 changes: 69 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import datetime
import functools
import logging
import socket
import ssl
import threading
from unittest.mock import MagicMock, patch

from aiohttp.test_utils import make_mocked_request
import multidict
import pytest
import pytest_socket
import requests_mock as _requests_mock

from homeassistant import core as ha, loader, runner, util
Expand Down Expand Up @@ -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):
"""Force a function to require a keyword _test_real to be passed in."""

Expand Down Expand Up @@ -319,7 +385,7 @@ def local_auth(hass):


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

async def auth_client():
Expand All @@ -332,7 +398,7 @@ async def auth_client():


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

async def client():
Expand Down Expand Up @@ -367,7 +433,7 @@ def current_request_with_host(current_request):


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

async def create_client(hass=hass, access_token=hass_access_token):
Expand Down
18 changes: 18 additions & 0 deletions tests/test_test_fixtures.py
Original file line number Diff line number Diff line change
@@ -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))

0 comments on commit f6682ba

Please sign in to comment.