"""端到端测试:CodingAgent 自主完成图片拼接工具的编写、测试和注册 运行方式: uv run python -m tests.test_e2e_stitcher 前置条件: - ANTHROPIC_API_KEY 已设置 - uv 已安装 """ import asyncio import json import logging import sys from pathlib import Path # 确保项目根目录在 sys.path 中 ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "src")) from tool_agent.config import settings from tool_agent.tool.agent import CodingAgent from tool_agent.registry.registry import ToolRegistry logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(name)s] %(levelname)s: %(message)s", ) logger = logging.getLogger("e2e_test") TOOL_ID = "image_stitcher" TASK_SPEC = f"""\ 请创建一个「图片拼接工具」,使用本地 uv 环境。 ## 功能需求 将多张图片按指定方式拼接成一张大图。 ## 输入参数(POST /stitch,JSON Body) - images: list[str] — Base64 编码的图片列表(至少 2 张) - direction: str — 拼接方向,可选 "horizontal" | "vertical" | "grid",默认 "horizontal" - columns: int — grid 模式下每行列数,默认 2 - spacing: int — 图片间距(像素),默认 0 - background_color: str — 间距填充色,默认 "#FFFFFF" - resize_mode: str — "none" 不缩放 | "fit_width" 统一宽度 | "fit_height" 统一高度,默认 "none" ## 输出(JSON) - image: str — 拼接结果,Base64 编码的 PNG - width: int — 结果图宽度 - height: int — 结果图高度 ## 技术要求 1. 使用 uv 环境,项目名 {TOOL_ID} 2. 核心依赖:Pillow 3. HTTP 接口:FastAPI + uvicorn,端口自选 4. 路由:POST /stitch(拼接)、GET /health(健康检查,返回 {{"status":"ok"}}) 5. 编写一个自测脚本 test_stitch.py:生成几张纯色小图 → 调用拼接函数 → 验证输出尺寸正确 6. 自测通过后,使用 register_tool 注册,tool_id = "{TOOL_ID}" ## 注意 - 这是 uv 本地项目,不需要 Docker - 先跑通自测脚本再注册,确保核心逻辑正确 """ async def run_test() -> bool: """执行端到端测试,返回是否通过""" logger.info("=" * 60) logger.info("E2E Test: Image Stitcher Tool") logger.info("=" * 60) # ---- 1. 执行任务 ---- agent = CodingAgent() logger.info("Sending task to CodingAgent...") result = await agent.execute(TASK_SPEC) logger.info("Agent finished. Result:") logger.info(result) # ---- 2. 验证注册 ---- logger.info("-" * 60) logger.info("Verifying registration...") registry = ToolRegistry() tool = registry.get(TOOL_ID) if not tool: logger.error(f"FAIL: tool '{TOOL_ID}' not found in registry") return False logger.info(f"OK: tool registered — {tool.tool_id} ({tool.name})") logger.info(f" runtime: {tool.runtime.type.value}") logger.info(f" status: {tool.status.value}") endpoint = registry.get_endpoint(TOOL_ID) if endpoint: logger.info(f" endpoint: {json.dumps(endpoint, ensure_ascii=False)}") else: logger.warning(" endpoint: None (may be expected for local stdio tool)") # ---- 3. 验证项目文件 ---- logger.info("-" * 60) logger.info("Verifying project files...") project_dir = settings.tools_dir / "local" / TOOL_ID checks = { "project dir exists": project_dir.is_dir(), "pyproject.toml": (project_dir / "pyproject.toml").exists(), } all_pass = True for label, ok in checks.items(): status = "OK" if ok else "FAIL" logger.info(f" {status}: {label}") if not ok: all_pass = False # 列出项目内文件 if project_dir.is_dir(): files = sorted(p.relative_to(project_dir) for p in project_dir.rglob("*") if p.is_file()) logger.info(f" project files ({len(files)}):") for f in files: logger.info(f" {f}") # ---- 4. 汇总 ---- logger.info("=" * 60) if all_pass and tool: logger.info("E2E TEST PASSED") else: logger.error("E2E TEST FAILED") return all_pass and tool is not None if __name__ == "__main__": ok = asyncio.run(run_test()) sys.exit(0 if ok else 1)