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

287 lines
9.2 KiB
Rust

//! erp-dialysis 透析方案管理集成测试
//!
//! 验证透析方案 CRUD、列表按患者过滤、租户隔离、乐观锁。
use erp_dialysis::dto::dialysis_prescription_dto::*;
use erp_dialysis::service::dialysis_prescription_service;
use super::test_fixture::TestApp;
fn default_create_req(patient_id: uuid::Uuid) -> CreateDialysisPrescriptionReq {
CreateDialysisPrescriptionReq {
patient_id,
dialyzer_model: Some("FX80".to_string()),
membrane_area: Some(1.8),
dialysate_potassium: Some(2.0),
dialysate_calcium: Some(1.5),
dialysate_bicarbonate: Some(35.0),
anticoagulation_type: Some("heparin".to_string()),
anticoagulation_dose: Some("2000IU".to_string()),
target_ultrafiltration_ml: Some(2000),
target_dry_weight: Some(65.0),
blood_flow_rate: Some(300),
dialysate_flow_rate: Some(500),
frequency_per_week: Some(3),
duration_minutes: Some(240),
vascular_access_type: Some("avf".to_string()),
vascular_access_location: Some("左前臂".to_string()),
effective_from: chrono::NaiveDate::from_ymd_opt(2026, 5, 1),
effective_to: None,
notes: None,
}
}
async fn seed_prescription(app: &TestApp, patient_id: uuid::Uuid) -> DialysisPrescriptionResp {
dialysis_prescription_service::create_prescription(
app.dialysis_state(),
app.tenant_id(),
Some(app.operator_id()),
default_create_req(patient_id),
)
.await
.expect("创建透析方案应成功")
}
// ---------------------------------------------------------------------------
// 测试 1: 创建透析方案
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_dialysis_prescription_create() {
let app = TestApp::new().await;
let patient_id = app.create_patient("透析方案患者").await;
let rx = seed_prescription(&app, patient_id).await;
assert_eq!(rx.patient_id, patient_id);
assert_eq!(rx.status, "active");
assert_eq!(rx.dialyzer_model, Some("FX80".to_string()));
assert_eq!(rx.frequency_per_week, Some(3));
assert_eq!(rx.version, 1);
}
// ---------------------------------------------------------------------------
// 测试 2: 查询透析方案
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_dialysis_prescription_get() {
let app = TestApp::new().await;
let patient_id = app.create_patient("方案查询患者").await;
let rx = seed_prescription(&app, patient_id).await;
let fetched = dialysis_prescription_service::get_prescription(
app.dialysis_state(),
app.tenant_id(),
rx.id,
)
.await
.expect("查询应成功");
assert_eq!(fetched.id, rx.id);
}
// ---------------------------------------------------------------------------
// 测试 3: 更新透析方案
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_dialysis_prescription_update() {
let app = TestApp::new().await;
let patient_id = app.create_patient("方案更新患者").await;
let rx = seed_prescription(&app, patient_id).await;
let updated = dialysis_prescription_service::update_prescription(
app.dialysis_state(),
app.tenant_id(),
rx.id,
Some(app.operator_id()),
UpdateDialysisPrescriptionReq {
blood_flow_rate: Some(350),
frequency_per_week: Some(4),
status: None,
dialyzer_model: None,
membrane_area: None,
dialysate_potassium: None,
dialysate_calcium: None,
dialysate_bicarbonate: None,
anticoagulation_type: None,
anticoagulation_dose: None,
target_ultrafiltration_ml: None,
target_dry_weight: None,
dialysate_flow_rate: None,
duration_minutes: None,
vascular_access_type: None,
vascular_access_location: None,
effective_from: None,
effective_to: None,
notes: None,
},
rx.version,
)
.await
.expect("更新应成功");
assert_eq!(updated.blood_flow_rate, Some(350));
assert_eq!(updated.frequency_per_week, Some(4));
assert_eq!(updated.version, 2);
}
// ---------------------------------------------------------------------------
// 测试 4: 列表按患者过滤
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_dialysis_prescription_list_by_patient() {
let app = TestApp::new().await;
let patient_a = app.create_patient("方案列表A").await;
let patient_b = app.create_patient("方案列表B").await;
seed_prescription(&app, patient_a).await;
seed_prescription(&app, patient_b).await;
let list_a = dialysis_prescription_service::list_prescriptions(
app.dialysis_state(),
app.tenant_id(),
1,
20,
Some(patient_a),
None,
)
.await
.unwrap();
assert_eq!(list_a.total, 1);
let list_b = dialysis_prescription_service::list_prescriptions(
app.dialysis_state(),
app.tenant_id(),
1,
20,
Some(patient_b),
None,
)
.await
.unwrap();
assert_eq!(list_b.total, 1);
}
// ---------------------------------------------------------------------------
// 测试 5: 软删除
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_dialysis_prescription_soft_delete() {
let app = TestApp::new().await;
let patient_id = app.create_patient("方案删除患者").await;
let rx = seed_prescription(&app, patient_id).await;
dialysis_prescription_service::delete_prescription(
app.dialysis_state(),
app.tenant_id(),
rx.id,
Some(app.operator_id()),
rx.version,
)
.await
.expect("删除应成功");
let result = dialysis_prescription_service::get_prescription(
app.dialysis_state(),
app.tenant_id(),
rx.id,
)
.await;
assert!(result.is_err(), "软删除后查询应失败");
}
// ---------------------------------------------------------------------------
// 测试 6: 租户隔离
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_dialysis_prescription_tenant_isolation() {
let app = TestApp::new().await;
let patient_id = app.create_patient("方案隔离患者").await;
seed_prescription(&app, patient_id).await;
let other_tenant = uuid::Uuid::new_v4();
let list = dialysis_prescription_service::list_prescriptions(
app.dialysis_state(),
other_tenant,
1,
20,
None,
None,
)
.await
.unwrap();
assert_eq!(list.total, 0, "不同租户不应看到方案");
}
// ---------------------------------------------------------------------------
// 测试 7: 乐观锁冲突
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_dialysis_prescription_version_conflict() {
let app = TestApp::new().await;
let patient_id = app.create_patient("方案锁患者").await;
let rx = seed_prescription(&app, patient_id).await;
// 先更新一次
dialysis_prescription_service::update_prescription(
app.dialysis_state(),
app.tenant_id(),
rx.id,
Some(app.operator_id()),
UpdateDialysisPrescriptionReq {
blood_flow_rate: Some(350),
status: None,
dialyzer_model: None,
membrane_area: None,
dialysate_potassium: None,
dialysate_calcium: None,
dialysate_bicarbonate: None,
anticoagulation_type: None,
anticoagulation_dose: None,
target_ultrafiltration_ml: None,
target_dry_weight: None,
frequency_per_week: None,
dialysate_flow_rate: None,
duration_minutes: None,
vascular_access_type: None,
vascular_access_location: None,
effective_from: None,
effective_to: None,
notes: None,
},
rx.version,
)
.await
.unwrap();
// 用旧 version 再更新应失败
let result = dialysis_prescription_service::update_prescription(
app.dialysis_state(),
app.tenant_id(),
rx.id,
Some(app.operator_id()),
UpdateDialysisPrescriptionReq {
blood_flow_rate: Some(400),
status: None,
dialyzer_model: None,
membrane_area: None,
dialysate_potassium: None,
dialysate_calcium: None,
dialysate_bicarbonate: None,
anticoagulation_type: None,
anticoagulation_dose: None,
target_ultrafiltration_ml: None,
target_dry_weight: None,
frequency_per_week: None,
dialysate_flow_rate: None,
duration_minutes: None,
vascular_access_type: None,
vascular_access_location: None,
effective_from: None,
effective_to: None,
notes: None,
},
rx.version,
)
.await;
assert!(result.is_err(), "乐观锁冲突应返回错误");
}