63a6fabc00
- collector.py, compliance_checker.py, trends.py: bare except -> specific - db_helper.py: datetime.now() -> timezone.utc (8 occurrences) - compliance_checker.py: regex \x08 -> \b word boundary + import json - search_providers.py: minor fixes - test_new_features.py: service reachability check retry Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
154 lines
5.9 KiB
Python
154 lines
5.9 KiB
Python
"""全面测试:新增功能验证"""
|
|
import uvicorn, time, threading, requests, json, os, sys
|
|
from pathlib import Path
|
|
|
|
# Setup
|
|
os.environ.setdefault("DATABASE_URL", "sqlite:///../../automation/data/test.db")
|
|
os.environ.setdefault("USE_POSTGRES", "false")
|
|
backend_dir = str(Path(__file__).parent.parent / "platform" / "backend")
|
|
sys.path.insert(0, backend_dir)
|
|
os.chdir(backend_dir)
|
|
|
|
results = {"pass": 0, "fail": 0}
|
|
|
|
def test(name, ok, detail=""):
|
|
status = chr(10003) if ok else chr(10007)
|
|
results["pass" if ok else "fail"] += 1
|
|
print(f" {status} {name}")
|
|
if detail:
|
|
print(f" {detail}")
|
|
|
|
# 1. Python compile check
|
|
print("=== 1. Python 编译检查 ===")
|
|
files = [
|
|
"platform/backend/app/models.py",
|
|
"platform/backend/app/api/collector_mgmt.py",
|
|
"platform/backend/app/core/nvidia_client.py",
|
|
"platform/backend/app/core/scheduler.py",
|
|
"platform/backend/app/initial_data.py",
|
|
"platform/backend/app/main.py",
|
|
"scripts/collector.py",
|
|
"scripts/db_helper.py",
|
|
]
|
|
root = Path(__file__).parent.parent
|
|
for f in files:
|
|
try:
|
|
src = (root / f).read_text()
|
|
compile(src, f, "exec")
|
|
test(f" {f}", True)
|
|
except SyntaxError as e:
|
|
test(f" {f}", False, str(e))
|
|
|
|
# 2. Start server
|
|
print("\n=== 2. API 服务启动 ===")
|
|
def start_server():
|
|
uvicorn.run("app.main:app", host="127.0.0.1", port=18503, log_level="error")
|
|
|
|
t = threading.Thread(target=start_server, daemon=True)
|
|
t.start()
|
|
base = "http://127.0.0.1:18503"
|
|
for _ in range(8):
|
|
time.sleep(1.5)
|
|
try:
|
|
r = requests.get(f"{base}/api/system/status", timeout=3)
|
|
test("服务可访问", True)
|
|
break
|
|
except Exception:
|
|
continue
|
|
else:
|
|
test("服务可访问", False, "Server did not start within 12s")
|
|
exit(1)
|
|
|
|
# 3. Login
|
|
print("\n=== 3. 登录认证 ===")
|
|
r = requests.post(f"{base}/api/auth/login", json={"username": "admin", "password": "admin123"}, timeout=5)
|
|
token = r.json().get("token", "") if r.status_code == 200 else ""
|
|
test("管理员登录", r.status_code == 200 and bool(token))
|
|
if not token:
|
|
exit(1)
|
|
auth = {"Authorization": f"Bearer {token}"}
|
|
|
|
# 4. System status
|
|
print("\n=== 4. 系统状态 ===")
|
|
r = requests.get(f"{base}/api/system/status", headers=auth, timeout=5)
|
|
test("GET /api/system/status", r.status_code == 200 and "stats" in r.json())
|
|
|
|
# 5. Collector categories CRUD
|
|
print("\n=== 5. 采集类别 CRUD ===")
|
|
r = requests.get(f"{base}/api/admin/collector/categories", headers=auth, timeout=5)
|
|
test("GET 类别列表", r.status_code == 200)
|
|
initial_count = len(r.json())
|
|
test(f"初始类别数>0", initial_count > 0, f"共{initial_count}条")
|
|
|
|
r = requests.post(f"{base}/api/admin/collector/categories", headers=auth,
|
|
json={"name": "新类别Z", "search_query": "新类别 2026", "sort_order": 99}, timeout=5)
|
|
test("POST 新增类别", r.status_code == 201)
|
|
new_id = r.json().get("id")
|
|
test("返回ID", bool(new_id))
|
|
|
|
r = requests.put(f"{base}/api/admin/collector/categories/{new_id}", headers=auth,
|
|
json={"description": "测试编辑"}, timeout=5)
|
|
test("PUT 编辑类别", r.status_code == 200 and r.json().get("description") == "测试编辑")
|
|
|
|
r = requests.delete(f"{base}/api/admin/collector/categories/{new_id}", headers=auth, timeout=5)
|
|
test("DELETE 删除类别", r.status_code == 200)
|
|
|
|
r = requests.get(f"{base}/api/admin/collector/categories", headers=auth, timeout=5)
|
|
test("删除后列表数恢复", len(r.json()) == initial_count)
|
|
|
|
# 6. Collector sources CRUD
|
|
print("\n=== 6. 信息源 CRUD ===")
|
|
r = requests.get(f"{base}/api/admin/collector/sources", headers=auth, timeout=5)
|
|
test("GET 源列表", r.status_code == 200)
|
|
src_count = len(r.json())
|
|
test(f"初始源数>0", src_count > 0, f"共{src_count}条")
|
|
|
|
r = requests.post(f"{base}/api/admin/collector/sources", headers=auth,
|
|
json={"name": "新源Z", "source_type": "web_search", "query": "新查询 2026"}, timeout=5)
|
|
test("POST 新增源", r.status_code == 201)
|
|
new_src_id = r.json().get("id")
|
|
|
|
r = requests.put(f"{base}/api/admin/collector/sources/{new_src_id}", headers=auth,
|
|
json={"focus": "测试领域"}, timeout=5)
|
|
test("PUT 编辑源", r.status_code == 200)
|
|
|
|
r = requests.delete(f"{base}/api/admin/collector/sources/{new_src_id}", headers=auth, timeout=5)
|
|
test("DELETE 删除源", r.status_code == 200)
|
|
|
|
# 7. LLM provider config
|
|
print("\n=== 7. LLM多供应商 ===")
|
|
sys.path.insert(0, str(root / "platform" / "backend"))
|
|
from app.core.nvidia_client import _get_active_provider, _get_provider_config
|
|
test("默认供应商存在", _get_active_provider() in ["opencode-go", "nvidia", "sensenova"])
|
|
cfg = _get_provider_config("opencode-go")
|
|
test("opencode-go已配置", cfg is not None)
|
|
cfg_nv = _get_provider_config("nvidia")
|
|
test("nvidia备用存在", cfg_nv is not None)
|
|
test("opencode-go模型非空", bool(cfg and cfg.get("model")))
|
|
test("opencode-go URL非空", bool(cfg and cfg.get("base_url")))
|
|
test("opencode-go Key非空", bool(cfg and cfg.get("api_key")))
|
|
|
|
# 8. Collector DB loading
|
|
print("\n=== 8. 采集器DB加载 ===")
|
|
sys.path.insert(0, str(root / "scripts"))
|
|
from collector import SustainabilityCollector
|
|
collector = SustainabilityCollector()
|
|
cats = collector.config.get("sustainability_categories", [])
|
|
test("从DB加载类别", len(cats) > 0, f"共{len(cats)}个类别: {cats}")
|
|
test("从DB加载源", len(collector.sources) > 0, f"共{len(collector.sources)}个源")
|
|
|
|
# 9. API auth protection
|
|
print("\n=== 9. API鉴权保护 ===")
|
|
r = requests.get(f"{base}/api/admin/collector/categories", timeout=5)
|
|
test("未认证访问被拒绝", r.status_code != 200, f"实际状态码: {r.status_code}")
|
|
r = requests.post(f"{base}/api/admin/collector/sources", timeout=5,
|
|
json={"name": "x", "source_type": "rss"})
|
|
test("未认证POST被拒绝", r.status_code != 200, f"实际状态码: {r.status_code}")
|
|
|
|
# Summary
|
|
total = results["pass"] + results["fail"]
|
|
print(f"\n{'='*40}")
|
|
print(f"总计: {results['pass']}/{total} 通过, {results['fail']} 失败")
|
|
if results["fail"] > 0:
|
|
exit(1)
|