- 新增完整的 Python 实现,替代 Go 版本 - 添加 Web 登录界面和仪表板 - 实现 JWT 认证和 API 密钥管理 - 添加数据库存储功能 - 保持与 Go 版本一致的目录结构和启动脚本 - 包含完整的文档和测试脚本
307 lines
9.8 KiB
Python
Executable File
307 lines
9.8 KiB
Python
Executable File
#!/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()) |