这是本节的多页打印视图。 .
E2E 测试
1 - E2E 测试约定(总纲)
本项目 E2E 体系的初始世界定义与规则手册。场景覆盖地图见 e2e-medilink-场景清单.md;功能行为的 as-is 权威 =
prd/fsd-medilink/(FSD)。 实现原则:API-first、workers 1、断业务结果不断实现细节、事件链轮询非 sleep。
1. 初始世界定义
1.1 环境矩阵
| 环境 | 库 | 端口 | 配置 | 触发 |
|---|---|---|---|---|
| dev | medilink @ 192.168.50.139:3308 | 18031 | configs/config.yaml | make start |
| e2e | medilink_e2e @ 同实例 | 18032 | configs/config.e2e.yaml | bash scripts/dev/e2e.sh(自管起停)或 make e2e |
| 浏览器冒烟 | 同 e2e(吃十段终态库) | web 5173 / app 8080 | MEDILINK_PROXY_TARGET 指向 18032 | ci.sh --with-env(冒烟前置位见 §1.6) |
Redis 两世界共用 127.0.0.1:6379,靠键命名空间隔离(限流 rl:<库名>:*、TOTP totp:<库名>:*、RBAC rbac:<库名>:*、翻译缓存 tr:<库名>:*)。MinIO bucket dev=medilink-dev / e2e=medilink-e2e 独立隔离。
1.2 单册策略
单库单册(tech-design §1 裁决):E2E 世界 = make db-reset 重建独立 e2e 库,无租户克隆、无 ID 重写,隔离问题退化为「独立测试库」。
1.3 账号矩阵(seed 预置,scripts/dev/seed.sql)
| 账号 | id | 角色 | 凭据(dev/e2e only) | TOTP |
|---|---|---|---|---|
| consultant@medilink.local | 9001 | CONSULTANT | Consultant#2026 | 密文预生成写死(python3 stdlib 现算 code) |
| doctor@medilink.local | 9002 | DOCTOR | 同策略 | secret 独立 |
| admin@medilink.local | 9003 | ADMIN | 同策略 | 二态重置(见 §1.6) |
| (注册链创建) | 自增 | PATIENT | 注册表单自设 | — |
患者账号不由 seed 预置——由 F-REG 段注册事务链真实创建(全链保真)。
1.4 预置种子
4 所医院(3 PUBLISHED + 1 DRAFT——列表 total=3 断言的依据)+ 医院科室 + 专家(PUBLISHED,医生选人消费)+ 3 账号 + 14 权限码字典 + FAILED 邮件样例行(last_error 带 seed: 前缀,幂等清理自造行)。
1.5 确定性规则
- TOTP 现算:python3 stdlib 按 seed 密钥现算 6 位码;防重放 SETNX EX 90 → 90s 内重跑必撞重放——失败时睡到下一 30s 边界重算重试恰一次。
- 限流相位浮动:60/min 固定窗,前段请求消耗随窗口相位浮动 → 限流段永远放最后([10/10]);冒烟前置位清
rl:*。 - 时延断言口径:Send 路径 7 次串行 DB 往返,dev MySQL RTT≈50-70ms → 上界 2.5s(环境如实界);「原文送达 <500ms」为同机房部署目标(DIF-002 口径),3G 模拟 P95 归人工验收。
- 翻译轮询:mock 译文前缀
[zh]/[en];worker 异步 → 轮询等待非固定 sleep。 - SSE 捕获:
curl --max-time后台捕获防悬挂;补拉先于实时(after_id=0 拉全量)。
1.6 隔离与清理
ci.sh --with-env 冒烟前置位:ADMIN 二态重置(seed 的 ADMIN totp_status 归 NONE——Playwright 登录无 TOTP 第二段)+ 清 rl:* 限流键(吃十段终态库时限流窗口已耗尽)。
1.7 明令禁止
- 禁
toISOString()(时区漂移是 E2E 假红第一来源)——时间断言用相对时间。 - 禁固定 sleep 替代轮询(翻译/邮件等异步链)。
- 禁断言实现细节(SQL 形态/内部字段序)——断业务结果(状态码/业务字段值/终态)。
- 禁跳过重建直接跑(十段假设初始世界)。
- 禁在限流段之前发无谓 /pub 请求(预算:60/min 含 F-HOS+聊天+F-REG 全部匿名请求)。
2. 可测性硬规则
- e2e.sh 是一次性长链(非独立用例):段间有状态依赖(患者=注册创建、工单=注册开出、支付单=方案确认后创建),不支持单段重跑——重跑=整链重跑(make e2e-reset 后 bash scripts/dev/e2e.sh)。
- 每段头
echo "==> [n/10] ...";断言失败即exit 1(expect_status 非零即档位失败——ci.sh –with-env 闸门)。 - 幂等锚:重复注册 409、重复已读 404/幂等、白名单跳段 409——负路径断言与正路径同等地位。
3. 运行手册
4. flaky 处理规则
- TOTP 重放(90s 窗)→ 睡到下一 30s 边界重算(已内置)。
- 限流相位 → 限流段移最后(结构性解决,已内置)。
- 翻译/邮件异步未达 → 轮询 + 超时上限(禁无条件重跑)。
- 连续 2 次假红 → 按确定性规则排查(时延口径/种子漂移),修脚本不修断言。
2 - E2E 场景清单(medilink 册)
覆盖地图非用例步骤表——用例步骤即
scripts/dev/e2e.sh本体(一次性长链,段=场景);本表给全景覆盖、FSD 回链与缺口。测试约定见 e2e-测试约定.md。 断言规模:83 处expect_status+ 若干业务字段断言(896 行脚本)。
1. 覆盖地图(10 场景 = 脚本十段)
| 场景 ID | 场景名 | FSD 功能 ID | 前置种子 | 主干路径 | 关键断言 | 冒烟 | 可并行 | 现状 |
|---|---|---|---|---|---|---|---|---|
| E2E-BOOT | 世界重建与起服 | — | — | db-reset→seed→build→起服 18032→healthz | healthz=200;库已重建 | ✅ | ✗(世界根) | 已实现 [1/10][2/10] |
| E2E-HOS | 医院公开面 | F2.01/F1.03 | 4 医院(3 PUBLISHED) | 无 token 401→guest-session→列表 total=3→详情→数组字段非 null | 401/total=3/[] 非 null | ✅ | ✗ | 已实现 [3/10] |
| E2E-CHAT | 双语聊天全链 | F3.01-F3.07/F1.01 | guest token;CONSULTANT 9001 | 建会话→SSE 后台捕获→发消息(≤2.5s)→[zh] 译文回填→B 端两段式登录→会话列表→顾问回复→访客收 [en] 译文→已读 | 201/译文 SSE/两段式/read_at | ✅ | ✗ | 已实现 [4/10] |
| E2E-REG | 注册与开单 | F4.01/F4.02/F4.03/F1.02/F3.08 | 直传文档 2 份 | presign→真 PUT MinIO→confirm→注册事务链→重复注册 409→绑定后 /pub 409→患者登录→/pt 补拉见历史→患者发送→顾问 SSE 收 | 201/409×2/事务链响应工单号 | ✅ | ✗ | 已实现 [5/10] |
| E2E-TICK | 工单流转与预诊断 | F5.02/F5.03/F6.01/F6.02/F6.03 | 工单=注册开出 | CREATED 筛选→分配顾问→流转→assign-doctor→B 端 SSE ticket 事件→跳段 409→医生登录→待诊列表→拒绝(回退边3)→重派→再预诊(M10 专家选人 dept 锚+hospital_id 回填)→患者看方案→进度点亮→确认方案 | 白名单 409/快照覆盖/estimate 字段 | ✅ | ✗ | 已实现 [6/10] |
| E2E-PAY | 支付闭环 | F7.01/F7.02/F7.03 | 方案已确认(PENDING_PAYMENT) | 创建支付单(fake 档)→构造 HMAC webhook 密文→失败事件(订单 FAILED+回退边1)→重新确认→支付单#2→成功事件(SUCCEEDED+PENDING_DEPARTURE+凭证 PDF)→重复投递 200 幂等 | 回退边1/幂等 200/凭证落桶 | ✅ | ✗ | 已实现 [7/10] |
| E2E-CARE | 诊疗阶段与出行 | F8.01/F8.02/F9.01/F9.02 | IN_TREATMENT 工单 | 阶段推进(C 轴前置校验)→阶段资料新组→同组替换 v2→出行记录创建+流转→患者 /pt/trips 只读 | is_current 翻转/版本 v2/白名单 | ✅ | ✗ | 已实现 [8/10] |
| E2E-ADMIN | 后台运营 | F10.01/F10.03-F10.06/F5.05/F2.03-F2.06/F4.05 | 全前段数据 | INBOX 待办→已读→重复 404→注册卡片→看板聚合+超时表→op_log 断言 ticket.assign→医院管理 CRUD/状态流转/科室/专家→TOTP 重置三态→用户管理→GDPR 链(export→erase→墓碑四连)→残留单对账与关单 | 墓碑语义/审计行/白名单流转 | ✅ | ✗ | 已实现 [9/10] |
| E2E-RATE | 限流 | F1.06 | 清 rl:* 后满窗 | 61 次匿名请求 → 第 61 次 429 | 429 | ✅ | ✗(污染窗口恒最后) | 已实现 [10/10] |
| E2E-UI | 浏览器冒烟 | F1.01/F3.06/F5.02/F2.01/F3.03 | 十段终态库 | web:登录→工单→看板;app:医院列表→聊天面 | 关键链可达 | ✅ | web/app 两 project | 已实现(Playwright 2 spec) |
2. 已知产品缺口(如实记录,非缺陷)
| 缺口 | 说明 | 去向 |
|---|---|---|
| 浏览器级仅 2 冒烟 spec | admin-smoke / patient-smoke 关键链;无逐页 UI 断言(用户裁决 M9:Playwright=关键链冒烟,否决全链浏览器级重写) | 维持裁决 |
| UI 细节无断言 | 表单校验提示/按钮显隐权限矩阵(authz.ts has())无浏览器级验证 | web vitest 单测覆盖 authz 纯函数 |
| 手工走查清单未建 | 专家 Modal/凭证 PDF/Taro 咨询链/TOTP 登录页(NEXT-SESSION 遗留项) | 本次 docs/manual/人工测试清单 补齐 |
3. 与人工测试清单的关系
人工测试清单(docs/manual/人工测试清单.md)按 T 组编号组织走查用例,每用例标注 E2E 覆盖状态:
- 🟢 已断言(挂场景 ID)——人工只需抽验;
- 🟡 待实现(UI 级无自动化)——人工必测;
- 归类规则:可稳定 API 断言的升格进 e2e.sh;纯视觉/交互体验的永不自动化。
4. FSD 反向覆盖统计
| 模块 | 功能节数 | e2e 直覆盖节数 | 覆盖率 |
|---|---|---|---|
| m01 认证权限 | 6 | 6(F1.01-F1.06 全部经链路断言) | 100% |
| m02 医院主数据 | 6 | 6(公开 2 + 管理 4) | 100% |
| m03 双语咨询 | 8 | 7(F3.04 图片消息未断言——直传链覆盖病历/阶段图,聊天图 mini 两步无专段) | 88% |
| m04 注册病历 | 5 | 5 | 100% |
| m05 工单看板 | 5 | 5 | 100% |
| m06 预诊断 | 3 | 3 | 100% |
| m07 支付对账 | 5 | 5 | 100% |
| m08 诊疗过程 | 3 | 3 | 100% |
| m09 出行协助 | 3 | 3 | 100% |
| m10 系统审计 | 6 | 6 | 100% |
| 合计 | 50 | 49 | 98% |
唯一缺口 F3.04:聊天图片消息(presign→PUT→发消息内联 confirm)无专段——登记为下一次 e2e 扩展项(非阻塞:与病历直传共享 Store 两道拦截逻辑)。