"""临时 E2E:对比 → 采用 → 存档 → 回滚。跑完可删。""" from __future__ import annotations import json import sys import urllib.request # Windows 控制台避免 ✅ 等字符触发 GBK 编码错误 sys.stdout.reconfigure(encoding="utf-8", errors="replace") def chat(text: str, sid: str | None = None) -> list[dict]: body = json.dumps({"text": text, "sessionId": sid}).encode() req = urllib.request.Request( "http://localhost:8000/api/chat", data=body, headers={"Content-Type": "application/json"}, ) events: list[dict] = [] with urllib.request.urlopen(req, timeout=60) as resp: buf = "" for chunk in resp: buf += chunk.decode() while "\n\n" in buf: frame, buf = buf.split("\n\n", 1) if frame.startswith("data: "): events.append(json.loads(frame[6:])) return events def get(url: str) -> dict: with urllib.request.urlopen(url, timeout=30) as r: return json.loads(r.read()) def post(url: str, data: dict) -> dict: body = json.dumps(data).encode() req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"}) with urllib.request.urlopen(req, timeout=60) as r: return json.loads(r.read()) def main() -> None: # 1) compare ev = chat("对比几种策略", "e2e-m2") blocks = [e for e in ev if e.get("type") == "block"] assert any(b["block"]["type"] == "scenario-cards" for b in blocks), blocks cards = next(b["block"]["props"]["cards"] for b in blocks if b["block"]["type"] == "scenario-cards") print("compare OK", len(cards), "cards:", [c["label"] for c in cards]) tl0 = get("http://localhost:8000/api/timeline") v0 = len(tl0["versions"]) # 2) apply best best = min(cards, key=lambda c: (c["kpi"]["totalTardiness"], c["kpi"]["conflictCount"])) r = post( "http://localhost:8000/api/actions/scenario/apply", {"sessionId": "e2e-m2", "strategy": best["strategy"], "engine": best["engine"]}, ) print("apply OK", (r.get("message") or "")[:80]) tl1 = get("http://localhost:8000/api/timeline") assert len(tl1["versions"]) >= v0, (v0, len(tl1["versions"])) print("versions", v0, "->", len(tl1["versions"])) # 3) checkpoint create ev2 = chat("存个档 叫 e2e基线", "e2e-m2") text = "".join(e.get("text", "") for e in ev2 if e.get("type") == "token") print("checkpoint create:", text[:100]) tl2 = get("http://localhost:8000/api/timeline") assert tl2["checkpoints"], tl2 pair = tl2["checkpoints"][-1] print("pair", pair["pairId"], pair["label"]) # 4) rollback stage + approve ev3 = chat(f"回滚到检查点 {pair['pairId']}", "e2e-m2") blocks3 = [e for e in ev3 if e.get("type") == "block"] assert any(b["block"]["type"] == "confirm-card" for b in blocks3), blocks3 cid = next(b["block"]["props"]["confirmId"] for b in blocks3 if b["block"]["type"] == "confirm-card") print("rollback staged", cid) r2 = post( "http://localhost:8000/api/actions/confirm", {"sessionId": "e2e-m2", "confirmId": cid, "approve": True}, ) print("rollback OK", (r2.get("message") or "")[:100]) print("E2E M2 PASS") if __name__ == "__main__": main()