Files
2026Technology-Competition/tests/test_frontend_audit_fixes.py
lhl be79624db5 fix(chat): 前端审计修复——预览契约改HTML/状态机统一/进度错误持久化/chat_state.js抽取
- app.py: /result/preview 返回渲染 HTML(HTMLResponse) 而非 {html} JSON;新增 /chat_state.js 静态路由;预览缺失返回 404(RESULT_NOT_FOUND)
- chat.html: currentProject 统一为 draftProject;新会话继承已选项目(§4.2);上传区显隐/类型映射/抽屉脏检测改用 GenesisState;loadSession 按 role 渲染 progress/error;加 h1 标题
- chat_state.js(新): 抽取 autoBindProject/shouldHideUploadSelect/resolveUploadType/computeDrawerSnapshot/isDrawerDirty/buildWelcome 纯函数(U+1 分隔符)
- agent.py: 新增 _persist_progress/_store_error,进度与错误以 role 入库(重载可见)
- 测试: test_chat_state.js(11 passed) + test_frontend_audit_fixes.py(5) + 修正 test_server_api 旧 JSON 契约断言;全量 pytest 568 passed / 99.06%
2026-08-29 11:28:08 +08:00

95 lines
3.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""前端审计修复验证(docs/frontend_audit_plan.md §7.2/§7.3)。
覆盖:
- 预览契约返回 HTML(非 JSON
- 进度消息持久化 role='progress'(重载会话可见)
- 错误消息持久化 role='error'
- /chat_state.js 静态路由可用
"""
from __future__ import annotations
import pytest
from pathlib import Path
from fastapi.testclient import TestClient
from genesis.chat.agent import ChatAgent
from genesis.server.app import create_app
from genesis.server.service import GenesisService
from genesis.server.store import ProjectsStore, SessionStore
_SAMPLE = Path(__file__).resolve().parents[1] / "sample"
@pytest.fixture
def client(tmp_path):
app = create_app(
store=SessionStore(db_path=str(tmp_path / "s.db")),
data_root=str(tmp_path / "data"),
engine="fake",
)
return TestClient(app)
def _upload_core(client, sid):
files = [
("requirements", "requirements_newdev.xlsx", _SAMPLE / "requirements_newdev.xlsx"),
("template", "template_design_ja.docx", _SAMPLE / "template_design_ja.docx"),
("write_instruction", "rules_design_ja.docx", _SAMPLE / "rules_design_ja.docx"),
("rules", "rules_entry_ja.docx", _SAMPLE / "rules_entry_ja.docx"),
]
for ft, name, path in files:
r = client.post(f"/api/sessions/{sid}/files",
data={"file_type": ft},
files={"file": (name, path.read_bytes())})
assert r.status_code == 200, r.text
def test_result_preview_returns_html(client):
"""预览链接应返回渲染后的 HTML,而非 {'html': ...} JSON(§4.3)。"""
sid = client.post("/api/sessions", json={"user_id": "u1"}).json()["session_id"]
_upload_core(client, sid)
client.post(f"/api/chat/{sid}/messages", json={"content": "请生成概要设计书"})
r = client.get(f"/api/sessions/{sid}/result/preview")
assert r.status_code == 200
assert "text/html" in r.headers["content-type"]
assert r.text.lstrip().lower().startswith("<!doctype html>")
def test_result_preview_missing_returns_404(client):
"""无结果文档时预览返回 404RESULT_NOT_FOUND)。"""
sid = client.post("/api/sessions", json={"user_id": "u1"}).json()["session_id"]
r = client.get(f"/api/sessions/{sid}/result/preview")
assert r.status_code == 404
assert r.json()["detail"]["code"] == "RESULT_NOT_FOUND"
def test_progress_messages_persisted(client):
"""生成全流程的进度条目应以 role='progress' 入库,重载会话可见(§4.6)。"""
sid = client.post("/api/sessions", json={"user_id": "u1"}).json()["session_id"]
_upload_core(client, sid)
client.post(f"/api/chat/{sid}/messages", json={"content": "请生成概要设计书"})
msgs = client.get(f"/api/chat/{sid}/messages").json()
assert any(m["role"] == "progress" for m in msgs)
def test_error_role_persisted(tmp_path):
"""错误回复以 role='error' 入库(§4.6 后端 _store_error)。"""
store = SessionStore(db_path=str(tmp_path / "s.db"))
projects = ProjectsStore(db_path=str(tmp_path / "s.db"))
service = GenesisService(store=store, data_root=str(tmp_path / "data"),
engine="fake", projects=projects)
agent = ChatAgent(service=service, fake=True, engine="fake")
sid = service.create_session("u1").session_id
agent._store_error(sid, "解析失败:boom", "generate")
msgs = service.store.list_messages(sid)
assert any(m["role"] == "error" for m in msgs)
def test_chat_state_js_route(client):
"""/chat_state.js 静态路由返回 JS(前端模块可加载,§4.5)。"""
r = client.get("/chat_state.js")
assert r.status_code == 200
assert "application/javascript" in r.headers["content-type"]
assert "GenesisState" in r.text or "autoBindProject" in r.text