//! erp-dialysis 透析记录集成测试 //! //! 验证透析 CRUD、状态流转、PII 加密、乐观锁、租户隔离等核心行为。 use chrono::{NaiveDate, NaiveTime}; use erp_dialysis::dto::dialysis_dto::*; use erp_dialysis::service::dialysis_service; use super::test_fixture::TestApp; fn default_create_req(patient_id: uuid::Uuid) -> CreateDialysisRecordReq { CreateDialysisRecordReq { patient_id, dialysis_date: NaiveDate::from_ymd_opt(2026, 5, 1).unwrap(), start_time: Some(NaiveTime::from_hms_opt(8, 0, 0).unwrap()), end_time: Some(NaiveTime::from_hms_opt(12, 0, 0).unwrap()), dry_weight: Some(65.0), pre_weight: Some(68.0), post_weight: Some(65.5), pre_bp_systolic: Some(140), pre_bp_diastolic: Some(90), post_bp_systolic: Some(130), post_bp_diastolic: Some(85), pre_heart_rate: Some(80), post_heart_rate: Some(75), ultrafiltration_volume: Some(2500), dialysis_duration: Some(240), blood_flow_rate: Some(300), dialysis_type: "HD".to_string(), symptoms: None, complication_notes: Some("无并发症".to_string()), } } // --------------------------------------------------------------------------- // 测试 1: 创建透析记录基本 CRUD // --------------------------------------------------------------------------- #[tokio::test] async fn test_dialysis_create_basic() { let app = TestApp::new().await; let patient_id = app.create_patient("透析患者").await; let req = default_create_req(patient_id); let record = dialysis_service::create_dialysis_record( app.dialysis_state(), app.tenant_id(), Some(app.operator_id()), req, ) .await .expect("创建透析记录应成功"); assert_eq!(record.patient_id, patient_id); assert_eq!(record.dialysis_type, "HD"); assert_eq!(record.status, "draft"); assert_eq!(record.ultrafiltration_volume, Some(2500)); // 读取 let fetched = dialysis_service::get_dialysis_record( app.dialysis_state(), app.tenant_id(), record.id, ) .await .expect("查询应成功"); assert_eq!(fetched.id, record.id); } // --------------------------------------------------------------------------- // 测试 2: PII 字段加密存储 // --------------------------------------------------------------------------- #[tokio::test] async fn test_dialysis_create_pii_encrypted() { let app = TestApp::new().await; let patient_id = app.create_patient("PII患者").await; let mut req = default_create_req(patient_id); req.symptoms = Some(serde_json::json!({"头晕": "轻度", "恶心": "无"})); req.complication_notes = Some("低血压发作".to_string()); let record = dialysis_service::create_dialysis_record( app.dialysis_state(), app.tenant_id(), Some(app.operator_id()), req, ) .await .expect("创建应成功"); // 通过 service 读取应自动解密 assert!(record.symptoms.is_some()); assert_eq!(record.complication_notes, Some("低血压发作".to_string())); } // --------------------------------------------------------------------------- // 测试 3: 状态流转 pending → reviewed // --------------------------------------------------------------------------- #[tokio::test] async fn test_dialysis_update_status_flow() { let app = TestApp::new().await; let patient_id = app.create_patient("状态流转患者").await; let record = dialysis_service::create_dialysis_record( app.dialysis_state(), app.tenant_id(), Some(app.operator_id()), default_create_req(patient_id), ) .await .unwrap(); assert_eq!(record.status, "draft"); // 先将状态推进到 completed(draft → completed → reviewed) use sea_orm::{EntityTrait, ColumnTrait, QueryFilter}; use erp_dialysis::entity::dialysis_record; let _ = dialysis_record::Entity::update_many() .col_expr(dialysis_record::Column::Status, sea_orm::sea_query::Expr::val("completed").into()) .col_expr(dialysis_record::Column::Version, sea_orm::sea_query::Expr::val(record.version + 1).into()) .filter(dialysis_record::Column::Id.eq(record.id)) .exec(app.db()) .await .expect("状态推进应成功"); // 审核: completed → reviewed let reviewed = dialysis_service::review_dialysis_record( app.dialysis_state(), app.tenant_id(), record.id, app.operator_id(), record.version + 1, ) .await .expect("审核应成功"); assert_eq!(reviewed.status, "reviewed"); assert!(reviewed.reviewed_by.is_some()); } // --------------------------------------------------------------------------- // 测试 4: 按患者 ID 过滤列表 // --------------------------------------------------------------------------- #[tokio::test] async fn test_dialysis_list_by_patient() { let app = TestApp::new().await; let patient_a = app.create_patient("列表患者A").await; let patient_b = app.create_patient("列表患者B").await; dialysis_service::create_dialysis_record( app.dialysis_state(), app.tenant_id(), Some(app.operator_id()), default_create_req(patient_a), ) .await .unwrap(); dialysis_service::create_dialysis_record( app.dialysis_state(), app.tenant_id(), Some(app.operator_id()), default_create_req(patient_a), ) .await .unwrap(); dialysis_service::create_dialysis_record( app.dialysis_state(), app.tenant_id(), Some(app.operator_id()), default_create_req(patient_b), ) .await .unwrap(); let list_a = dialysis_service::list_dialysis_records( app.dialysis_state(), app.tenant_id(), patient_a, 1, 20, ) .await .unwrap(); assert_eq!(list_a.total, 2); let list_b = dialysis_service::list_dialysis_records( app.dialysis_state(), app.tenant_id(), patient_b, 1, 20, ) .await .unwrap(); assert_eq!(list_b.total, 1); } // --------------------------------------------------------------------------- // 测试 5: 租户隔离 // --------------------------------------------------------------------------- #[tokio::test] async fn test_dialysis_tenant_isolation() { let app = TestApp::new().await; let patient_id = app.create_patient("隔离患者").await; let record = dialysis_service::create_dialysis_record( app.dialysis_state(), app.tenant_id(), Some(app.operator_id()), default_create_req(patient_id), ) .await .unwrap(); // 用不同 tenant_id 查询应失败 let other_tenant = uuid::Uuid::new_v4(); let result = dialysis_service::get_dialysis_record( app.dialysis_state(), other_tenant, record.id, ) .await; assert!(result.is_err(), "不同租户不应看到此记录"); } // --------------------------------------------------------------------------- // 测试 6: 乐观锁 — 旧版本更新拒绝 // --------------------------------------------------------------------------- #[tokio::test] async fn test_dialysis_version_conflict() { let app = TestApp::new().await; let patient_id = app.create_patient("乐观锁患者").await; let record = dialysis_service::create_dialysis_record( app.dialysis_state(), app.tenant_id(), Some(app.operator_id()), default_create_req(patient_id), ) .await .unwrap(); // 用正确版本更新 let updated = dialysis_service::update_dialysis_record( app.dialysis_state(), app.tenant_id(), record.id, Some(app.operator_id()), UpdateDialysisRecordReq { dialysis_date: None, start_time: None, end_time: None, dry_weight: None, pre_weight: None, post_weight: None, pre_bp_systolic: None, pre_bp_diastolic: None, post_bp_systolic: None, post_bp_diastolic: None, pre_heart_rate: None, post_heart_rate: None, ultrafiltration_volume: None, dialysis_duration: None, blood_flow_rate: None, dialysis_type: Some("HDF".to_string()), symptoms: None, complication_notes: None, }, record.version, ) .await .expect("更新应成功"); assert_eq!(updated.dialysis_type, "HDF"); // 用旧版本更新应失败 let result = dialysis_service::update_dialysis_record( app.dialysis_state(), app.tenant_id(), record.id, Some(app.operator_id()), UpdateDialysisRecordReq { dialysis_date: None, start_time: None, end_time: None, dry_weight: None, pre_weight: None, post_weight: None, pre_bp_systolic: None, pre_bp_diastolic: None, post_bp_systolic: None, post_bp_diastolic: None, pre_heart_rate: None, post_heart_rate: None, ultrafiltration_volume: None, dialysis_duration: None, blood_flow_rate: None, dialysis_type: Some("HD".to_string()), symptoms: None, complication_notes: None, }, record.version, // 旧版本 ) .await; assert!(result.is_err(), "旧版本更新应失败"); } // --------------------------------------------------------------------------- // 测试 7: 软删除后不可见 // --------------------------------------------------------------------------- #[tokio::test] async fn test_dialysis_soft_delete() { let app = TestApp::new().await; let patient_id = app.create_patient("软删除患者").await; let record = dialysis_service::create_dialysis_record( app.dialysis_state(), app.tenant_id(), Some(app.operator_id()), default_create_req(patient_id), ) .await .unwrap(); // 删除 dialysis_service::delete_dialysis_record( app.dialysis_state(), app.tenant_id(), record.id, Some(app.operator_id()), record.version, ) .await .expect("删除应成功"); // 查询应失败 let result = dialysis_service::get_dialysis_record( app.dialysis_state(), app.tenant_id(), record.id, ) .await; assert!(result.is_err(), "软删除后应不可见"); // 列表中不应出现 let list = dialysis_service::list_dialysis_records( app.dialysis_state(), app.tenant_id(), patient_id, 1, 20, ) .await .unwrap(); assert_eq!(list.total, 0); } // --------------------------------------------------------------------------- // 测试 8: 无效患者 ID 返回错误 // --------------------------------------------------------------------------- #[tokio::test] async fn test_dialysis_create_without_patient_returns_error() { let app = TestApp::new().await; let fake_patient = uuid::Uuid::new_v4(); let result = dialysis_service::create_dialysis_record( app.dialysis_state(), app.tenant_id(), Some(app.operator_id()), default_create_req(fake_patient), ) .await; assert!(result.is_err(), "无效患者应返回错误"); }