fix: inttest runner crash and minio bucket-init proxy issue

- Remove --profiling-output arg from runner.yaml (plugin uses default path)
- Inline profiling hooks in root conftest.py with graceful fallback
- Replace mc-based bucket-init with Python urllib (no proxy interference)
- Add explicit ProxyHandler({}) to guarantee no proxy usage in bucket-init
This commit is contained in:
Celes Renata
2026-04-19 19:15:20 +00:00
parent ed6c0a2ade
commit b2b8aca7c6
5 changed files with 120 additions and 41 deletions
+66 -2
View File
@@ -1,2 +1,66 @@
# Root conftest — pytest_plugins must be declared at the top level. # Root conftest — profiling hooks for integration tests.
pytest_plugins = ["tests.integration.conftest_profiling"] #
# Registers --profiling-output CLI option and writes a JSON timing report
# after the test session completes. The profiler fixture itself is defined
# in tests/integration/conftest.py (always available as a fallback) and
# optionally overridden by this plugin when it loads successfully.
from __future__ import annotations
import pytest
_profiler_instance = None
_DEFAULT_OUTPUT = "/tmp/profiling-report.json"
def pytest_addoption(parser: pytest.Parser) -> None:
"""Add --profiling-output CLI flag."""
parser.addoption(
"--profiling-output",
action="store",
default=_DEFAULT_OUTPUT,
help=f"Path for the JSON profiling report (default: {_DEFAULT_OUTPUT})",
)
@pytest.fixture(scope="session")
def profiler():
"""Session-scoped EndpointProfiler instance."""
global _profiler_instance # noqa: PLW0603
try:
from tests.integration.profiler import EndpointProfiler
_profiler_instance = EndpointProfiler()
except ImportError:
# If profiler module unavailable, return a no-op object
from unittest.mock import MagicMock
_profiler_instance = MagicMock()
return _profiler_instance
def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None:
"""Write profiling JSON report after all tests complete."""
if _profiler_instance is None:
return
output_path = session.config.getoption("profiling_output", _DEFAULT_OUTPUT)
try:
if hasattr(_profiler_instance, "write_json"):
_profiler_instance.write_json(output_path)
except (OSError, TypeError):
pass
def pytest_terminal_summary(
terminalreporter: pytest.TerminalReporter,
exitstatus: int,
config: pytest.Config,
) -> None:
"""Print profiling summary at end of test session."""
if _profiler_instance is None:
return
output_path = config.getoption("profiling_output", _DEFAULT_OUTPUT)
try:
if hasattr(_profiler_instance, "print_summary"):
terminalreporter.section("Profiling Summary")
_profiler_instance.print_summary()
terminalreporter.write_line(f"JSON report written to: {output_path}")
except (OSError, TypeError):
pass
+45 -13
View File
@@ -129,8 +129,8 @@ spec:
type: RuntimeDefault type: RuntimeDefault
restartPolicy: OnFailure restartPolicy: OnFailure
containers: containers:
- name: mc - name: bucket-init
image: minio/mc:latest image: registry.celestium.life/dockerhub-cache/library/python:3.12-slim
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
@@ -143,23 +143,55 @@ spec:
limits: limits:
cpu: 250m cpu: 250m
memory: 128Mi memory: 128Mi
command: ["/bin/sh", "-c"]
env: env:
- name: HTTP_PROXY - name: HTTP_PROXY
value: "" value: ""
- name: HTTPS_PROXY - name: HTTPS_PROXY
value: "" value: ""
- name: http_proxy
value: ""
- name: https_proxy
value: ""
- name: NO_PROXY - name: NO_PROXY
value: "minio,.local,10.0.0.0/8,192.168.0.0/16" value: "*"
- name: no_proxy - name: no_proxy
value: "minio,.local,10.0.0.0/8,192.168.0.0/16" value: "*"
command: ["python", "-c"]
args: args:
- | - |
echo "Waiting for MinIO to be ready..." import urllib.request, urllib.error, time
until mc alias set sandbox http://minio:9000 minioadmin minioadmin 2>/dev/null; do
echo "MinIO not ready, retrying in 2s..." # Disable all proxy usage in urllib
sleep 2 no_proxy_handler = urllib.request.ProxyHandler({})
done opener = urllib.request.build_opener(no_proxy_handler)
echo "MinIO is ready. Creating bucket..." urllib.request.install_opener(opener)
mc mb --ignore-existing sandbox/stonks-normalized
echo "Bucket stonks-normalized created successfully." endpoint = "http://minio:9000"
max_wait = 60
# Wait for MinIO readiness
print("Waiting for MinIO to be ready...")
start = time.time()
while time.time() - start < max_wait:
try:
r = urllib.request.urlopen(f"{endpoint}/minio/health/ready", timeout=3)
if r.status == 200:
print("MinIO is ready.")
break
except Exception:
pass
time.sleep(2)
else:
raise SystemExit("MinIO did not become ready within 60s")
# Create bucket via S3 PUT request
bucket = "stonks-normalized"
req = urllib.request.Request(f"{endpoint}/{bucket}", method="PUT")
try:
urllib.request.urlopen(req, timeout=5)
print(f"Bucket '{bucket}' created successfully.")
except urllib.error.HTTPError as e:
if e.code == 409:
print(f"Bucket '{bucket}' already exists.")
else:
raise
-1
View File
@@ -49,7 +49,6 @@ spec:
- "-v" - "-v"
- "--tb=short" - "--tb=short"
- "--junitxml=/tmp/results.xml" - "--junitxml=/tmp/results.xml"
- "--profiling-output=/tmp/profiling-report.json"
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
readOnlyRootFilesystem: true readOnlyRootFilesystem: true
+5 -7
View File
@@ -16,6 +16,7 @@ from typing import Any
import httpx import httpx
import pytest import pytest
import pytest_asyncio
from tests.integration.profiler import EndpointProfiler from tests.integration.profiler import EndpointProfiler
from tests.integration.seed_sandbox import ( from tests.integration.seed_sandbox import (
@@ -34,9 +35,6 @@ from tests.integration.seed_sandbox import (
SEED_VARIANT_IDS, SEED_VARIANT_IDS,
) )
# Profiling plugin loaded via root conftest.py (pytest_plugins must be top-level)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# ProfiledAsyncClient — transparent timing wrapper # ProfiledAsyncClient — transparent timing wrapper
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -129,7 +127,7 @@ def trading_api_url() -> str:
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@pytest.fixture @pytest_asyncio.fixture
async def query_client( async def query_client(
query_api_url: str, profiler: EndpointProfiler, query_api_url: str, profiler: EndpointProfiler,
) -> ProfiledAsyncClient: ) -> ProfiledAsyncClient:
@@ -138,7 +136,7 @@ async def query_client(
yield ProfiledAsyncClient(client, profiler) yield ProfiledAsyncClient(client, profiler)
@pytest.fixture @pytest_asyncio.fixture
async def registry_client( async def registry_client(
registry_api_url: str, profiler: EndpointProfiler, registry_api_url: str, profiler: EndpointProfiler,
) -> ProfiledAsyncClient: ) -> ProfiledAsyncClient:
@@ -147,7 +145,7 @@ async def registry_client(
yield ProfiledAsyncClient(client, profiler) yield ProfiledAsyncClient(client, profiler)
@pytest.fixture @pytest_asyncio.fixture
async def risk_client( async def risk_client(
risk_api_url: str, profiler: EndpointProfiler, risk_api_url: str, profiler: EndpointProfiler,
) -> ProfiledAsyncClient: ) -> ProfiledAsyncClient:
@@ -156,7 +154,7 @@ async def risk_client(
yield ProfiledAsyncClient(client, profiler) yield ProfiledAsyncClient(client, profiler)
@pytest.fixture @pytest_asyncio.fixture
async def trading_client( async def trading_client(
trading_api_url: str, profiler: EndpointProfiler, trading_api_url: str, profiler: EndpointProfiler,
) -> ProfiledAsyncClient: ) -> ProfiledAsyncClient:
+4 -18
View File
@@ -16,27 +16,13 @@ import pytest
from tests.integration.profiler import EndpointProfiler from tests.integration.profiler import EndpointProfiler
# ---------------------------------------------------------------------------
# CLI option — registered by root conftest.py to ensure availability
# ---------------------------------------------------------------------------
DEFAULT_PROFILING_OUTPUT = "/tmp/profiling-report.json" DEFAULT_PROFILING_OUTPUT = "/tmp/profiling-report.json"
# ---------------------------------------------------------------------------
# CLI option
# ---------------------------------------------------------------------------
def pytest_addoption(parser: pytest.Parser) -> None:
"""Add ``--profiling-output`` CLI flag to pytest."""
parser.addoption(
"--profiling-output",
action="store",
default=DEFAULT_PROFILING_OUTPUT,
help=(
"Path for the JSON profiling report "
f"(default: {DEFAULT_PROFILING_OUTPUT})"
),
)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Session-scoped profiler instance (shared across all tests) # Session-scoped profiler instance (shared across all tests)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------