Use freezegun for testing aes http client delays (#954)

This commit is contained in:
Steven B 2024-06-05 20:13:10 +03:00 committed by GitHub
parent 8a0edbe2c5
commit 39fc21a124
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 79 additions and 12 deletions

View File

@ -15,6 +15,7 @@ import aiohttp
import pytest import pytest
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding as asymmetric_padding from cryptography.hazmat.primitives.asymmetric import padding as asymmetric_padding
from freezegun.api import FrozenDateTimeFactory
from yarl import URL from yarl import URL
from ..aestransport import AesEncyptionSession, AesTransport, TransportState from ..aestransport import AesEncyptionSession, AesTransport, TransportState
@ -287,12 +288,20 @@ async def test_port_override():
@pytest.mark.parametrize( @pytest.mark.parametrize(
"request_delay, should_error, should_succeed", "device_delay_required, should_error, should_succeed",
[(0, False, True), (0.125, True, True), (0.3, True, True), (0.7, True, False)], [
ids=["No error", "Error then succeed", "Two errors then succeed", "No succeed"], pytest.param(0, False, True, id="No error"),
pytest.param(0.125, True, True, id="Error then succeed"),
pytest.param(0.3, True, True, id="Two errors then succeed"),
pytest.param(0.7, True, False, id="No succeed"),
],
) )
async def test_device_closes_connection( async def test_device_closes_connection(
mocker, request_delay, should_error, should_succeed mocker,
freezer: FrozenDateTimeFactory,
device_delay_required,
should_error,
should_succeed,
): ):
"""Test the delay logic in http client to deal with devices that close connections after each request. """Test the delay logic in http client to deal with devices that close connections after each request.
@ -300,16 +309,19 @@ async def test_device_closes_connection(
""" """
host = "127.0.0.1" host = "127.0.0.1"
# Speed up the test by dividing all times by a factor. Doesn't seem to work on windows default_delay = HttpClient.WAIT_BETWEEN_REQUESTS_ON_OSERROR
# but leaving here as a TODO to manipulate system time for testing.
speed_up_factor = 1
default_delay = HttpClient.WAIT_BETWEEN_REQUESTS_ON_OSERROR / speed_up_factor
request_delay = request_delay / speed_up_factor
mock_aes_device = MockAesDevice( mock_aes_device = MockAesDevice(
host, 200, 0, 0, sequential_request_delay=request_delay host, 200, 0, 0, sequential_request_delay=device_delay_required
) )
mocker.patch.object(aiohttp.ClientSession, "post", side_effect=mock_aes_device.post) mocker.patch.object(aiohttp.ClientSession, "post", side_effect=mock_aes_device.post)
async def _asyncio_sleep_mock(delay, result=None):
freezer.tick(delay)
return result
mocker.patch("asyncio.sleep", side_effect=_asyncio_sleep_mock)
config = DeviceConfig(host, credentials=Credentials("foo", "bar")) config = DeviceConfig(host, credentials=Credentials("foo", "bar"))
transport = AesTransport(config=config) transport = AesTransport(config=config)
transport._http_client.WAIT_BETWEEN_REQUESTS_ON_OSERROR = default_delay transport._http_client.WAIT_BETWEEN_REQUESTS_ON_OSERROR = default_delay
@ -332,7 +344,7 @@ async def test_device_closes_connection(
# If the device errors without a delay then it should error immedately ( + 1) # If the device errors without a delay then it should error immedately ( + 1)
# and then the number of times the default delay passes within the request delay window # and then the number of times the default delay passes within the request delay window
expected_error_count = ( expected_error_count = (
0 if not should_error else int(request_delay / default_delay) + 1 0 if not should_error else int(device_delay_required / default_delay) + 1
) )
for _ in range(3): for _ in range(3):
try: try:

56
poetry.lock generated
View File

@ -636,6 +636,20 @@ docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
typing = ["typing-extensions (>=4.8)"] typing = ["typing-extensions (>=4.8)"]
[[package]]
name = "freezegun"
version = "1.5.1"
description = "Let your Python tests travel through time"
optional = false
python-versions = ">=3.7"
files = [
{file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"},
{file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"},
]
[package.dependencies]
python-dateutil = ">=2.7"
[[package]] [[package]]
name = "frozenlist" name = "frozenlist"
version = "1.4.1" version = "1.4.1"
@ -1505,6 +1519,21 @@ pytest = ">=4.6"
[package.extras] [package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
[[package]]
name = "pytest-freezer"
version = "0.4.8"
description = "Pytest plugin providing a fixture interface for spulec/freezegun"
optional = false
python-versions = ">= 3.6"
files = [
{file = "pytest_freezer-0.4.8-py3-none-any.whl", hash = "sha256:644ce7ddb8ba52b92a1df0a80a699bad2b93514c55cf92e9f2517b68ebe74814"},
{file = "pytest_freezer-0.4.8.tar.gz", hash = "sha256:8ee2f724b3ff3540523fa355958a22e6f4c1c819928b78a7a183ae4248ce6ee6"},
]
[package.dependencies]
freezegun = ">=1.0"
pytest = ">=3.6"
[[package]] [[package]]
name = "pytest-mock" name = "pytest-mock"
version = "3.14.0" version = "3.14.0"
@ -1555,6 +1584,20 @@ files = [
[package.dependencies] [package.dependencies]
pytest = ">=7.0.0" pytest = ">=7.0.0"
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
]
[package.dependencies]
six = ">=1.5"
[[package]] [[package]]
name = "pytz" name = "pytz"
version = "2024.1" version = "2024.1"
@ -1681,6 +1724,17 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
[[package]] [[package]]
name = "sniffio" name = "sniffio"
version = "1.3.1" version = "1.3.1"
@ -2156,4 +2210,4 @@ speedups = ["kasa-crypt", "orjson"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.8" python-versions = "^3.8"
content-hash = "ba5c0da1e413e466834d0954528c7ace6dd9e01d9fb2e626f4c6b23044803aef" content-hash = "871ef421fe7d48608bcea18b4c41d8bb368e84d74bf7b29db832dc97c5b980ae"

View File

@ -58,6 +58,7 @@ codecov = "*"
xdoctest = "*" xdoctest = "*"
coverage = {version = "*", extras = ["toml"]} coverage = {version = "*", extras = ["toml"]}
pytest-timeout = "^2" pytest-timeout = "^2"
pytest-freezer = "^0.4"
[tool.poetry.extras] [tool.poetry.extras]
docs = ["sphinx", "sphinx_rtd_theme", "sphinxcontrib-programoutput", "myst-parser", "docutils"] docs = ["sphinx", "sphinx_rtd_theme", "sphinxcontrib-programoutput", "myst-parser", "docutils"]