""" 天目 pytest 配置。 提供服务启停夹具,方便运行集成测试。 """ from __future__ import annotations import json import os import subprocess import time import urllib.request from pathlib import Path import pytest API_BASE = os.environ.get("TIANMU_API_BASE", "http://localhost:8101") TIANMU_DIR = Path(__file__).resolve().parent.parent def _wait_for_service(url: str, timeout: float = 15.0, interval: float = 0.5) -> bool: """轮询等待服务就绪。 Args: url: 健康检查 URL。 timeout: 最长等待秒数。 interval: 轮询间隔秒数。 Returns: True 表示服务就绪,False 表示超时。 """ start = time.monotonic() while time.monotonic() - start < timeout: try: req = urllib.request.Request(url, method="GET") with urllib.request.urlopen(req, timeout=2) as resp: if resp.status == 200: return True except (urllib.error.URLError, ConnectionError, OSError): pass time.sleep(interval) return False @pytest.fixture(scope="session") def tianmu_server(): """启动天目服务(仅一次,整个 session 共用)。 如果环境变量 TIANMU_API_BASE 已设置,跳过启动(连接外部服务)。 启动后轮询 /healthz,就绪后才继续运行测试。 """ if "TIANMU_API_BASE" in os.environ: yield return proc = subprocess.Popen( ["python3", "-m", "src.main"], cwd=str(TIANMU_DIR), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) ready = _wait_for_service(f"{API_BASE}/healthz") if not ready: proc.terminate() proc.wait() raise RuntimeError( f"天目服务启动超时({API_BASE}/healthz 15s 内未就绪)" ) yield proc.terminate() try: proc.wait(timeout=5) except subprocess.TimeoutExpired: proc.kill() proc.wait() @pytest.fixture def api_base() -> str: return API_BASE