"""New publications are readable; historical reports keep their original evidence.""" from copy import deepcopy import pytest from app.domain.schemas.operational_task import ( InvestigationProgress, InvestigationProgressInput, InvestigationReport, InvestigationReportInput, ) def report(): return { "title": "压力异常恢复复核报告", "current_conclusion": "恢复复核通过。7 月 16 日 13:15,13 台复核设备均处于历史正常范围。", "conclusion_boundary": "依据完工后的一个采样时刻判断恢复,长期稳定性继续由日常监测跟踪。", "stage": "final", "outcome": "concluded", "data_gaps": [], "evidence_refs": ["recovery:sha256:stored-evidence"], "recommendations": ["继续监测压力与流量。"], "pending_items": [], } @pytest.mark.parametrize("text", [ "恢复复核 status=passed,13 台设备 normal。", "第 3 轮 execution_generation=3,handling_context.phase=verification_ready。", "完工后 required_samples=1,interval_seconds=900,verified_through=13:15。", "已读取 recovery:sha256:01234567,复核通过。", "Skill scada-anomaly v1.0.16 已按 Server 分发执行。", "query_scada_readings 已执行,query_complete=true。", "关联工单 60000000-0000-4000-8000-000000000002 已完工。", "恢复采样时间为 2026-07-16T05:15:00Z。", "恢复采样时间为 2026-07-16 13:15(UTC+8)。", "恢复采样时间为 2026-07-16 13:15+08:00。", "**恢复复核通过**,其余设备正常。", "恢复复核通过。", ]) def test_new_report_and_progress_reject_execution_details_and_raw_display_markup(text): legacy = {**report(), "current_conclusion": text} assert InvestigationReport.model_validate(legacy).current_conclusion == text assert InvestigationProgress.model_validate({"summary": text}).summary == text with pytest.raises(ValueError): InvestigationReportInput.model_validate(legacy) with pytest.raises(ValueError): InvestigationProgressInput.model_validate({"summary": text}) @pytest.mark.parametrize(("field", "limit"), [ ("title", 80), ("current_conclusion", 300), ("conclusion_boundary", 240), ]) def test_new_report_bounds_summary_fields_without_truncating_history(field, limit): valid = {**report(), field: "测" * limit} assert getattr(InvestigationReportInput.model_validate(valid), field) == valid[field] too_long = {**valid, field: "测" * (limit + 1)} assert getattr(InvestigationReport.model_validate(too_long), field) == too_long[field] with pytest.raises(ValueError): InvestigationReportInput.model_validate(too_long) def test_progress_summary_is_short_and_keeps_technical_references_structured(): progress = {"summary": "测" * 300, "graph_update": {"nodes": [{ "node_id": "recovery", "label": "恢复监测复核通过", "status": "recorded", "evidence_refs": ["recovery:sha256:stored-evidence"], }]}} assert InvestigationProgressInput.model_validate(progress).summary == progress["summary"] with pytest.raises(ValueError): InvestigationProgressInput.model_validate({**progress, "summary": "测" * 301}) def test_asset_ids_measurements_and_evidence_linked_tables_remain_publishable(): value = report() value["current_conclusion"] = "05:00-08:00 的候选管段 515870(DN600)相似度 97.08%,尚待现场核验。压力测点 170486 回升至 26.12 m。" value["ui_blocks"] = [{"evidence_refs": value["evidence_refs"], "block": { "version": "answer-ui@1", "block_id": "recovery-table", "phase": "final", "fallback_text": "压力测点 170486 恢复至 26.12 m。", "spec": {"root": "table", "elements": {"table": { "type": "Table", "children": [], "props": { "title": "恢复监测明细", "columns": ["测点", "修复后", "正常范围"], "rows": [["170486", "26.12 m", "26.02 至 26.30 m"]], }, }}}, }}] assert InvestigationReportInput.model_validate(value).ui_blocks leaked = deepcopy(value) leaked["ui_blocks"][0]["block"]["spec"]["elements"]["table"]["props"]["rows"][0][2] = "status=passed" assert InvestigationReport.model_validate(leaked).ui_blocks with pytest.raises(ValueError): InvestigationReportInput.model_validate(leaked) @pytest.mark.parametrize("field", ["recommendations", "pending_items"]) def test_follow_up_is_business_prose(field): value = {**report(), field: ["等待 handling_context.phase=waiting_scada。"]} with pytest.raises(ValueError): InvestigationReportInput.model_validate(value)