Files
hms/crates/erp-server/tests/integration/health_follow_up_tests.rs
iven 6d5a711d2c
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
fix: 修复测试发现的 7 个问题 + 全 workspace clippy 清零
功能修复:
1. 患者创建空名称验证:后端添加 name.trim().is_empty() 检查
2. 仪表盘统计容错:单个查询失败返回零值而非 500
3. FHIR 路由修复:从 /fhir 移到 /api/v1/fhir 保持一致
4. 冻结模块后端中间件:新增 frozen_module_middleware 拦截冻结路径
5. 积分端点权限码:health.health-data.list → health.points.list
6. 角色权限迁移:护士补充 devices.list,运营补充 points.list/manage
7. 测试结果文档:R01-R05 角色测试 + T00/T10 结果归档

Clippy 全 workspace 清零(14→0 errors):
- erp-core: 修复 empty doc line、collapsible if、redundant closure 等 9 处
- erp-health: 修复 too_many_arguments、unused var、unnecessary parens 等 58 处
- erp-ai: 修复 dead_code、unused import 等 11 处
- erp-plugin: 修复 too_many_arguments、wildcard pattern 等 11 处
- erp-server-migration: 修复 enum_variant_names 5 处
- erp-auth/config/workflow/message: 各 1-3 处

工程改进:
- lint-staged 配置迁移到 .lintstagedrc.js(函数式避免文件列表传给 clippy)
- cargo fmt 统一格式化
2026-05-07 23:43:14 +08:00

358 lines
11 KiB
Rust

//! erp-health 随访管理集成测试
//!
//! 验证随访任务 CRUD、状态流转、批量操作、记录创建、租户隔离、乐观锁。
use erp_health::dto::follow_up_dto::*;
use erp_health::service::follow_up_service;
use super::test_fixture::TestApp;
fn default_create_task(patient_id: uuid::Uuid) -> CreateFollowUpTaskReq {
CreateFollowUpTaskReq {
patient_id,
assigned_to: None,
follow_up_type: "phone".to_string(),
planned_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 15).unwrap(),
content_template: Some("请询问血压情况".to_string()),
related_appointment_id: None,
}
}
// ---------------------------------------------------------------------------
// 测试 1: 创建 + 查询随访任务
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_follow_up_task_create_and_get() {
let app = TestApp::new().await;
let patient_id = app.create_patient("随访患者").await;
let task = follow_up_service::create_task(
app.health_state(),
app.tenant_id(),
Some(app.operator_id()),
default_create_task(patient_id),
)
.await
.expect("创建任务应成功");
assert_eq!(task.patient_id, patient_id);
assert_eq!(task.follow_up_type, "phone");
assert_eq!(task.status, "pending");
assert_eq!(task.version, 1);
let fetched = follow_up_service::get_task(app.health_state(), app.tenant_id(), task.id)
.await
.expect("查询应成功");
assert_eq!(fetched.id, task.id);
}
// ---------------------------------------------------------------------------
// 测试 2: 列表按患者过滤
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_follow_up_task_list_by_patient() {
let app = TestApp::new().await;
let patient_a = app.create_patient("列表A").await;
let patient_b = app.create_patient("列表B").await;
follow_up_service::create_task(
app.health_state(),
app.tenant_id(),
None,
default_create_task(patient_a),
)
.await
.unwrap();
follow_up_service::create_task(
app.health_state(),
app.tenant_id(),
None,
default_create_task(patient_a),
)
.await
.unwrap();
follow_up_service::create_task(
app.health_state(),
app.tenant_id(),
None,
default_create_task(patient_b),
)
.await
.unwrap();
let list_a = follow_up_service::list_tasks(
app.health_state(),
app.tenant_id(),
1,
20,
Some(patient_a),
None,
None,
)
.await
.unwrap();
assert_eq!(list_a.total, 2);
let list_b = follow_up_service::list_tasks(
app.health_state(),
app.tenant_id(),
1,
20,
Some(patient_b),
None,
None,
)
.await
.unwrap();
assert_eq!(list_b.total, 1);
}
// ---------------------------------------------------------------------------
// 测试 3: 状态流转 pending → in_progress → completed
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_follow_up_task_status_flow() {
let app = TestApp::new().await;
let patient_id = app.create_patient("流转患者").await;
let task = follow_up_service::create_task(
app.health_state(),
app.tenant_id(),
Some(app.operator_id()),
default_create_task(patient_id),
)
.await
.unwrap();
assert_eq!(task.status, "pending");
// pending → in_progress
let started = follow_up_service::update_task(
app.health_state(),
app.tenant_id(),
task.id,
Some(app.operator_id()),
UpdateFollowUpTaskReq {
status: Some("in_progress".to_string()),
assigned_to: None,
follow_up_type: None,
planned_date: None,
content_template: None,
},
task.version,
)
.await
.expect("开始应成功");
assert_eq!(started.status, "in_progress");
// in_progress → completed
let completed = follow_up_service::update_task(
app.health_state(),
app.tenant_id(),
task.id,
Some(app.operator_id()),
UpdateFollowUpTaskReq {
status: Some("completed".to_string()),
assigned_to: None,
follow_up_type: None,
planned_date: None,
content_template: None,
},
started.version,
)
.await
.expect("完成应成功");
assert_eq!(completed.status, "completed");
}
// ---------------------------------------------------------------------------
// 测试 4: 乐观锁冲突
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_follow_up_task_version_conflict() {
let app = TestApp::new().await;
let patient_id = app.create_patient("乐观锁患者").await;
let task = follow_up_service::create_task(
app.health_state(),
app.tenant_id(),
Some(app.operator_id()),
default_create_task(patient_id),
)
.await
.unwrap();
// 正确版本更新
follow_up_service::update_task(
app.health_state(),
app.tenant_id(),
task.id,
Some(app.operator_id()),
UpdateFollowUpTaskReq {
status: Some("in_progress".to_string()),
assigned_to: None,
follow_up_type: None,
planned_date: None,
content_template: None,
},
task.version,
)
.await
.expect("更新应成功");
// 旧版本更新应失败
let result = follow_up_service::update_task(
app.health_state(),
app.tenant_id(),
task.id,
Some(app.operator_id()),
UpdateFollowUpTaskReq {
status: Some("cancelled".to_string()),
assigned_to: None,
follow_up_type: None,
planned_date: None,
content_template: None,
},
task.version,
)
.await;
assert!(result.is_err(), "旧版本应失败");
}
// ---------------------------------------------------------------------------
// 测试 5: 软删除后不可见
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_follow_up_task_soft_delete() {
let app = TestApp::new().await;
let patient_id = app.create_patient("软删除患者").await;
let task = follow_up_service::create_task(
app.health_state(),
app.tenant_id(),
Some(app.operator_id()),
default_create_task(patient_id),
)
.await
.unwrap();
follow_up_service::delete_task(
app.health_state(),
app.tenant_id(),
task.id,
Some(app.operator_id()),
task.version,
)
.await
.expect("删除应成功");
let result = follow_up_service::get_task(app.health_state(), app.tenant_id(), task.id).await;
assert!(result.is_err(), "软删除后应不可见");
}
// ---------------------------------------------------------------------------
// 测试 6: 租户隔离
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_follow_up_task_tenant_isolation() {
let app = TestApp::new().await;
let patient_id = app.create_patient("隔离患者").await;
let task = follow_up_service::create_task(
app.health_state(),
app.tenant_id(),
None,
default_create_task(patient_id),
)
.await
.unwrap();
let other_tenant = uuid::Uuid::new_v4();
let result = follow_up_service::get_task(app.health_state(), other_tenant, task.id).await;
assert!(result.is_err(), "不同租户不应看到此任务");
}
// ---------------------------------------------------------------------------
// 测试 7: 批量创建随访任务
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_follow_up_batch_create() {
let app = TestApp::new().await;
let patient_a = app.create_patient("批量A").await;
let patient_b = app.create_patient("批量B").await;
let result = follow_up_service::batch_create_tasks(
app.health_state(),
app.tenant_id(),
Some(app.operator_id()),
BatchCreateTasksReq {
patient_ids: vec![patient_a, patient_b],
assigned_to: None,
follow_up_type: "phone".to_string(),
planned_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 20).unwrap(),
content_template: None,
},
)
.await
.expect("批量创建应成功");
assert_eq!(result.succeeded, 2);
assert_eq!(result.failed, 0);
}
// ---------------------------------------------------------------------------
// 测试 8: 创建随访记录
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_follow_up_record_create() {
let app = TestApp::new().await;
let patient_id = app.create_patient("记录患者").await;
let task = follow_up_service::create_task(
app.health_state(),
app.tenant_id(),
Some(app.operator_id()),
default_create_task(patient_id),
)
.await
.unwrap();
let record = follow_up_service::create_record(
app.health_state(),
app.tenant_id(),
Some(app.operator_id()),
CreateFollowUpRecordReq {
task_id: task.id,
executed_by: Some(app.operator_id()),
executed_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 16).unwrap(),
result: "已跟进".to_string(),
patient_condition: Some("血压正常".to_string()),
medical_advice: Some("继续服药".to_string()),
next_follow_up_date: Some(chrono::NaiveDate::from_ymd_opt(2026, 6, 16).unwrap()),
},
)
.await
.expect("创建记录应成功");
assert_eq!(record.task_id, task.id);
assert_eq!(record.result, "已跟进");
assert_eq!(record.medical_advice, Some("继续服药".to_string()));
}
// ---------------------------------------------------------------------------
// 测试 9: 无效患者返回错误
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_follow_up_task_invalid_patient() {
let app = TestApp::new().await;
let fake_patient = uuid::Uuid::new_v4();
let result = follow_up_service::create_task(
app.health_state(),
app.tenant_id(),
None,
default_create_task(fake_patient),
)
.await;
assert!(result.is_err(), "无效患者应返回错误");
}