Files
next-tjwater-server/tests/unit/test_report_ui.py
T
jiang 1d061f6099
Generic Container CI/CD / test-build-publish (push) Failing after 6s
Server CI/CD v2 / build-test-publish-and-deploy (push) Failing after 6s
feat: add project-scoped operations
2026-10-22 18:40:20 +08:00

75 lines
3.6 KiB
Python

from copy import deepcopy
import json
import pytest
from pydantic import ValidationError
from app.domain.schemas.operational_task import InvestigationReportInput, InvestigationReportRevision
def rich_report():
return {
"title": "演练压力核验", "current_conclusion": "待核验", "conclusion_boundary": "演练记录",
"stage": "interim", "outcome": "concluded", "data_gaps": [], "evidence_refs": ["demo-1"],
"ui_blocks": [{
"evidence_refs": ["demo-1"],
"block": {
"version": "answer-ui@1", "block_id": "pressure", "fallback_text": "演练压力 0.27 MPa",
"spec": {"root": "chart", "elements": {
"chart": {"type": "Chart", "children": [], "props": {
"title": "演练压力", "chart_type": "line", "x_data": ["08:00", "09:00"],
"series": [{"name": "压力 MPa", "data": [0.3, 0.27]}], "source": "演练记录",
}},
}},
},
}],
}
def test_report_ui_round_trips_with_a_version_and_keeps_legacy_reports_readable():
report = InvestigationReportInput.model_validate(rich_report())
snapshot = InvestigationReportRevision(
revision=3, execution_generation=2, source_session_id="test",
published_at="2026-10-20T08:00:00Z", published_by="test", report=report,
)
restored = InvestigationReportRevision.model_validate_json(snapshot.model_dump_json())
assert restored.report.ui_blocks[0].block.spec.elements["chart"].props["series"][0]["data"] == [0.3, 0.27]
assert (restored.revision, restored.execution_generation) == (3, 2)
old = json.loads(snapshot.model_dump_json())
del old["report"]["ui_blocks"]
assert InvestigationReportRevision.model_validate(old).report.ui_blocks == []
@pytest.mark.parametrize("defect", ["unknown", "script", "unaligned", "nan", "cycle", "unreachable", "duplicate", "foreign", "many", "oversized"])
def test_report_ui_rejects_invalid_or_unbound_content(defect):
data = rich_report()
entry = data["ui_blocks"][0]
spec = entry["block"]["spec"]
chart = spec["elements"]["chart"]
if defect == "unknown":
chart["type"] = "iframe"
elif defect == "script":
chart["props"]["onClick"] = "execute()"
elif defect == "unaligned":
chart["props"]["series"][0]["data"] = [1]
elif defect == "nan":
chart["props"]["series"][0]["data"][0] = float("nan")
elif defect == "cycle":
spec["elements"]["chart"] = {"type": "Stack", "props": {"columns": "one", "gap": "normal"}, "children": ["chart"]}
elif defect == "unreachable":
spec["elements"]["unused"] = deepcopy(chart)
elif defect == "duplicate":
data["ui_blocks"].append(deepcopy(entry))
elif defect == "foreign":
entry["evidence_refs"] = ["other-task"]
elif defect == "many":
data["ui_blocks"] = [{**deepcopy(entry), "block": {**deepcopy(entry["block"]), "block_id": f"b{i}"}} for i in range(9)]
elif defect == "oversized":
# Individually valid blocks can still exceed the UTF-8 report budget.
elements = {"root": {"type": "Stack", "props": {"columns": "one", "gap": "normal"}, "children": [f"n{i}" for i in range(20)]}}
elements.update({f"n{i}": {"type": "Text", "props": {"text": "资" * 3900, "tone": "neutral"}, "children": []} for i in range(20)})
entry["block"]["spec"] = {"root": "root", "elements": elements}
data["ui_blocks"] = [entry, {**deepcopy(entry), "block": {**deepcopy(entry["block"]), "block_id": "second"}}]
with pytest.raises(ValidationError):
InvestigationReportInput.model_validate(data)