Files
hms/crates/erp-server/tests/integration/health_data_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

314 lines
9.6 KiB
Rust

//! erp-health 健康数据(体征 + 化验报告)集成测试
//!
//! 验证体征 CRUD、化验报告 CRUD + 审阅、租户隔离、乐观锁。
use erp_health::dto::health_data_dto::*;
use erp_health::service::health_data_service;
use super::test_fixture::TestApp;
fn default_vital_signs_req() -> CreateVitalSignsReq {
CreateVitalSignsReq {
record_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 1).unwrap(),
systolic_bp_morning: Some(125),
diastolic_bp_morning: Some(82),
systolic_bp_evening: Some(120),
diastolic_bp_evening: Some(78),
heart_rate: Some(72),
weight: Some(68.5),
blood_sugar: Some(5.2),
body_temperature: None,
spo2: Some(98),
blood_sugar_type: Some("fasting".to_string()),
water_intake_ml: Some(2000),
urine_output_ml: Some(1500),
notes: Some("状态良好".to_string()),
source: Some("manual".to_string()),
}
}
// ---------------------------------------------------------------------------
// 测试 1: 创建体征记录
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_vital_signs_create() {
let app = TestApp::new().await;
let patient_id = app.create_patient("体征患者").await;
let vs = health_data_service::create_vital_signs(
app.health_state(),
app.tenant_id(),
patient_id,
Some(app.operator_id()),
default_vital_signs_req(),
)
.await
.expect("创建体征应成功");
assert_eq!(vs.patient_id, patient_id);
assert_eq!(vs.systolic_bp_morning, Some(125));
assert_eq!(vs.heart_rate, Some(72));
assert_eq!(vs.weight, Some(68.5));
}
// ---------------------------------------------------------------------------
// 测试 2: 体征列表按患者过滤
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_vital_signs_list() {
let app = TestApp::new().await;
let patient_a = app.create_patient("列表A").await;
let patient_b = app.create_patient("列表B").await;
health_data_service::create_vital_signs(
app.health_state(),
app.tenant_id(),
patient_a,
None,
default_vital_signs_req(),
)
.await
.unwrap();
health_data_service::create_vital_signs(
app.health_state(),
app.tenant_id(),
patient_b,
None,
default_vital_signs_req(),
)
.await
.unwrap();
let list_a = health_data_service::list_vital_signs(
app.health_state(),
app.tenant_id(),
patient_a,
1,
20,
)
.await
.unwrap();
assert_eq!(list_a.total, 1);
let list_b = health_data_service::list_vital_signs(
app.health_state(),
app.tenant_id(),
patient_b,
1,
20,
)
.await
.unwrap();
assert_eq!(list_b.total, 1);
}
// ---------------------------------------------------------------------------
// 测试 3: 体征更新
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_vital_signs_update() {
let app = TestApp::new().await;
let patient_id = app.create_patient("更新患者").await;
let vs = health_data_service::create_vital_signs(
app.health_state(),
app.tenant_id(),
patient_id,
Some(app.operator_id()),
default_vital_signs_req(),
)
.await
.unwrap();
let updated = health_data_service::update_vital_signs(
app.health_state(),
app.tenant_id(),
patient_id,
vs.id,
Some(app.operator_id()),
UpdateVitalSignsReq {
record_date: None,
systolic_bp_morning: None,
diastolic_bp_morning: None,
systolic_bp_evening: None,
diastolic_bp_evening: None,
heart_rate: Some(65),
weight: Some(67.0),
blood_sugar: None,
body_temperature: None,
spo2: None,
blood_sugar_type: None,
water_intake_ml: None,
urine_output_ml: None,
notes: None,
},
vs.version,
)
.await
.expect("更新应成功");
assert_eq!(updated.heart_rate, Some(65));
assert_eq!(updated.weight, Some(67.0));
}
// ---------------------------------------------------------------------------
// 测试 4: 体征租户隔离
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_vital_signs_tenant_isolation() {
let app = TestApp::new().await;
let patient_id = app.create_patient("隔离患者").await;
health_data_service::create_vital_signs(
app.health_state(),
app.tenant_id(),
patient_id,
None,
default_vital_signs_req(),
)
.await
.unwrap();
let other_tenant = uuid::Uuid::new_v4();
let list =
health_data_service::list_vital_signs(app.health_state(), other_tenant, patient_id, 1, 20)
.await
.unwrap();
assert_eq!(list.total, 0, "不同租户不应看到体征记录");
}
// ---------------------------------------------------------------------------
// 测试 5: 创建化验报告
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_lab_report_create() {
let app = TestApp::new().await;
let patient_id = app.create_patient("化验患者").await;
let report = health_data_service::create_lab_report(
app.health_state(), app.tenant_id(), patient_id, Some(app.operator_id()),
CreateLabReportReq {
report_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 2).unwrap(),
report_type: "blood_routine".to_string(),
source: Some("manual_input".to_string()),
items: Some(serde_json::json!([
{"name": "WBC", "value": "6.5", "unit": "10^9/L", "reference_low": "3.5", "reference_high": "9.5", "is_abnormal": false},
{"name": "HGB", "value": "110", "unit": "g/L", "reference_low": "120", "reference_high": "160", "is_abnormal": true},
])),
image_urls: None,
doctor_notes: Some("轻度贫血".to_string()),
},
)
.await
.expect("创建化验报告应成功");
assert_eq!(report.patient_id, patient_id);
assert_eq!(report.report_type, "blood_routine");
assert_eq!(report.status, "pending");
assert!(report.items.is_some());
}
// ---------------------------------------------------------------------------
// 测试 6: 化验报告审阅
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_lab_report_review() {
let app = TestApp::new().await;
let patient_id = app.create_patient("审阅患者").await;
let report = health_data_service::create_lab_report(
app.health_state(),
app.tenant_id(),
patient_id,
Some(app.operator_id()),
CreateLabReportReq {
report_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 3).unwrap(),
report_type: "blood_routine".to_string(),
source: None,
items: None,
image_urls: None,
doctor_notes: None,
},
)
.await
.unwrap();
assert_eq!(report.status, "pending");
let reviewed = health_data_service::review_lab_report(
app.health_state(),
app.tenant_id(),
patient_id,
report.id,
app.operator_id(),
ReviewLabReportReq {
doctor_notes: Some("复查确认".to_string()),
items: None,
},
report.version,
)
.await
.expect("审阅应成功");
assert_eq!(reviewed.status, "reviewed");
assert!(reviewed.reviewed_by.is_some());
}
// ---------------------------------------------------------------------------
// 测试 7: 化验报告列表按患者过滤
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_lab_report_list() {
let app = TestApp::new().await;
let patient_a = app.create_patient("化验列表A").await;
let patient_b = app.create_patient("化验列表B").await;
for pid in &[patient_a, patient_b] {
health_data_service::create_lab_report(
app.health_state(),
app.tenant_id(),
*pid,
None,
CreateLabReportReq {
report_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 4).unwrap(),
report_type: "blood_routine".to_string(),
source: None,
items: None,
image_urls: None,
doctor_notes: None,
},
)
.await
.unwrap();
}
let list_a = health_data_service::list_lab_reports(
app.health_state(),
app.tenant_id(),
patient_a,
1,
20,
)
.await
.unwrap();
assert_eq!(list_a.total, 1);
assert_eq!(list_a.data[0].patient_id, patient_a);
}
// ---------------------------------------------------------------------------
// 测试 8: 无效患者创建体征返回错误
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_vital_signs_invalid_patient() {
let app = TestApp::new().await;
let fake_patient = uuid::Uuid::new_v4();
let result = health_data_service::create_vital_signs(
app.health_state(),
app.tenant_id(),
fake_patient,
None,
default_vital_signs_req(),
)
.await;
assert!(result.is_err(), "无效患者应返回错误");
}