# -*- coding: utf-8 -*- """UAT 计划与执行支撑文档(docs/UAT计划与执行支撑.md,issue #92)一致性检查。 检查项: 1. 必备章节齐全(目标/角色/准入/用例集/执行计划/缺陷/准出/交付物/风险); 2. 验收流程六节点关键词存在(PRD §10:自测→内部评审→客户UAT→上线→质保观察→闭环); 3. NFR 量化指标齐全(PRD §9:P99≤1.8s / 丢失率≤0.02% / 可用性≥99.8% / 路由≥96.5% / 首屏≤2s); 4. 用例覆盖三类场景(监控A/优化B/LLM C)+ NFR + 移动端; 5. 引用配套资产路径存在(部署手册/采集网隔离文档/healthz 探针)。 用法:python _check_uat_plan.py """ import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) DOC_PATH = os.path.join(HERE, "UAT计划与执行支撑.md") REQUIRED_SECTIONS = [ "## 1. 目标与范围", "## 2. 角色与职责(RACI)", "## 3. 准入条件(Entry Criteria)", "## 4. UAT 测试用例集", "## 5. 执行计划与排期", "## 6. 缺陷管理(Bug Triage)", "## 7. 准出条件(Exit Criteria / 放行)", "## 8. 交付物清单(UAT 阶段)", "## 9. 风险与缓解", ] # PRD §10 验收流程六节点 FLOW_KEYWORDS = ["自测", "内部评审", "客户 UAT", "上线", "质保观察", "闭环"] # PRD §9 NFR 量化底线 NFR_KEYWORDS = ["1.8s", "0.02%", "99.8%", "96.5%", "2s"] # 用例场景覆盖(用例编号前缀) SCENARIO_PREFIXES = ["UAT-A-", "UAT-B-", "UAT-C-", "UAT-N-", "UAT-M-"] # 必须存在的配套资产路径(相对仓库根) # 注:部署手册(issue #91)随其 PR 合并后存在;本脚本校验本仓库主干已具备的资产。 ASSET_PATHS = [ "docs/产品设计文档_iAOP.md", # PRD(UAT 用例依据来源) "docs/采集网只读隔离与网闸配置.md", # UAT-N-05 网络只读验证依据 "deploy/k8s/healthz/probe_availability.py", # UAT-N-02 可用性探针 ] def main() -> int: failures = [] repo_root = os.path.dirname(HERE) if not os.path.isfile(DOC_PATH): print("FAIL\n - UAT 计划文档不存在:UAT计划与执行支撑.md") return 1 text = open(DOC_PATH, "r", encoding="utf-8").read() for sec in REQUIRED_SECTIONS: if sec not in text: failures.append(f"缺少必备章节:{sec}") for kw in FLOW_KEYWORDS: if kw not in text: failures.append(f"缺少验收流程节点关键词:{kw}") for kw in NFR_KEYWORDS: if kw not in text: failures.append(f"缺少 NFR 量化指标关键词:{kw}") for prefix in SCENARIO_PREFIXES: if prefix not in text: failures.append(f"用例未覆盖场景前缀:{prefix}") for rel in ASSET_PATHS: if not os.path.isfile(os.path.join(repo_root, *rel.split("/"))): failures.append(f"配套资产缺失:{rel}") if failures: print("FAIL") for f in failures: print(" -", f) return 1 print("OK: UAT 计划章节/流程节点/NFR指标/场景覆盖/配套资产校验通过") return 0 if __name__ == "__main__": sys.exit(main())