Skip to content

Commit 5839f2b

Browse files
committed
Add HTTP proxy server fixture for integration tests
Introduces a pytest fixture to start a local HTTP proxy using proxy.py for tests requiring proxy support. Updates the native upload integration test to use the new fixture, improving reliability and isolation of proxy-dependent tests.
1 parent f7e4b2b commit 5839f2b

2 files changed

Lines changed: 86 additions & 4 deletions

File tree

tests/conftest.py

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import os
2-
import pytest
3-
import httpx
42
import random
3+
import signal
4+
import socket
5+
import subprocess
6+
import sys
7+
import time
8+
9+
import httpx
10+
import pytest
511

612

713
@pytest.fixture
@@ -39,7 +45,7 @@ def create_dataset(
3945
response = httpx.post(
4046
url=url,
4147
headers={"X-Dataverse-key": api_token},
42-
data=open("./tests/fixtures/create_dataset.json", "rb"),
48+
data=open("./tests/fixtures/create_dataset.json", "rb"), # type: ignore[reportUnboundVariable]
4349
)
4450

4551
response.raise_for_status()
@@ -99,3 +105,78 @@ def create_mock_tabular_file(
99105
)
100106

101107
return path
108+
109+
110+
def _wait_for_port(host: str, port: int, timeout: float = 5.0) -> None:
111+
"""Wait until a TCP port is open on host within timeout seconds."""
112+
start = time.time()
113+
while time.time() - start < timeout:
114+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
115+
sock.settimeout(0.2)
116+
try:
117+
if sock.connect_ex((host, port)) == 0:
118+
return
119+
except OSError:
120+
pass
121+
time.sleep(0.1)
122+
raise TimeoutError(f"Proxy did not start on {host}:{port} within {timeout}s")
123+
124+
125+
@pytest.fixture(scope="function")
126+
def http_proxy_server():
127+
"""Start a local HTTP proxy on 127.0.0.1:3128 for tests that require it."""
128+
host = "127.0.0.1"
129+
port = 3128
130+
131+
# Ensure dependency is available
132+
try:
133+
import proxy # noqa: F401
134+
except Exception as exc: # pragma: no cover
135+
pytest.skip(
136+
f"Skipping: proxy module not available ({exc}). Install 'proxy.py'."
137+
)
138+
139+
# Launch proxy.py as a subprocess to avoid API instability between versions
140+
cmd = [
141+
sys.executable,
142+
"-m",
143+
"proxy",
144+
"--hostname",
145+
host,
146+
"--port",
147+
str(port),
148+
"--num-workers",
149+
"1",
150+
"--log-level",
151+
"WARNING",
152+
]
153+
154+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
155+
156+
try:
157+
try:
158+
_wait_for_port(host, port, timeout=10.0)
159+
except TimeoutError:
160+
# Collect logs for debugging and skip the test instead of failing hard
161+
try:
162+
stdout, stderr = proc.communicate(timeout=1)
163+
except Exception:
164+
stdout, stderr = (b"", b"")
165+
msg = (
166+
"Proxy did not start on "
167+
f"{host}:{port}. stderr: {stderr.decode(errors='ignore').strip()}"
168+
)
169+
pytest.skip(msg)
170+
return
171+
172+
yield f"http://{host}:{port}"
173+
finally:
174+
if proc.poll() is None:
175+
try:
176+
proc.send_signal(signal.SIGTERM)
177+
try:
178+
proc.wait(timeout=5)
179+
except subprocess.TimeoutExpired:
180+
proc.kill()
181+
except Exception:
182+
proc.kill()

tests/integration/test_native_upload.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,10 @@ def test_forced_native_upload(
110110
def test_native_upload_with_proxy(
111111
self,
112112
credentials,
113+
http_proxy_server,
113114
):
114115
BASE_URL, API_TOKEN = credentials
115-
proxy = "http://127.0.0.1:3128"
116+
proxy = http_proxy_server
116117

117118
with tempfile.TemporaryDirectory() as directory:
118119
# Arrange

0 commit comments

Comments
 (0)