Skip to content

Commit 3bc6c62

Browse files
tarilabsclaude
andauthored
ci: test-e2e reworked to use TestPypi (#105)
* ci: test-e2e reworked to use TestPypi Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: tarilabs <matteo.mortari@gmail.com> * chore: linting Signed-off-by: tarilabs <matteo.mortari@gmail.com> * chore: impl code review feedback Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: tarilabs <matteo.mortari@gmail.com> * chore: impl review feedback Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: tarilabs <matteo.mortari@gmail.com> --------- Signed-off-by: tarilabs <matteo.mortari@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent ba08ff0 commit 3bc6c62

File tree

6 files changed

+68
-101
lines changed

6 files changed

+68
-101
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,9 @@ jobs:
4949
run: uv run mypy src/evalhub
5050

5151
e2e:
52-
name: "Optional: E2E Tests (latest_eval_hub_server=${{ matrix.latest_eval_hub_server }})"
52+
name: "Optional: E2E Tests"
5353
runs-on: ubuntu-latest
5454
continue-on-error: true
55-
strategy:
56-
matrix:
57-
latest_eval_hub_server: [true, false]
5855
steps:
5956
- uses: actions/checkout@v4
6057
- name: Set up uv
@@ -66,13 +63,6 @@ jobs:
6663
run: uv python install ${{ env.MIN_PY_VER }}
6764
- name: Install dependencies
6865
run: uv sync --all-extras
69-
- name: Fetch latest eval-hub server from eval-hub repo workflow
70-
if: matrix.latest_eval_hub_server == true
71-
env:
72-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
73-
run: |
74-
uv run ./scripts/fetch_latest_eval_hub_server.py
75-
tree .venv/lib/python${{ env.MIN_PY_VER }}/site-packages/evalhub_server
7666
- name: Create kind cluster
7767
uses: helm/kind-action@v1
7868
- name: Deploy distribution registry

Makefile

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ test:
88

99
.PHONY: test-e2e
1010
test-e2e:
11-
@echo "*** WARN: Running E2E with uv run --no-sync so not to override any replacement for eval-hub-server ***"
12-
uv run --no-sync uv pip show eval-hub-server
13-
uv run --no-sync pytest --e2e -s -x --color=yes -ra
11+
uv run uv pip show eval-hub-server
12+
uv run pytest --e2e --e2e-debug -s -x --color=yes -ra
1413

1514
.PHONY: ruff
1615
ruff:

pyproject.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ tag_format = "v$version"
194194

195195
[dependency-groups]
196196
dev = [
197-
"eval-hub-server @ git+https://github.com/eval-hub/eval-hub#subdirectory=python-server", # TODO: this should be moved to a pypi release
197+
"eval-hub-server",
198198
"fastapi>=0.124.4",
199199
"httpx>=0.28.1",
200200
"mypy>=1.19.1",
@@ -209,3 +209,11 @@ dev = [
209209
"types-pyyaml>=6.0.12.20250915",
210210
"uvicorn>=0.38.0",
211211
]
212+
213+
[[tool.uv.index]]
214+
name = "testpypi"
215+
url = "https://test.pypi.org/simple/"
216+
explicit = true # uv will only use TestPyPI for packages explicitly configured to use it
217+
218+
[tool.uv.sources]
219+
eval-hub-server = { index = "testpypi" }

tests/conftest.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Pytest configuration for eval-hub-sdk tests."""
22

3+
import logging
34
from typing import Any
45

56
import pytest
@@ -13,13 +14,26 @@ def pytest_addoption(parser: Any) -> None:
1314
default=False,
1415
help="Run only E2E tests",
1516
)
17+
parser.addoption(
18+
"--e2e-debug",
19+
action="store_true",
20+
default=False,
21+
help="Enable DEBUG logging for E2E test fixtures",
22+
)
1623

1724

1825
def pytest_configure(config: Any) -> None:
19-
"""Register custom markers."""
26+
"""Register custom markers and configure logging."""
2027
config.addinivalue_line(
2128
"markers", "e2e: mark test as end-to-end test (run with --e2e flag)"
2229
)
30+
if config.getoption("--e2e-debug", default=False):
31+
e2e_logger = logging.getLogger("tests.e2e.conftest")
32+
e2e_logger.setLevel(logging.DEBUG)
33+
handler = logging.StreamHandler()
34+
handler.setLevel(logging.DEBUG)
35+
handler.setFormatter(logging.Formatter("%(name)s %(levelname)s: %(message)s"))
36+
e2e_logger.addHandler(handler)
2337

2438

2539
def pytest_collection_modifyitems(config: Any, items: list[Any]) -> None:

tests/e2e/conftest.py

Lines changed: 31 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Shared fixtures and utilities for E2E tests."""
22

3+
import logging
34
import platform
45
import shutil
56
import subprocess
@@ -11,6 +12,8 @@
1112
import httpx
1213
import pytest
1314

15+
logger = logging.getLogger(__name__)
16+
1417

1518
def _kill_process_on_port(port: int) -> bool:
1619
"""
@@ -46,67 +49,12 @@ def _kill_process_on_port(port: int) -> bool:
4649
return False
4750

4851

49-
def _run_server(working_dir: str) -> None:
50-
"""
51-
Run the eval-hub server binary in the specified working directory.
52-
53-
This function is intended to be used as a target for multiprocessing.Process.
54-
55-
Args:
56-
working_dir: Directory containing the config subdirectory
57-
"""
58-
from evalhub_server import get_binary_path
59-
60-
binary_path = get_binary_path()
61-
subprocess.run([binary_path], cwd=working_dir, check=False)
62-
63-
6452
def _ensure_server_binary() -> bool:
65-
"""
66-
TODO: this should be REMOVED when eval-hub-server is moved to a pypi release
67-
TODO: this is temporary until eval-hub-server is release'd on Pypi because we need the binary(ies)
68-
"""
6953
try:
7054
from evalhub_server import get_binary_path
7155

72-
# Check if binary already exists
73-
try:
74-
binary_path = get_binary_path()
75-
return Path(binary_path).exists()
76-
except FileNotFoundError:
77-
pass
78-
79-
# Try to copy from local eval-hub repo
80-
system = platform.system().lower()
81-
machine = platform.machine().lower()
82-
83-
if system == "darwin":
84-
binary_name = (
85-
f"eval-hub-darwin-{'arm64' if machine == 'arm64' else 'amd64'}"
86-
)
87-
elif system == "linux":
88-
binary_name = f"eval-hub-linux-{'arm64' if 'aarch64' in machine or 'arm64' in machine else 'amd64'}"
89-
else:
90-
return False
91-
92-
# Look for eval-hub repo (assume it's a sibling directory)
93-
eval_hub_repo = Path(__file__).parent.parent.parent.parent / "eval-hub"
94-
binary_source = eval_hub_repo / "bin" / binary_name
95-
96-
if binary_source.exists():
97-
# Copy to evalhub_server package
98-
import evalhub_server
99-
100-
pkg_dir = Path(evalhub_server.__file__).parent
101-
binaries_dir = pkg_dir / "binaries"
102-
binaries_dir.mkdir(exist_ok=True)
103-
104-
binary_dest = binaries_dir / binary_name
105-
shutil.copy2(binary_source, binary_dest)
106-
binary_dest.chmod(0o755)
107-
return True
108-
109-
return False
56+
binary_path = get_binary_path()
57+
return Path(binary_path).exists()
11058
except Exception:
11159
return False
11260

@@ -147,29 +95,33 @@ def evalhub_server_with_real_config() -> Generator[str, None, None]:
14795
"Please ensure the config directory is properly set up."
14896
)
14997

150-
# Create temporary directory for server files
151-
with tempfile.TemporaryDirectory() as tmpdir:
98+
# Create temporary directory for server files (preserved after run for debugging of server logfiles, etc)
99+
tmpdir = tempfile.mkdtemp(prefix="evalhub-e2e-")
100+
server_process = None
101+
try:
102+
logger.debug(f"\nTemp directory for this run: {tmpdir}")
152103
# Copy entire config directory to temp location (including providers subdirectory)
153104
config_dir = Path(tmpdir) / "config"
154105
shutil.copytree(config_source_dir, config_dir)
155106

156107
# Debug: print directory structure
157-
print("\n\n===== SERVER DIRECTORY STRUCTURE =====")
158-
print(f"Working dir will be: {tmpdir}")
159-
for item in sorted(Path(tmpdir).rglob("*")):
160-
rel = item.relative_to(tmpdir)
161-
print(f" {rel}{'/' if item.is_dir() else ''}")
162-
print("=" * 50)
108+
dir_listing = "\n".join(
109+
f" {item.relative_to(tmpdir)}{'/' if item.is_dir() else ''}"
110+
for item in sorted(Path(tmpdir).rglob("*"))
111+
)
112+
logger.debug(
113+
"Server directory structure (working dir: %s):\n%s", tmpdir, dir_listing
114+
)
163115

164116
# Create log file for server output
165117
log_file = Path(tmpdir) / "server.log"
166118

167119
# Kill any process already using port 8080
168120
port = 8080
169121
if _kill_process_on_port(port):
170-
print(f"\n⚠️ WARNING: Killed existing process on port {port}")
171-
print(
172-
" (This is normal if a previous test run didn't clean up properly)\n"
122+
logger.warning(
123+
"Killed existing process on port %d (normal if a previous test run didn't clean up properly)",
124+
port,
173125
)
174126
# Give the OS a moment to release the port
175127
time.sleep(0.5)
@@ -208,22 +160,19 @@ def evalhub_server_with_real_config() -> Generator[str, None, None]:
208160

209161
# Debug: Print server logs
210162
if log_file.exists():
211-
print("\n\n===== SERVER LOGS =====")
212163
with open(log_file) as f:
213164
logs = f.read()
214-
# Only print first 3000 chars to avoid flooding output
215-
if len(logs) > 3000:
216-
print(logs[:3000] + f"\n... ({len(logs) - 3000} more chars)")
217-
else:
218-
print(logs)
219-
print("=" * 50)
165+
if len(logs) > 3000:
166+
logs = logs[:3000] + f"\n... ({len(logs) - 3000} more chars)"
167+
logger.debug("Server log file: %s\n%s", log_file.resolve(), logs)
220168

221169
yield base_url
222-
170+
finally:
223171
# Cleanup: terminate the server subprocess
224-
try:
225-
server_process.terminate()
226-
server_process.wait(timeout=5)
227-
except subprocess.TimeoutExpired:
228-
server_process.kill()
229-
server_process.wait()
172+
if server_process is not None:
173+
try:
174+
server_process.terminate()
175+
server_process.wait(timeout=5)
176+
except subprocess.TimeoutExpired:
177+
server_process.kill()
178+
server_process.wait()

uv.lock

Lines changed: 10 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)