Files
hms/docs/superpowers/plans/2026-04-26-test-coverage-strategy.md
iven 2defbd7ab3
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
docs: 测试覆盖率提升实施计划
基于 spec 的 5 Phase + CI 门禁实施计划:
- Phase 0: TestApp + TestFixture + MSW + 覆盖率工具 + CI(2天)
- Phase 1: 高风险 service 测试 — points/dialysis/alert/device_reading(Week 1-2)
- Phase 2: 中风险 service 测试 — patient/appointment/follow_up/consultation/doctor/ai(Week 3-4)
- Phase 3: 低风险 service + handler + DTO + 后端增量门禁(Week 5-6)
- Phase 4: 前端补测 — API/Store/hooks/pages/E2E(Week 7-9)
共 43 个 Task,预计新增 ~230 个测试用例
2026-04-27 00:25:30 +08:00

18 KiB
Raw Blame History

测试覆盖率提升实施计划

设计规格: docs/superpowers/specs/2026-04-26-test-coverage-strategy-design.md 日期: 2026-04-26 | 状态: draft | 总周期: 9 周(+ Phase 0: 2 天)


Phase 0: 测试基础设施搭建2 天)

Task 1: TestApp struct 实现

目标: 在现有 TestDb 基础上封装 TestApp,提供完整测试环境。

涉及文件:

  • 修改: crates/erp-server/tests/integration/test_db.rs

详细步骤:

  1. test_db.rs 中新增 TestApp struct
pub struct TestApp {
    test_db: TestDb,
    health_state: HealthState,
    tenant_id: Uuid,
    operator_id: Uuid,
}
  1. 实现 TestApp::new():

    • 调用 TestDb::new() 创建隔离数据库
    • 构建 HealthState { db, event_bus: EventBus::new(100), crypto: PiiCrypto::dev_default() }
    • 生成随机 tenant_idoperator_id
    • 复用现有 make_state 模式(见 health_patient_tests.rs:15-21
  2. 添加访问方法:

    • db()&DatabaseConnection
    • health_state()&HealthState
    • tenant_id()Uuid
    • operator_id()Uuid

验收标准:

  • cargo test -p erp-server test_app_new 通过
  • TestApp Drop 时自动清理临时数据库(复用 TestDb 的 Drop 实现)
  • 现有 7 个集成测试不受影响

Task 2: TestFixture 工厂函数

目标: 提供预构建测试数据的工厂函数,减少每个测试的 setup 代码。

涉及文件:

  • 新增: crates/erp-server/tests/integration/test_fixture.rs
  • 修改: crates/erp-server/tests/integration.rs(添加模块注册)

详细步骤:

  1. 创建 TestFixture 模块,实现以下工厂函数:
impl TestApp {
    /// 创建患者(含基本字段,可自定义覆盖)
    pub async fn create_patient(&self, overrides: PatientOverrides) -> Patient { ... }

    /// 创建医生
    pub async fn create_doctor(&self, overrides: DoctorOverrides) -> DoctorProfile { ... }

    /// 创建排班
    pub async fn create_schedule(&self, doctor_id: Uuid, date: NaiveDate, ...) -> DoctorSchedule { ... }

    /// 创建随访任务
    pub async fn create_follow_up_task(&self, patient_id: Uuid, ...) -> FollowUpTask { ... }

    /// 创建积分账户 + 初始积分
    pub async fn create_points_account(&self, patient_id: Uuid, initial_balance: i32) -> PointsAccount { ... }
}
  1. 使用 Default trait + 覆盖模式:
#[derive(Default)]
pub struct PatientOverrides {
    pub name: Option<String>,
    pub gender: Option<String>,
    pub birth_date: Option<NaiveDate>,
    pub id_number: Option<String>,
    // ...
}

验收标准:

  • 现有 health_patient_tests.rs 可用 fixture 重写(验证工厂函数可用)
  • 每个工厂函数 ≤ 30 行代码
  • 创建的数据包含正确的 tenant_idcreated_by

Task 3: 前端 MSW v2 初始配置

涉及文件:

  • 新增: apps/web/src/test/mocks/handlers.ts
  • 新增: apps/web/src/test/mocks/server.ts
  • 修改: apps/web/src/test/setup.ts
  • 修改: apps/web/package.json(添加 msw 依赖)

详细步骤:

  1. 安装 MSW v2: pnpm add -D msw
  2. 创建 handlers.ts — 初始仅包含 auth 基础 handler登录/刷新):
export const handlers = [
  http.post('/api/v1/auth/login', () => HttpResponse.json({ ... })),
  http.post('/api/v1/auth/refresh', () => HttpResponse.json({ ... })),
]
  1. 创建 server.ts:
import { setupServer } from 'msw/node'
export const server = setupServer(...handlers)
  1. setup.ts 中启动/清理 MSW server:
beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

验收标准:

  • pnpm test 无报错
  • MSW server 正确拦截匹配的请求
  • 未匹配的请求产生 warning不是 error

Task 4: 前端覆盖率工具配置

涉及文件:

  • 修改: apps/web/package.json
  • 修改: apps/web/vitest.config.ts

详细步骤:

  1. 安装: pnpm add -D @vitest/coverage-v8
  2. vitest.config.ts 添加 coverage 配置:
test: {
  coverage: {
    provider: 'v8',
    reporter: ['text', 'lcov'],
    include: ['src/**/*.{ts,tsx}'],
    exclude: ['src/test/**', 'src/**/*.test.*', 'src/**/*.d.ts'],
  },
}
  1. 添加 npm script: "test:coverage": "vitest run --coverage"

验收标准:

  • pnpm test:coverage 生成覆盖率报告
  • 输出包含各文件的行覆盖率百分比

Task 5: 后端覆盖率工具安装验证

涉及文件: 无代码变更,仅环境安装

详细步骤:

  1. 安装 cargo-llvm-cov: cargo install cargo-llvm-cov
  2. 验证基本功能: cargo llvm-cov --workspace --text
  3. 编写增量检查脚本 scripts/coverage-diff-check.py(或 shell
    • 读取 cargo llvm-cov --json 输出
    • 通过 git diff --name-only origin/main 获取变更文件列表
    • 过滤出变更文件的覆盖率
    • 低于 80% 的文件列表退出码 1

验收标准:

  • cargo llvm-cov --workspace --text 输出覆盖率报告
  • 增量检查脚本对模拟变更文件正确判断通过/不通过

Task 6: CI workflow 初始创建

涉及文件:

  • 新增: .github/workflows/test.yml

详细步骤:

  1. 创建基础 CI workflow包含

    • backend-test job: cargo checkcargo testcargo clippy
    • frontend-test job: pnpm installpnpm test:cipnpm build
    • PostgreSQL service 容器(仅 backend-test
    • 环境变量配置(测试用 JWT secret、数据库 URL
  2. 配置触发条件: on: pull_request + on: push: main

验收标准:

  • Workflow 文件语法正确(actionlint 验证)
  • 包含 spec 要求的完整验证链: check → test → clippy → build

Phase 1: 高风险 Service 测试Week 1-2

共 6 个 service约 47 个测试。所有测试使用 TestApp + TestFixture 模式。

Task 7: points_service 测试12 个)

涉及文件:

  • 新增: crates/erp-server/tests/integration/health_points_tests.rs
  • 修改: crates/erp-server/tests/integration.rs

测试用例清单:

测试名 场景
test_points_earn_sign_in 签到积分 — 首次签到成功,重复签到同天拒绝
test_points_earn_custom 自定义积分增加
test_points_consume_fifo_deduction FIFO 消费 — 先消耗最早的积分记录
test_points_consume_balance_insufficient 余额不足时返回错误
test_points_consume_exact_balance 消费金额等于余额
test_points_consume_partial 消费金额小于单条记录,正确拆分
test_points_account_create_on_first_earn 首次获取积分时自动创建账户
test_points_checkin_streak 连续签到奖励
test_points_order_create 积分兑换商品 — 创建订单
test_points_order_insufficient_cancel 积分不足时订单失败
test_points_transaction_history 交易记录查询
test_points_tenant_isolation 租户隔离 — A 的积分 B 看不到

验收标准:

  • cargo test -p erp-server health_points 全部通过
  • FIFO 消费逻辑有明确的事务性验证(回滚场景)

Task 8: dialysis_service 测试8 个)

涉及文件:

  • 新增: crates/erp-server/tests/integration/health_dialysis_tests.rs

测试用例清单:

测试名 场景
test_dialysis_create_basic 创建透析记录基本 CRUD
test_dialysis_create_pii_encrypted PII 字段(病史、并发症)加密存储
test_dialysis_update_status_flow 状态流转: scheduled → in_progress → completed
test_dialysis_list_by_patient 按患者 ID 过滤列表
test_dialysis_tenant_isolation 租户隔离
test_dialysis_version_conflict 乐观锁 — 旧版本更新拒绝
test_dialysis_soft_delete 软删除后不可见
test_dialysis_create_without_patient_returns_error 无效患者 ID 返回错误

验收标准:

  • cargo test -p erp-server health_dialysis 全部通过
  • PII 加密字段在数据库层面不可明文读取(验证密文格式)

Task 9: alert_engine 测试8 个)

涉及文件:

  • 新增: crates/erp-server/tests/integration/health_alert_tests.rs(包含 engine + rule + service 测试)

测试用例清单:

测试名 场景
test_alert_engine_evaluate_threshold_exceeded 体征超阈值触发告警
test_alert_engine_evaluate_threshold_normal 体征正常不触发
test_alert_engine_cooldown_prevents_duplicate cooldown 期内不重复触发
test_alert_engine_multiple_rules 多规则并行评估
test_alert_rule_crud 规则 CRUD 完整性
test_alert_rule_enable_disable 规则启用/禁用
test_alert_acknowledge 告警确认状态变更
test_alert_batch_acknowledge 批量确认

验收标准:

  • cargo test -p erp-server health_alert 全部通过
  • cooldown 逻辑精确到秒级验证

Task 10: device_reading_service 测试8 个)

涉及文件:

  • 新增: crates/erp-server/tests/integration/health_device_reading_tests.rs

测试用例清单:

测试名 场景
test_device_reading_batch_insert 批量插入(模拟 50 条数据)
test_device_reading_upsert_hourly 降采样聚合 — 同一小时的数据合并
test_device_reading_query_range 时间范围查询
test_device_reading_query_by_device_type 按设备类型过滤
test_device_reading_tenant_isolation 租户隔离
test_device_reading_invalid_data_rejected 无效数据(空值、超范围)拒绝
test_device_reading_latest_by_patient 获取患者最新读数
test_device_reading_sync_event_published 数据同步后事件发布

验收标准:

  • cargo test -p erp-server health_device_reading 全部通过
  • 降采样逻辑的聚合值avg/min/max精度正确

Task 11: 扩展已有患者测试(+3 个)

涉及文件:

  • 修改: crates/erp-server/tests/integration/health_patient_tests.rs

新增测试:

测试名 场景
test_patient_update_version_conflict 乐观锁冲突
test_patient_create_with_pii_encrypted PII 字段加密存储验证
test_patient_search_by_name 按名称搜索

Task 12: 扩展已有预约测试(+3 个)

涉及文件:

  • 修改: crates/erp-server/tests/integration/health_appointment_tests.rs

新增测试:

测试名 场景
test_appointment_cas_exceeds_max_returns_error 预约超额拒绝
test_appointment_cancel_releases_slot 取消预约释放名额
test_appointment_status_flow 状态完整流转

Phase 1 验收:

  • 所有新增测试通过: cargo test -p erp-server
  • 覆盖率提升约 20-25%(从当前基线)

Phase 2: 中风险 Service 测试Week 3-4

共 5 个 service + erp-ai约 47 个测试。

Task 13: patient_service 完整测试10 个)

涉及文件:

  • 修改: crates/erp-server/tests/integration/health_patient_tests.rs

新增测试: 家庭成员管理 CRUD4、标签管理 CRUD3、健康摘要2、搜索/过滤1

Task 14: appointment_service 完整测试8 个)

涉及文件:

  • 修改: crates/erp-server/tests/integration/health_appointment_tests.rs

新增测试: 排班管理 CRUD3、预约时间冲突1、并发安全2、多租户隔离2

Task 15: follow_up_service 测试8 个)

涉及文件:

  • 新增: crates/erp-server/tests/integration/health_follow_up_tests.rs

新增测试: 状态机 6 种转换6、任务分配1、执行记录1

Task 16: consultation_service 测试5 个)

涉及文件:

  • 新增: crates/erp-server/tests/integration/health_consultation_tests.rs

新增测试: 会话 CRUD3、消息收发1、状态变更1

Task 17: doctor_service 测试4 个)

涉及文件:

  • 新增: crates/erp-server/tests/integration/health_doctor_tests.rs

新增测试: CRUD3、排班关联1

Task 18: erp-ai 基础测试12 个)

涉及文件:

  • 新增: crates/erp-server/tests/integration/ai_prompt_template_tests.rs
  • 新增: crates/erp-server/tests/integration/ai_analysis_tests.rs
  • 新增: crates/erp-server/tests/integration/ai_usage_tests.rs

新增测试: 提示模板 CRUD4、分析记录 CRUD4、使用统计4

Phase 2 验收:

  • cargo test -p erp-server 全部通过
  • 后端 service 层覆盖率 ≥ 55%

Phase 3: 低风险 Service + Handler + DTOWeek 5-6

共 14 个测试文件,约 71 个测试。

Task 19-28: 低风险 Service 测试

每个 service 一个测试文件遵循统一模式CRUD + 租户隔离 + 软删除 + 乐观锁):

Task Service 文件 测试数
19 article_service health_article_tests.rs 5
20 article_category_service (合并入 Task 19 4
21 article_tag_service (合并入 Task 19 3
22 offline_event_service health_offline_event_tests.rs 4
23 consent_service health_consent_tests.rs 3
24 diagnosis_service health_diagnosis_tests.rs 3
25 daily_monitoring_service health_daily_monitoring_tests.rs 4
26 critical_value_threshold_service (合并入 Task 9 alert 测试文件) 3
27 stats_service health_stats_tests.rs 5
28 health_data_service health_data_tests.rs 8

Task 29: trend_service 集成测试补充3 个)

涉及文件:

  • 新增: crates/erp-server/tests/integration/health_trend_tests.rs

Task 30: Handler 层 HTTP 测试16 个)

目标: 验证 HTTP 层的权限检查、请求解析、响应格式。

涉及文件:

  • 新增: crates/erp-server/tests/integration/health_handler_tests.rs

测试模式: 使用 tower::ServiceExt 调用 Axum Router验证 HTTP 状态码和响应体格式。

覆盖: 每个核心 handler 1 个测试(患者/预约/随访/咨询/医生/文章/积分/体征/告警/透析等 16 个 handler

Task 31: DTO 转换测试10 个)

涉及文件:

  • 新增: crates/erp-health/src/dto/dto_tests.rs(内联 #[cfg(test)]

测试内容: 请求 DTO 反序列化JSON → struct+ 响应 DTO 序列化struct → JSON+ 边界值

Task 32: CI 后端增量门禁上线

涉及文件:

  • 修改: .github/workflows/test.yml

操作: 在 backend-test job 中添加增量覆盖率检查步骤

Phase 3 验收:

  • 后端全量测试覆盖率 ≥ 70%
  • CI 增量门禁生效

Phase 4: 前端补测Week 7-9

Task 33: API 层测试 — client.ts12 个)

涉及文件:

  • 新增: apps/web/src/api/client.test.ts

测试内容: token 主动刷新、401 被动刷新、并发请求队列去重、GET 缓存命中/过期、错误映射

工具: MSW mock /api/v1/auth/refresh 等端点

Task 34: API 层测试 — health 模块19 个)

涉及文件:

  • 新增: apps/web/src/api/health/patients.test.ts
  • 新增: apps/web/src/api/health/appointments.test.ts
  • 新增: apps/web/src/api/health/other.test.ts

测试内容: 每个 API 文件的 CRUD 参数正确性、分页参数、错误映射

Task 35: Store 层测试17 个)

涉及文件:

  • 新增: apps/web/src/stores/auth.test.ts
  • 新增: apps/web/src/stores/plugin.test.ts
  • 新增: apps/web/src/stores/health.test.ts
  • 新增: apps/web/src/stores/message.test.ts

Task 36: Hooks 测试12 个)

涉及文件:

  • 新增: apps/web/src/hooks/useApiRequest.test.ts
  • 新增: apps/web/src/hooks/usePaginatedData.test.ts
  • 新增: apps/web/src/hooks/usePermission.test.ts

Task 37: 页面组件测试19 个)

涉及文件:

  • 新增 8 个测试文件,对应 8 个核心健康模块页面

Task 38: Playwright E2E — 患者管理1 spec

涉及文件:

  • 新增: apps/web/e2e/health-patient.spec.ts

Task 39: Playwright E2E — 预约/随访/咨询3 spec

涉及文件:

  • 新增: apps/web/e2e/health-appointment.spec.ts
  • 新增: apps/web/e2e/health-follow-up.spec.ts
  • 新增: apps/web/e2e/health-consultation.spec.ts

Task 40: Playwright E2E — 统计仪表板1 spec

涉及文件:

  • 新增: apps/web/e2e/health-statistics.spec.ts

Phase 4 验收:

  • 前端全量覆盖率 ≥ 60%
  • 健康模块 E2E ≥ 5 个 spec

验证与 CI 门禁

Task 41: 后端增量门禁上线Week 6 后)

操作: 在 CI workflow 中启用后端增量覆盖率检查,新增/修改文件覆盖率 < 80% 时 PR 不允许合并。

Task 42: 前端增量门禁上线Week 9 后)

操作: 同上,启用前端增量覆盖率检查。

Task 43: 全量覆盖率验证 + 复盘Week 12 后)

操作:

  1. 运行 cargo llvm-cov --workspace --text 记录后端全量覆盖率
  2. 运行 pnpm test:coverage 记录前端全量覆盖率
  3. 对比 spec 目标(后端 80% + 前端 60%),分析差距
  4. 输出复盘文档到 docs/discussions/2026-05-XX-test-coverage-retrospective.md
  5. 制定后续补测计划(如有必要)

执行原则

  1. 每 Task 完成后立即提交 — 不积压,保持可追溯
  2. 先验证基础设施 — Phase 0 的 TestApp/TestFixture 必须先通过才能开始 Phase 1
  3. 测试文件独立于业务代码 — 新增测试文件不修改已有业务逻辑
  4. cargo check/test 必须通过 — 每个 Task 完成后运行 cargo test --workspace 验证
  5. Phase 间复盘 — 每个 Phase 结束后统计覆盖率,与目标对比,必要时调整