Files
PlantAI-rag-new/tests/paper/test_paper_bff_agent.py
T
admin d37e0d9c3a fix(paper): 确认大纲后即时 seed 进度卡并逐节刷新
- 后端写稿循环不再落空 paper_progress 块(重载后出现空卡),改落开始撰写文本
- 前端 confirmPaper 时用大纲节标题本地 seed 进度卡,2s 轮询 patch 逐节状态
- 当前节按 current_section_id 标 writing(…动画)
2026-10-08 17:10:50 +08:00

125 lines
5.6 KiB
Python

"""tests/paper/test_paper_bff_agent.py —— BFF 论文大脑状态机测试(假 MCP server + 假 LLM)。
跑通 brief→outline→confirm(写节循环)→complete→artifact 全状态机。
"""
from __future__ import annotations
import pytest
from conftest import _run
from app import db as bff_db
from app.routers import docgen as docgen_mod
from app.services.paper import agent
class FakePaperService:
"""脚本化 paper 服务:按工具名返回预设结果,模拟写节循环推进。"""
def __init__(self):
self.paper_id = "p1"
self.s1_done = False
self.s2_done = False
async def call_tool(self, name, arguments):
if name == "propose_paper_outline":
return {"ok": True, "paper_id": self.paper_id, "title": "水稻SPL基因家族研究进展",
"abstract_expect": "综述", "sections": [
{"section_id": "s1", "title": "引言", "key_points": [], "target_words": 1500},
{"section_id": "s2", "title": "讨论", "key_points": [], "target_words": 1500},
]}
if name == "get_paper_progress":
sections = [
{"section_id": "s1", "title": "引言", "status": "done" if self.s1_done else "pending",
"word_count": 0, "citation_count": 0},
{"section_id": "s2", "title": "讨论", "status": "done" if self.s2_done else "pending",
"word_count": 0, "citation_count": 0},
]
return {"ok": True, "paper_id": self.paper_id, "status": "writing",
"sections": sections, "current_section_id": None}
if name == "write_paper_section":
sid = arguments["section_id"]
if sid == "s1":
self.s1_done = True
else:
self.s2_done = True
return {"ok": True, "section_id": sid, "status": "done",
"markdown": "内容" * 50, "citations_used": [], "new_citations": []}
if name == "complete_paper":
return {"ok": True, "full_markdown": "# 全文\n内容",
"references": [{"n": 1, "text": "X.", "kind": "title", "source_doc_id": "d1"}],
"renumber_map": {}, "disclaimer": "d"}
if name == "get_paper":
return {"ok": True, "paper_id": self.paper_id, "title": "水稻SPL基因家族研究进展",
"status": "complete", "full_markdown": "# 全文",
"references": [{"n": 1, "text": "X.", "kind": "title", "source_doc_id": "d1"}],
"disclaimer": "d"}
if name == "cancel_paper":
return {"ok": True}
if name == "revise_paper":
return {"ok": True, "affected_section_ids": ["s1"], "plan": "修订引言"}
if name == "revise_paper_section":
return {"ok": True, "section_id": arguments["section_id"], "markdown": "改后", "citations_used": []}
raise AssertionError(f"unexpected tool: {name}")
@pytest.fixture
def bff_database(tmp_path):
_run(bff_db.init_db(tmp_path / "bff.db"))
_run(bff_db.db.execute(
"INSERT INTO sessions (id, title, status, meta, created_at, updated_at) "
"VALUES ('s1', 't', 'idle', '{}', '2026-01-01T00:00:00Z', '2026-01-01T00:00:00Z')"))
yield bff_db.db
_run(bff_db.close_db())
def _fake_brief(complete=True):
async def _complete_json(system, user, **kwargs):
if complete:
return {"topic": "水稻SPL基因家族", "paper_type": "综述", "language": "中文",
"length": "5000字", "format_req": "GB/T 7714", "style": "学术严谨", "requirements": ""}
return {"topic": "水稻SPL", "paper_type": "", "language": "", "length": ""}
return _complete_json
@pytest.fixture
def fake_insert_artifact(monkeypatch):
async def _fake(db, **kwargs):
return "art-fake"
monkeypatch.setattr(docgen_mod, "insert_artifact", _fake)
return _fake
def test_trigger_complete_brief_proposes_outline(bff_database, monkeypatch, fake_insert_artifact):
svc = FakePaperService()
monkeypatch.setattr(agent, "_new_client", lambda: svc)
monkeypatch.setattr(agent, "llm_complete_json", _fake_brief(complete=True))
blocks = _run(agent.handle_turn(bff_database, "s1", "帮我写一篇水稻SPL基因家族综述"))
assert blocks[-1]["type"] == "paper_outline"
assert blocks[-1]["paper_id"] == "p1"
rows = _run(bff_database.fetchall("SELECT * FROM messages WHERE session_id = 's1'"))
assert len(rows) == 1
assert rows[0]["kind"] == "paper"
def test_trigger_incomplete_brief_returns_form(bff_database, monkeypatch):
monkeypatch.setattr(agent, "_new_client", lambda: FakePaperService())
monkeypatch.setattr(agent, "llm_complete_json", _fake_brief(complete=False))
blocks = _run(agent.handle_turn(bff_database, "s1", "帮我写论文"))
assert blocks[-1]["type"] == "paper_brief"
assert blocks[-1]["status"] == "pending"
def test_writing_loop_completes_to_artifact(bff_database, monkeypatch, fake_insert_artifact):
svc = FakePaperService()
monkeypatch.setattr(agent, "_new_client", lambda: svc)
_run(agent.run_writing_loop(bff_database, "p1", "s1"))
assert svc.s1_done and svc.s2_done
rows = _run(bff_database.fetchall("SELECT * FROM messages WHERE session_id = 's1' ORDER BY rowid"))
# 进度卡由前端确认时本地 seed + 轮询驱动,后端只落「开始撰写」文本 + 逐节小结 + artifact
assert "开始逐节撰写" in rows[0]["blocks"]
assert "artifact" in rows[-1]["blocks"]
assert "art-fake" in rows[-1]["blocks"]