feat(web): 项目级配置 + 会话命名/历史 + 设计文档纳入影响调查

- 会话支持 name/project 字段,上传要件定义后自动命名;前端侧边栏会话历史 + localStorage 恢复,顶部只显示会话名
- 新增 ProjectsStore(SQLite)与 /api/projects CRUD;绑定项目后 _rebuild_source 合并模板/规则/代码库/设计文档,上传区仅要件定义
- StructuredSource.design_docs 与 ImpactReport.design_references;影响调查新增既有设计文档确定性交叉引用(无 LLM)
- 同步更新 docs/design.md §12.7、README、_AI_USAGE_LOG.md;全量测试 558 通过,覆盖率 99.10%
This commit is contained in:
lhl
2026-08-27 12:13:28 +08:00
parent 838a93720e
commit 916c5beed7
18 changed files with 1174 additions and 65 deletions
+81
View File
@@ -95,6 +95,87 @@ def test_status_reports_current_state(tmp_path):
assert "uploading" in r["reply"]
def test_status_reply_with_project_design_docs(tmp_path):
from genesis.server.store import ProjectsStore
store = SessionStore(db_path=str(tmp_path / "s.db"))
dd = tmp_path / "design"
dd.mkdir()
(dd / "d.docx").write_bytes(b"PK\x03\x04")
projects = ProjectsStore(db_path=str(tmp_path / "p.db"))
projects.upsert(name="projA", display_name="P", template="", write_instruction="",
rules=[], existing_system_code_dir="", design_docs_dir=str(dd))
agent = ChatAgent(GenesisService(store=store, data_root=str(tmp_path / "data"),
engine=FakeEngine(), projects=projects), fake=True)
sid = agent.service.create_session("u1", project="projA").session_id
r = agent.handle_message(sid, "现在什么状态?")
assert "design_docs" in r["reply"]
def test_status_reply_lists_existing_system_and_design_docs(tmp_path):
from genesis.server.store import ProjectsStore
store = SessionStore(db_path=str(tmp_path / "s.db"))
dd = tmp_path / "design"; dd.mkdir(); (dd / "d.docx").write_bytes(b"PK\x03\x04")
code = tmp_path / "code"; code.mkdir()
projects = ProjectsStore(db_path=str(tmp_path / "p.db"))
projects.upsert(name="projA", display_name="P", template="", write_instruction="",
rules=[], existing_system_code_dir=str(code), design_docs_dir=str(dd))
agent = ChatAgent(GenesisService(store=store, data_root=str(tmp_path / "data"),
engine=FakeEngine(), projects=projects), fake=True)
sid = agent.service.create_session("u1", project="projA").session_id
reply = agent._status_reply(agent.service.get_session(sid), [])
assert "existing_system" in reply["reply"] and "design_docs" in reply["reply"]
def test_auto_generate_unknown_status_falls_to_status_reply(tmp_path):
from genesis.server.store import ProjectsStore
store = SessionStore(db_path=str(tmp_path / "s.db"))
projects = ProjectsStore(db_path=str(tmp_path / "p.db"))
projects.upsert(name="projA", display_name="P",
template=str(_SAMPLE / "template_design_ja.docx"),
write_instruction="", rules=[], existing_system_code_dir="", design_docs_dir="")
agent = ChatAgent(GenesisService(store=store, data_root=str(tmp_path / "data"),
engine=FakeEngine(), projects=projects), fake=True)
sid = agent.service.create_session("u1", project="projA").session_id
# 上传要件定义,使 has_file 通过;随后将状态置为 done 触发 _status_reply 分支
agent.service.upload_file(sid, "requirements", "requirements_newdev.xlsx",
(_SAMPLE / "requirements_newdev.xlsx").read_bytes())
agent.service.store.update_status(sid, "done")
r = agent._auto_generate(sid, "auto", [])
assert "done" in r["reply"]
def test_run_generate_from_awaiting_impact_confirm_confirms_first(tmp_path):
agent = ChatAgent(_svc(tmp_path), fake=True)
sid = agent.service.create_session("u1").session_id
agent.service.store.update_status(sid, "awaiting_impact_confirm")
rec = agent.service.get_session(sid)
r = agent._run_generate(sid, rec, [], output_language="auto")
# confirm_impact 先执行(line 194),随后 generate 因缺文件抛错被捕获
assert r["status"] in ("writing", "awaiting_impact_confirm")
def test_run_impact_from_awaiting_parse_confirm(tmp_path):
from genesis.server.store import ProjectsStore
store = SessionStore(db_path=str(tmp_path / "s.db"))
code = tmp_path / "code"; code.mkdir(); (code / "A.java").write_text("class A {}")
projects = ProjectsStore(db_path=str(tmp_path / "p.db"))
projects.upsert(name="projA", display_name="P", template="", write_instruction="",
rules=[], existing_system_code_dir=str(code), design_docs_dir="")
agent = ChatAgent(GenesisService(store=store, data_root=str(tmp_path / "data"),
engine=FakeEngine(), projects=projects), fake=True)
sid = agent.service.create_session("u1", project="projA").session_id
agent.service.store.update_status(sid, "awaiting_parse_confirm")
r = agent._run_impact(sid, [])
assert r["status"] == "awaiting_impact_confirm"
def test_impact_brief_malformed_returns_default(tmp_path):
agent = ChatAgent(_svc(tmp_path), fake=True)
rec = agent.service.create_session("u1")
rec.impact_summary = "not-json"
assert agent._impact_brief(rec) == "已完成"
def test_generate_without_files_hints_upload(tmp_path):
agent = ChatAgent(_svc(tmp_path), fake=True)
sid = agent.service.create_session("u1").session_id