Files

107 lines
3.1 KiB
Python

"""Pytest fixture for managing the pipeline-runner server lifecycle during tests."""
import json
import os
import shutil
import socket
import subprocess
import tempfile
import time
import pytest
def get_free_port():
"""Get a free port for the test server."""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", 0))
port = s.getsockname()[1]
s.close()
return port
class PipelineServer:
"""Manages a pipeline-runner server instance for tests."""
def __init__(self, port, work_dir, verbose=False, web_root=None):
self.port = port
self.work_dir = work_dir
self.verbose = verbose
self.web_root = web_root
test_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(test_dir)
self.bin_path = os.path.join(project_root, "build", "pipeline-runner")
config = {
"port": port,
"pipelines_dir": os.path.join(work_dir, "pipelines") + "/",
"logs_dir": os.path.join(work_dir, "logs") + "/",
"opencode_path": "/usr/bin/echo",
}
if web_root:
config["web_root"] = web_root
self.config_path = os.path.join(work_dir, "pipeline-runner.conf")
with open(self.config_path, "w") as f:
json.dump(config, f)
self.process = None
@property
def base_url(self):
return f"http://localhost:{self.port}"
def start(self, timeout=30):
"""Start the server and wait until it responds."""
self.process = subprocess.Popen(
[self.bin_path, self.config_path],
cwd=self.work_dir,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
deadline = time.time() + timeout
while time.time() < deadline:
try:
resp = __import__("requests").get(
f"{self.base_url}/api/pipelines", timeout=1
)
if resp.status_code == 200:
return True
except Exception:
pass
time.sleep(0.3)
return False
def stop(self):
"""Stop the server."""
if self.process:
self.process.terminate()
try:
self.process.wait(timeout=10)
except subprocess.TimeoutExpired:
self.process.kill()
self.process.wait(timeout=5)
# Close pipes to prevent resource leaks and zombie processes
if self.process.stdout:
self.process.stdout.close()
if self.process.stderr:
self.process.stderr.close()
@pytest.fixture(scope="session")
def pipeline_server():
"""Session-scoped fixture that starts and stops the pipeline-runner server."""
work_dir = tempfile.mkdtemp(prefix="pipeline-test-")
port = get_free_port()
server = PipelineServer(port, work_dir)
if not server.start():
pytest.fail("Could not start pipeline-runner server")
yield server
server.stop()
shutil.rmtree(work_dir, ignore_errors=True)