From fdbbc47a60f0b6bbf2aa6e84be8673b1f664aaed Mon Sep 17 00:00:00 2001 From: iven Date: Mon, 27 Apr 2026 21:58:57 +0800 Subject: [PATCH] =?UTF-8?q?test(health):=20=E6=89=A9=E5=B1=95=E6=82=A3?= =?UTF-8?q?=E8=80=85=E9=9B=86=E6=88=90=E6=B5=8B=E8=AF=95=20+3=20=E2=80=94?= =?UTF-8?q?=20=E6=9B=B4=E6=96=B0=E4=B9=90=E8=A7=82=E9=94=81/PII=E5=8A=A0?= =?UTF-8?q?=E5=AF=86=E9=AA=8C=E8=AF=81/=E5=A7=93=E5=90=8D=E6=90=9C?= =?UTF-8?q?=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/integration/health_patient_tests.rs | 113 +++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/crates/erp-server/tests/integration/health_patient_tests.rs b/crates/erp-server/tests/integration/health_patient_tests.rs index acc6609..8d2f6ab 100644 --- a/crates/erp-server/tests/integration/health_patient_tests.rs +++ b/crates/erp-server/tests/integration/health_patient_tests.rs @@ -4,7 +4,7 @@ //! 使用 TestDb 创建隔离 PostgreSQL 数据库,直接调用 service 层函数。 use erp_core::events::EventBus; -use erp_health::dto::patient_dto::CreatePatientReq; +use erp_health::dto::patient_dto::{CreatePatientReq, UpdatePatientReq}; use erp_health::service::patient_service; use erp_health::state::HealthState; use erp_core::crypto::PiiCrypto; @@ -206,3 +206,114 @@ async fn test_patient_soft_delete() { let lookup = patient_service::get_patient(&state, tenant_id, patient.id).await; assert!(lookup.is_err(), "软删除后查询应返回错误"); } + +#[tokio::test] +async fn test_patient_update_and_optimistic_lock() { + let test_db = TestDb::new().await; + let state = make_state(test_db.db()); + let tenant_id = uuid::Uuid::new_v4(); + let operator_id = uuid::Uuid::new_v4(); + + let patient = patient_service::create_patient(&state, tenant_id, Some(operator_id), CreatePatientReq { + name: "更新前".to_string(), + gender: Some("male".to_string()), + birth_date: None, blood_type: None, id_number: None, + allergy_history: None, medical_history_summary: None, + emergency_contact_name: None, emergency_contact_phone: None, + source: None, notes: None, + }) + .await + .expect("创建应成功"); + + // 正确版本更新 + let updated = patient_service::update_patient( + &state, tenant_id, patient.id, Some(operator_id), + UpdatePatientReq { + name: Some("更新后".to_string()), + gender: None, birth_date: None, blood_type: None, + id_number: None, allergy_history: None, + medical_history_summary: None, emergency_contact_name: None, + emergency_contact_phone: None, source: None, notes: None, + status: None, verification_status: None, + }, + patient.version, + ) + .await + .expect("更新应成功"); + assert_eq!(updated.name, "更新后"); + assert_eq!(updated.version, 2); + + // 旧版本更新应失败 + let result = patient_service::update_patient( + &state, tenant_id, patient.id, Some(operator_id), + UpdatePatientReq { + name: Some("冲突".to_string()), + gender: None, birth_date: None, blood_type: None, + id_number: None, allergy_history: None, + medical_history_summary: None, emergency_contact_name: None, + emergency_contact_phone: None, source: None, notes: None, + status: None, verification_status: None, + }, + patient.version, // 旧版本 + ) + .await; + assert!(result.is_err(), "旧版本更新应失败"); +} + +#[tokio::test] +async fn test_patient_pii_encrypted() { + let test_db = TestDb::new().await; + let state = make_state(test_db.db()); + let tenant_id = uuid::Uuid::new_v4(); + + let patient = patient_service::create_patient(&state, tenant_id, None, CreatePatientReq { + name: "加密患者".to_string(), + gender: None, + birth_date: None, blood_type: None, + id_number: Some("330102199001011234".to_string()), + allergy_history: Some("花粉过敏".to_string()), + medical_history_summary: Some("高血压".to_string()), + emergency_contact_name: Some("王五".to_string()), + emergency_contact_phone: Some("13900139000".to_string()), + source: None, notes: None, + }) + .await + .expect("创建应成功"); + + // 列表视图应隐藏 id_number + assert!(patient.id_number.is_none(), "列表视图不应返回身份证号"); + + // get_patient 详情应包含 PII 解密后的字段(部分脱敏) + let detail = patient_service::get_patient(&state, tenant_id, patient.id) + .await + .expect("查询详情应成功"); + // id_number 返回脱敏版本 + assert!(detail.id_number.is_some(), "详情应返回 id_number(脱敏)"); + assert_eq!(detail.allergy_history, Some("花粉过敏".to_string())); + assert_eq!(detail.emergency_contact_name, Some("王五".to_string())); +} + +#[tokio::test] +async fn test_patient_search_by_name() { + let test_db = TestDb::new().await; + let state = make_state(test_db.db()); + let tenant_id = uuid::Uuid::new_v4(); + + for name in &["赵一", "钱二", "孙三"] { + patient_service::create_patient(&state, tenant_id, None, CreatePatientReq { + name: name.to_string(), + gender: None, birth_date: None, blood_type: None, id_number: None, + allergy_history: None, medical_history_summary: None, + emergency_contact_name: None, emergency_contact_phone: None, + source: None, notes: None, + }) + .await + .unwrap(); + } + + let result = patient_service::list_patients(&state, tenant_id, 1, 10, Some("钱".to_string()), None) + .await + .expect("搜索应成功"); + assert_eq!(result.total, 1); + assert_eq!(result.data[0].name, "钱二"); +}