feat(investigations): refine report authoring and replay flushing
Generic Container CI/CD / test-build-publish (push) Successful in 4m31s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 4m31s

This commit is contained in:
2026-11-14 23:00:15 +08:00
parent 2e4f6cb93b
commit b45c5c6f46
27 changed files with 1125 additions and 94 deletions
+72
View File
@@ -0,0 +1,72 @@
"""Presentation prose is validated on writes without rejecting historical scenes."""
from copy import deepcopy
from datetime import datetime, timezone
from uuid import uuid4
import pytest
from app.domain.schemas.operational_task import TaskPresentationEventCommand, TaskPresentationHistoryItem
def presentation():
return {
"request_id": uuid4(), "run_id": uuid4(), "source_session_id": "fixture-session",
"purpose": "核对压力变化与异常时刻的对应关系。",
"narration": {"stage": "evidence_verification", "summary": "压力已回升,恢复情况仍需结合现场反馈复核。",
"next_step": "核对完工后的恢复监测。", "evidence_refs": ["recovery:sha256:fixture"]},
"command": {"action": "open_chart", "chart_id": "pressure", "evidence_refs": ["recovery:sha256:fixture"],
"chart": {"title": "170486 压力恢复趋势", "chart_type": "line", "x_axis_name": "时间",
"y_axis_name": "压力(m)", "x_data": ["13:00", "13:15"],
"series": [{"name": "170486 当前压力", "data": [20.09, 26.12],
"sample_times": ["2026-07-16T05:00:00Z", "2026-07-16T05:15:00Z"]}]}},
}
@pytest.mark.parametrize("text", [
"status=passed,required_samples=1。", "Skill scada-anomaly v1.0.16 已执行。",
"已读取 recovery:sha256:01234567。", "恢复时刻 2026-07-16T05:15:00Z。",
"恢复时刻 13:15(UTC+8)。", "**恢复复核通过**。",
])
def test_presentation_rejects_execution_text_in_purpose_finding_and_follow_up(text):
value = presentation()
for field in ("purpose", "summary", "next_step"):
changed = deepcopy(value)
if field == "purpose":
changed[field] = text
else:
changed["narration"][field] = text
history = TaskPresentationHistoryItem(
event_seq=1, run_id=changed["run_id"], execution_generation=1,
occurred_at=datetime(2026, 7, 16, tzinfo=timezone.utc),
command=changed["command"], purpose=changed["purpose"], narration=changed["narration"],
)
assert history.purpose == changed["purpose"]
with pytest.raises(ValueError):
TaskPresentationEventCommand.model_validate(changed)
@pytest.mark.parametrize("field", ["title", "x_axis_name", "y_axis_name", "x_data", "series"])
def test_new_chart_visible_labels_follow_the_same_prose_contract(field):
value = presentation()
chart = value["command"]["chart"]
if field == "x_data":
chart[field][0] = "2026-07-16T05:00:00Z"
elif field == "series":
chart[field][0]["name"] = "execution_generation=3"
else:
chart[field] = "压力复核 status=passed"
with pytest.raises(ValueError):
TaskPresentationEventCommand.model_validate(value)
def test_display_validation_preserves_real_timestamps_identifiers_and_values():
value = presentation()
parsed = TaskPresentationEventCommand.model_validate(value).model_dump(mode="json")
assert parsed["command"] == value["command"]
assert parsed["narration"]["evidence_refs"] == value["narration"]["evidence_refs"]
value["command"] = {"action": "set_overlay", "overlay_id": "estimated-impact",
"title": "候选管段预计影响范围", "mode": "geometry", "evidence_id": "mcp:fixture"}
assert TaskPresentationEventCommand.model_validate(value).command.title == "候选管段预计影响范围"
value["command"]["title"] = "候选影响范围(UTC+8)"
with pytest.raises(ValueError):
TaskPresentationEventCommand.model_validate(value)