Files
zhuangang/topics/ai/langgraph/examples/ticket-flow/test_ticket_flow.py
T

52 lines
1.9 KiB
Python

# requires: langgraph, pydantic, pytest
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command
from ticket_flow import FakeLLM, Mailer, build
def run(text, llm=None, mailer=None, tid="t"):
llm, mailer = llm or FakeLLM(), mailer or Mailer()
app = build(llm, mailer, InMemorySaver())
cfg = {"configurable": {"thread_id": tid}}
out = app.invoke({"ticket_id": tid, "text": text, "status": "new", "events": []}, cfg)
return app, cfg, out, llm, mailer
def test_small_refund_sent_without_approval():
_, _, out, _, mailer = run("申请退款 30 元")
assert out["status"] == "sent" and "t" in mailer.sent
def test_large_refund_waits_then_rejected():
app, cfg, _, _, mailer = run("申请退款 900 元")
assert app.get_state(cfg).next == ("approve",)
out = app.invoke(Command(resume={"ok": False, "note": "超过权限"}), cfg)
assert out["status"] == "rejected" and not mailer.sent
def test_transient_error_retried():
_, _, out, llm, _ = run("系统报错 500", llm=FakeLLM(fail_times=2))
assert out["status"] == "sent" and llm.calls == 3
def test_retry_exhausted_raises_and_can_resume():
llm = FakeLLM(fail_times=3)
app = build(llm, Mailer(), InMemorySaver())
cfg = {"configurable": {"thread_id": "x"}}
try:
app.invoke({"ticket_id": "x", "text": "怎么开发票", "status": "new", "events": []}, cfg)
raise AssertionError("应该抛出异常")
except Exception as e:
assert "超时" in str(e)
assert app.get_state(cfg).next == ("draft",) # 停在失败的节点,分类结果保留
out = app.invoke(None, cfg) # 故障恢复后续跑
assert out["status"] == "sent" and out["events"].count("classified:question") == 1
def test_send_is_idempotent():
mailer = Mailer()
mailer.send("a", "x")
mailer.send("a", "y")
assert mailer.sent == {"a": "x"}