Skip to content

Commit

Permalink
speed up exporter tests by introducing asgiref async testing
Browse files Browse the repository at this point in the history
  • Loading branch information
ekneg54 committed Nov 12, 2024
1 parent ed4e659 commit 34b65ec
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 34 deletions.
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"python.analysis.importFormat": "absolute",
"python.testing.pytestPath": "pytest",
"editor.formatOnSave": true,
"editor.rulers": [
80,
100,
120
],
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
"source.organizeImports": "explicit",
"source.unusedImports": "always",
"source.convertImportFormat": "always"
},
"autoDocstring.docstringFormat": "numpy",
"files.exclude": {
Expand Down
12 changes: 7 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,18 @@ keywords = [
"logdata",
]
dependencies = [
"aiohttp>=3.9.2", # CVE-2024-23334
"aiohttp>=3.9.2", # CVE-2024-23334
"attrs",
"certifi>=2023.7.22", # CVE-2023-37920
"ciso8601", # fastest iso8601 datetime parser. can be removed after dropping support for python < 3.11
"certifi>=2023.7.22", # CVE-2023-37920
"ciso8601", # fastest iso8601 datetime parser. can be removed after dropping support for python < 3.11
"colorama",
"confluent-kafka>2",
"geoip2",
"hyperscan>=0.7.0",
"jsonref",
"luqum",
"more-itertools==8.10.0",
"mysql-connector-python>=9.1.0", # CVE-2024-21272
"mysql-connector-python>=9.1.0", # CVE-2024-21272
"numpy>=1.26.0",
"opensearch-py",
"prometheus_client",
Expand All @@ -84,7 +84,7 @@ dependencies = [
"schedule",
"tldextract",
"urlextract",
"urllib3>=1.26.17", # CVE-2023-43804
"urllib3>=1.26.17", # CVE-2023-43804
"uvicorn",
"deepdiff",
"msgspec",
Expand Down Expand Up @@ -113,6 +113,8 @@ dev = [
"jinja2",
"maturin",
"cibuildwheel",
"asgiref",
"pytest-asyncio",
]

doc = [
Expand Down
83 changes: 55 additions & 28 deletions tests/unit/metrics/test_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
# pylint: disable=protected-access
# pylint: disable=attribute-defined-outside-init
# pylint: disable=line-too-long
import asyncio
import os.path
from unittest import mock

import pytest
import requests
from prometheus_client import REGISTRY
from asgiref.testing import ApplicationCommunicator
from prometheus_client import REGISTRY, CollectorRegistry

from logprep.metrics.exporter import PrometheusExporter
from logprep.metrics.exporter import PrometheusExporter, make_patched_asgi_app
from logprep.util import http
from logprep.util.configuration import MetricsConfig

Expand Down Expand Up @@ -148,38 +150,63 @@ def test_health_endpoint_calls_updated_functions(self):

exporter.server.shut_down()

@pytest.mark.parametrize(
"functions, expected",
[
([lambda: True], 200),
([lambda: True, lambda: True], 200),
([lambda: False], 503),
([lambda: False, lambda: False], 503),
([lambda: False, lambda: True, lambda: True], 503),
],
)
def test_health_check_returns_status_code(self, functions, expected):
def test_health_check_returns_body_and_status_code(self):
exporter = PrometheusExporter(self.metrics_config)
exporter.run(daemon=False)
exporter.update_healthchecks(functions)
exporter.update_healthchecks([lambda: True])
resp = requests.get("http://localhost:8000/health", timeout=0.5)
assert resp.status_code == expected
assert resp.status_code == 200
assert resp.content.decode() == "OK"
exporter.server.shut_down()


class TestAsgiApp:
"""These tests uses the `asgiref.testing.ApplicationCommunicator` to test the ASGI app itself
For more information see: https://dokk.org/documentation/django-channels/2.4.0/topics/testing/
"""

def setup_method(self):
self.registry = CollectorRegistry()
self.captured_status = None
self.captured_headers = None
# Setup ASGI scope
self.scope = {
"client": ("127.0.0.1", 32767),
"headers": [],
"http_version": "1.0",
"method": "GET",
"path": "/",
"query_string": b"",
"scheme": "http",
"server": ("127.0.0.1", 80),
"type": "http",
}
self.communicator = None

def teardown_method(self):
if self.communicator:
asyncio.get_event_loop().run_until_complete(self.communicator.wait())

def seed_app(self, app):
self.communicator = ApplicationCommunicator(app, self.scope)

@pytest.mark.parametrize(
"functions, expected",
"functions, expected_status, expected_body",
[
([lambda: True], "OK"),
([lambda: True, lambda: True], "OK"),
([lambda: False], "FAIL"),
([lambda: False, lambda: False], "FAIL"),
([lambda: False, lambda: True, lambda: True], "FAIL"),
([lambda: True], 200, b"OK"),
([lambda: True, lambda: True], 200, b"OK"),
([lambda: False], 503, b"FAIL"),
([lambda: False, lambda: False], 503, b"FAIL"),
([lambda: False, lambda: True, lambda: True], 503, b"FAIL"),
],
)
def test_health_check_returns_body(self, functions, expected):
exporter = PrometheusExporter(self.metrics_config)
exporter.run(daemon=False)
exporter.update_healthchecks(functions)
resp = requests.get("http://localhost:8000/health", timeout=0.5)
assert resp.content.decode() == expected
exporter.server.shut_down()
@pytest.mark.asyncio
async def test_asgi_app(self, functions, expected_status, expected_body):
app = make_patched_asgi_app(functions)
self.scope["path"] = "/health"
self.seed_app(app)
await self.communicator.send_input({"type": "http.request"})
event = await self.communicator.receive_output(timeout=1)
assert event["status"] == expected_status
event = await self.communicator.receive_output(timeout=1)
assert expected_body in event["body"]

0 comments on commit 34b65ec

Please sign in to comment.