#!/usr/bin/env python3 """ Webhook 功能测试脚本 用于验证 Gitea Webhook Ambassador 的各项功能 """ import asyncio import json import httpx import time from datetime import datetime # 测试配置 BASE_URL = "http://localhost:8000" WEBHOOK_SECRET = "your-secret-key-here-make-it-long-and-random" # 测试数据 TEST_WEBHOOK_DATA = { "ref": "refs/heads/dev", "before": "abc1234567890abcdef1234567890abcdef123456", "after": "def1234567890abcdef1234567890abcdef123456", "compare_url": "https://gitea.freeleaps.com/freeleaps/test-project/compare/abc123...def123", "commits": [ { "id": "def1234567890abcdef1234567890abcdef123456", "message": "feat: add new feature", "url": "https://gitea.freeleaps.com/freeleaps/test-project/commit/def1234567890abcdef1234567890abcdef123456", "author": { "id": 1, "login": "developer", "full_name": "Test Developer", "email": "dev@freeleaps.com" } } ], "repository": { "id": 1, "name": "test-project", "owner": { "id": 1, "login": "freeleaps", "full_name": "Freeleaps Team", "email": "team@freeleaps.com" }, "full_name": "freeleaps/test-project", "private": False, "clone_url": "https://gitea.freeleaps.com/freeleaps/test-project.git", "ssh_url": "git@gitea.freeleaps.com:freeleaps/test-project.git", "html_url": "https://gitea.freeleaps.com/freeleaps/test-project", "default_branch": "main" }, "pusher": { "id": 1, "login": "developer", "full_name": "Test Developer", "email": "dev@freeleaps.com" } } async def test_health_check(): """测试健康检查""" print("🔍 测试健康检查...") async with httpx.AsyncClient() as client: try: response = await client.get(f"{BASE_URL}/health") if response.status_code == 200: data = response.json() print(f"✅ 健康检查通过: {data['status']}") return True else: print(f"❌ 健康检查失败: {response.status_code}") return False except Exception as e: print(f"❌ 健康检查异常: {e}") return False async def test_queue_status(): """测试队列状态""" print("🔍 测试队列状态...") async with httpx.AsyncClient() as client: try: response = await client.get(f"{BASE_URL}/health/queue") if response.status_code == 200: data = response.json() print(f"✅ 队列状态: {data['queue_stats']}") return True else: print(f"❌ 队列状态检查失败: {response.status_code}") return False except Exception as e: print(f"❌ 队列状态检查异常: {e}") return False async def test_webhook_endpoint(): """测试 Webhook 端点""" print("🔍 测试 Webhook 端点...") headers = { "Content-Type": "application/json", "X-Gitea-Signature": WEBHOOK_SECRET } async with httpx.AsyncClient() as client: try: response = await client.post( f"{BASE_URL}/webhook/gitea", headers=headers, json=TEST_WEBHOOK_DATA ) print(f"📊 响应状态: {response.status_code}") print(f"📊 响应内容: {response.text}") if response.status_code in [200, 202]: print("✅ Webhook 端点测试通过") return True else: print(f"❌ Webhook 端点测试失败: {response.status_code}") return False except Exception as e: print(f"❌ Webhook 端点测试异常: {e}") return False async def test_metrics_endpoint(): """测试监控指标端点""" print("🔍 测试监控指标端点...") async with httpx.AsyncClient() as client: try: response = await client.get(f"{BASE_URL}/metrics") if response.status_code == 200: print("✅ 监控指标端点测试通过") # 打印一些关键指标 content = response.text for line in content.split('\n'): if 'webhook_requests_total' in line or 'queue_size' in line: print(f"📊 {line}") return True else: print(f"❌ 监控指标端点测试失败: {response.status_code}") return False except Exception as e: print(f"❌ 监控指标端点测试异常: {e}") return False async def test_deduplication(): """测试防抖功能""" print("🔍 测试防抖功能...") headers = { "Content-Type": "application/json", "X-Gitea-Signature": WEBHOOK_SECRET } async with httpx.AsyncClient() as client: try: # 第一次请求 print("📤 发送第一次请求...") response1 = await client.post( f"{BASE_URL}/webhook/gitea", headers=headers, json=TEST_WEBHOOK_DATA ) print(f"📊 第一次响应: {response1.status_code}") # 等待一秒 await asyncio.sleep(1) # 第二次请求(相同数据,应该被防抖) print("📤 发送第二次请求(相同数据)...") response2 = await client.post( f"{BASE_URL}/webhook/gitea", headers=headers, json=TEST_WEBHOOK_DATA ) print(f"📊 第二次响应: {response2.status_code}") # 修改提交哈希,发送第三次请求 modified_data = TEST_WEBHOOK_DATA.copy() modified_data["after"] = "ghi1234567890abcdef1234567890abcdef123456" print("📤 发送第三次请求(不同提交哈希)...") response3 = await client.post( f"{BASE_URL}/webhook/gitea", headers=headers, json=modified_data ) print(f"📊 第三次响应: {response3.status_code}") print("✅ 防抖功能测试完成") return True except Exception as e: print(f"❌ 防抖功能测试异常: {e}") return False async def test_invalid_webhook(): """测试无效的 Webhook 请求""" print("🔍 测试无效的 Webhook 请求...") async with httpx.AsyncClient() as client: try: # 测试缺少签名 print("📤 测试缺少签名...") response1 = await client.post( f"{BASE_URL}/webhook/gitea", headers={"Content-Type": "application/json"}, json=TEST_WEBHOOK_DATA ) print(f"📊 缺少签名响应: {response1.status_code}") # 测试错误的签名 print("📤 测试错误的签名...") response2 = await client.post( f"{BASE_URL}/webhook/gitea", headers={ "Content-Type": "application/json", "X-Gitea-Signature": "wrong-secret" }, json=TEST_WEBHOOK_DATA ) print(f"📊 错误签名响应: {response2.status_code}") # 测试无效的 JSON print("📤 测试无效的 JSON...") response3 = await client.post( f"{BASE_URL}/webhook/gitea", headers={ "Content-Type": "application/json", "X-Gitea-Signature": WEBHOOK_SECRET }, content="invalid json" ) print(f"📊 无效 JSON 响应: {response3.status_code}") print("✅ 无效请求测试完成") return True except Exception as e: print(f"❌ 无效请求测试异常: {e}") return False async def main(): """主测试函数""" print("🚀 开始 Gitea Webhook Ambassador 功能测试") print("=" * 50) tests = [ ("健康检查", test_health_check), ("队列状态", test_queue_status), ("Webhook 端点", test_webhook_endpoint), ("监控指标", test_metrics_endpoint), ("防抖功能", test_deduplication), ("无效请求", test_invalid_webhook), ] results = [] for test_name, test_func in tests: print(f"\n🧪 {test_name}") print("-" * 30) try: result = await test_func() results.append((test_name, result)) except Exception as e: print(f"❌ {test_name} 测试异常: {e}") results.append((test_name, False)) # 等待一下再进行下一个测试 await asyncio.sleep(1) # 输出测试结果 print("\n" + "=" * 50) print("📊 测试结果汇总") print("=" * 50) passed = 0 total = len(results) for test_name, result in results: status = "✅ 通过" if result else "❌ 失败" print(f"{test_name}: {status}") if result: passed += 1 print(f"\n📈 总体结果: {passed}/{total} 测试通过") if passed == total: print("🎉 所有测试通过!服务运行正常。") else: print("⚠️ 部分测试失败,请检查服务配置和日志。") if __name__ == "__main__": # 运行测试 asyncio.run(main())