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:
@@ -9,7 +9,7 @@ from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from genesis.server.store import SessionStore
|
||||
from genesis.server.store import SessionStore, ProjectsStore
|
||||
from genesis.server.service import GenesisService, ServiceStepError, FileTypeError
|
||||
|
||||
_SAMPLE = Path(__file__).resolve().parents[1] / "sample"
|
||||
@@ -59,6 +59,104 @@ def test_create_and_upload_files(svc):
|
||||
assert got.files["requirements"]["name"] == "requirements_newdev.xlsx"
|
||||
|
||||
|
||||
def test_create_session_name_and_project(tmp_path):
|
||||
svc = GenesisService(
|
||||
store=SessionStore(db_path=str(tmp_path / "s.db")),
|
||||
data_root=str(tmp_path / "data"),
|
||||
engine=FakeEngine(),
|
||||
)
|
||||
s = svc.create_session("u1", name="我的会话", project="projA")
|
||||
got = svc.get_session(s.session_id)
|
||||
assert got.name == "我的会话"
|
||||
assert got.project == "projA"
|
||||
|
||||
|
||||
def test_upload_requirements_auto_names_session(tmp_path):
|
||||
svc = GenesisService(
|
||||
store=SessionStore(db_path=str(tmp_path / "s.db")),
|
||||
data_root=str(tmp_path / "data"),
|
||||
engine=FakeEngine(),
|
||||
)
|
||||
s = svc.create_session("u1") # 默认「新会话」
|
||||
svc.upload_file(s.session_id, "requirements", "要件定义_v2.xlsx", b"PK\x03\x04")
|
||||
assert svc.get_session(s.session_id).name == "要件定义_v2"
|
||||
|
||||
|
||||
def test_has_file_falls_back_to_project_config(tmp_path):
|
||||
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="")
|
||||
svc = GenesisService(
|
||||
store=store, data_root=str(tmp_path / "data"), engine=FakeEngine(), projects=projects,
|
||||
)
|
||||
s = svc.create_session("u1", project="projA")
|
||||
svc.upload_file(s.session_id, "requirements", "requirements_newdev.xlsx",
|
||||
(_SAMPLE / "requirements_newdev.xlsx").read_bytes())
|
||||
rec = svc.get_session(s.session_id)
|
||||
# requirements 仅用户上传;template 来自项目配置
|
||||
assert svc.has_file(rec, "requirements") is True
|
||||
assert svc.has_file(rec, "template") is True
|
||||
assert svc.has_file(rec, "write_instruction") is False
|
||||
assert svc._eff_path(rec, "template") == str(_SAMPLE / "template_design_ja.docx")
|
||||
|
||||
|
||||
def test_rebuild_source_merges_project_config(tmp_path):
|
||||
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=str(_SAMPLE / "rules_design_ja.docx"),
|
||||
rules=[str(_SAMPLE / "rules_entry_ja.docx")],
|
||||
existing_system_code_dir="", design_docs_dir="")
|
||||
svc = GenesisService(
|
||||
store=store, data_root=str(tmp_path / "data"), engine=FakeEngine(), projects=projects,
|
||||
)
|
||||
s = svc.create_session("u1", project="projA")
|
||||
svc.upload_file(s.session_id, "requirements", "requirements_newdev.xlsx",
|
||||
(_SAMPLE / "requirements_newdev.xlsx").read_bytes())
|
||||
ss = svc._rebuild_source(svc.get_session(s.session_id))
|
||||
assert ss.template is not None
|
||||
assert len(ss.rule_docs) >= 2 # write_instruction + rules 合并
|
||||
|
||||
|
||||
def test_rebuild_source_enumerates_design_docs_dir(tmp_path):
|
||||
from docx import Document
|
||||
store = SessionStore(db_path=str(tmp_path / "s.db"))
|
||||
projects = ProjectsStore(db_path=str(tmp_path / "p.db"))
|
||||
dd = tmp_path / "design"
|
||||
dd.mkdir()
|
||||
d = Document()
|
||||
d.add_paragraph("设计文档:订单管理整体方案")
|
||||
d.save(dd / "d1.docx")
|
||||
projects.upsert(name="projA", display_name="P", template="", write_instruction="",
|
||||
rules=[], existing_system_code_dir="", design_docs_dir=str(dd))
|
||||
svc = GenesisService(
|
||||
store=store, data_root=str(tmp_path / "data"), engine=FakeEngine(), projects=projects,
|
||||
)
|
||||
s = svc.create_session("u1", project="projA")
|
||||
svc.upload_file(s.session_id, "requirements", "requirements_newdev.xlsx",
|
||||
(_SAMPLE / "requirements_newdev.xlsx").read_bytes())
|
||||
ss = svc._rebuild_source(svc.get_session(s.session_id))
|
||||
assert len(ss.design_docs) == 1
|
||||
|
||||
|
||||
def test_has_file_without_projects_returns_false(svc):
|
||||
s = svc.create_session("u1")
|
||||
rec = svc.get_session(s.session_id)
|
||||
assert svc.has_file(rec, "template") is False
|
||||
assert svc._eff_path(rec, "template") is None
|
||||
|
||||
|
||||
def test_upload_requirements_does_not_override_named_session(tmp_path):
|
||||
svc = GenesisService(
|
||||
store=SessionStore(db_path=str(tmp_path / "s.db")),
|
||||
data_root=str(tmp_path / "data"), engine=FakeEngine(),
|
||||
)
|
||||
s = svc.create_session("u1", name="已命名会话")
|
||||
svc.upload_file(s.session_id, "requirements", "other_name.xlsx", b"PK\x03\x04")
|
||||
assert svc.get_session(s.session_id).name == "已命名会话"
|
||||
|
||||
|
||||
def test_upload_invalid_type_rejected(svc):
|
||||
s = svc.create_session("u1")
|
||||
with pytest.raises(FileTypeError):
|
||||
|
||||
Reference in New Issue
Block a user