#!/usr/bin/env python3
"""安全与错误处理测试"""
import sys
import os
import json
BASE_URL = "http://localhost:8001"
try:
import requests
except ImportError:
os.system("pip install requests -q")
import requests
def get_token():
r = requests.post(f"{BASE_URL}/api/auth/login", json={"username": "admin", "password": "admin123"})
return r.json()["token"] if r.status_code == 200 else None
def h(token):
return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
def check(name, condition, details=""):
icon = "✅" if condition else "❌"
print(f" {icon} {name}{' (' + details + ')' if details else ''}")
return condition
def section(name):
print(f"\n{'='*50}")
print(f" {name}")
print(f"{'='*50}")
def main():
print("=" * 60)
print("宇之然平台 - 安全与错误处理测试")
print("=" * 60)
results = []
section("🔐 认证安全")
r = requests.get(f"{BASE_URL}/api/topics")
results.append(check("无 token 请求被拒绝", r.status_code == 401 or r.status_code == 403, f"HTTP {r.status_code}"))
r = requests.get(f"{BASE_URL}/api/topics", headers={"Authorization": "Bearer invalid_token"})
results.append(check("无效 token 被拒绝", r.status_code == 401 or r.status_code == 403, f"HTTP {r.status_code}"))
r = requests.get(f"{BASE_URL}/api/topics", headers={"Authorization": ""})
results.append(check("空 token 被拒绝", r.status_code == 401 or r.status_code == 403, f"HTTP {r.status_code}"))
r = requests.post(f"{BASE_URL}/api/auth/login", json={"username": "admin", "password": "wrong"})
results.append(check("错误密码登录失败", r.status_code == 401, f"HTTP {r.status_code}"))
r = requests.post(f"{BASE_URL}/api/auth/login", json={"username": "nonexistent", "password": "test"})
results.append(check("不存在的用户登录失败", r.status_code == 401, f"HTTP {r.status_code}"))
section("🛡️ CORS 安全")
r = requests.get(f"{BASE_URL}/api/system/status", headers=h(get_token()))
cors_origin = r.headers.get("access-control-allow-origin", "")
results.append(check("CORS 不返回通配符 *", cors_origin != "*", f"Origin: {cors_origin or '(none)'}"))
section("⚠️ 错误处理")
r = requests.get(f"{BASE_URL}/api/nonexistent", headers=h(get_token()))
results.append(check("不存在路由返回 404", r.status_code == 404, f"HTTP {r.status_code}"))
r = requests.get(f"{BASE_URL}/api/topics/99999", headers=h(get_token()))
results.append(check("不存在资源返回 404", r.status_code == 404, f"HTTP {r.status_code}"))
r = requests.post(f"{BASE_URL}/api/topics", headers=h(get_token()), json={})
results.append(check("空数据创建返回验证错误", r.status_code == 422, f"HTTP {r.status_code}"))
r = requests.post(f"{BASE_URL}/api/topics", headers=h(get_token()), json={"title": ""})
results.append(check("空标题返回验证错误", r.status_code == 422, f"HTTP {r.status_code}"))
section("📄 内容安全")
token = get_token()
r = requests.post(f"{BASE_URL}/api/topics", headers=h(token), json={
"title": "测试",
"field_id": 1,
"content": ""
})
results.append(check("含 HTML 的内容可创建", r.ok, f"HTTP {r.status_code}"))
if r.ok:
new_id = r.json().get("id")
if new_id:
r2 = requests.delete(f"{BASE_URL}/api/topics/{new_id}", headers=h(token))
results.append(check("清理测试数据", r2.ok or r2.status_code == 404))
section("🔄 幂等性")
payload = {"title": "幂等测试", "field_id": 1}
r1 = requests.post(f"{BASE_URL}/api/topics", headers=h(token), json=payload)
r2 = requests.post(f"{BASE_URL}/api/topics", headers=h(token), json=payload)
results.append(check("重复创建不报错", r1.ok and r2.ok, f"第1次:{r1.status_code} 第2次:{r2.status_code}"))
if r1.ok:
del_id = r1.json().get("id")
if del_id:
requests.delete(f"{BASE_URL}/api/topics/{del_id}", headers=h(token))
if r2.ok and r2.json().get("id") != (r1.json().get("id") if r1.ok else None):
del_id2 = r2.json().get("id")
if del_id2:
requests.delete(f"{BASE_URL}/api/topics/{del_id2}", headers=h(token))
print(f"\n{'='*60}")
passed = sum(results)
total = len(results)
print(f"测试结果: {passed}/{total} 通过 ({passed*100//total}%)")
print("=" * 60)
return 0 if passed == total else 1
if __name__ == "__main__":
sys.exit(main())